refactor(ivy): ngcc - mark target entry-point as processed even if ngcc was a noop (#29092)

If `targetEntryPointPath` is provided to `mainNgcc` then we will now mark all
the `propertiesToConsider` for that entry-point if we determine that
it does not contain code that was compiled by Angular (for instance it has
no `...metadata.json` file).

The commit also renames `__modified_by_ngcc__` to `__processed_by_ivy_ngcc__`, since
there may be entry-points that are marked despite ngcc not actually compiling anything.

PR Close #29092
This commit is contained in:
Pete Bacon Darwin 2019-03-20 13:47:59 +00:00 committed by Matias Niemelä
parent 083fb99033
commit a827bc2e3a
7 changed files with 131 additions and 119 deletions

View File

@ -12,23 +12,23 @@ ivy-ngcc
# Did it add the appropriate build markers? # Did it add the appropriate build markers?
# - esm2015 # - esm2015
grep '"__modified_by_ngcc__":[^}]*"esm2015":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"esm2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
# - fesm2015 # - fesm2015
grep '"__modified_by_ngcc__":[^}]*"fesm2015":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"fesm2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
grep '"__modified_by_ngcc__":[^}]*"es2015":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"es2015":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
# - esm5 # - esm5
grep '"__modified_by_ngcc__":[^}]*"esm5":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"esm5":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
# - fesm5 # - fesm5
grep '"__modified_by_ngcc__":[^}]*"module":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"module":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
grep '"__modified_by_ngcc__":[^}]*"fesm5":"' node_modules/@angular/common/package.json grep '"__processed_by_ivy_ngcc__":[^}]*"fesm5":"' node_modules/@angular/common/package.json
if [[ $? != 0 ]]; then exit 1; fi if [[ $? != 0 ]]; then exit 1; fi
# Did it replace the PRE_R3 markers correctly? # Did it replace the PRE_R3 markers correctly?

View File

@ -6,5 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {hasBeenProcessed as _hasBeenProcessed} from './src/packages/build_marker';
import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entry_point';
export {NgccOptions, mainNgcc as process} from './src/main'; export {NgccOptions, mainNgcc as process} from './src/main';
export {hasBeenProcessed} from './src/packages/build_marker';
export function hasBeenProcessed(packageJson: object, format: string) {
// We are wrapping this function to hide the internal types.
return _hasBeenProcessed(packageJson as EntryPointPackageJson, format as EntryPointJsonProperty);
}

View File

@ -7,10 +7,11 @@
*/ */
import {resolve} from 'canonical-path'; import {resolve} from 'canonical-path';
import {readFileSync} from 'fs';
import {AbsoluteFsPath, PathSegment} from '../../src/ngtsc/path'; import {AbsoluteFsPath} from '../../src/ngtsc/path';
import {checkMarker, writeMarker} from './packages/build_marker'; import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
import {DependencyHost} from './packages/dependency_host'; import {DependencyHost} from './packages/dependency_host';
import {DependencyResolver} from './packages/dependency_resolver'; import {DependencyResolver} from './packages/dependency_resolver';
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point'; import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
@ -67,22 +68,41 @@ export function mainNgcc({basePath, targetEntryPointPath,
undefined; undefined;
const {entryPoints} = const {entryPoints} =
finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath); finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath);
entryPoints.forEach(entryPoint => {
if (absoluteTargetEntryPointPath && entryPoints.every(entryPoint => {
return entryPoint.path !== absoluteTargetEntryPointPath;
})) {
// If we get here, then the requested entry-point did not contain anything compiled by
// the old Angular compiler. Therefore there is nothing for ngcc to do.
// So mark all formats in this entry-point as processed so that clients of ngcc can avoid
// triggering ngcc for this entry-point in the future.
const packageJsonPath =
AbsoluteFsPath.from(resolve(absoluteTargetEntryPointPath, 'package.json'));
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
propertiesToConsider.forEach(formatProperty => {
if (packageJson[formatProperty])
markAsProcessed(packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
});
return;
}
entryPoints.forEach(entryPoint => {
// Are we compiling the Angular core? // Are we compiling the Angular core?
const isCore = entryPoint.name === '@angular/core'; const isCore = entryPoint.name === '@angular/core';
const compiledFormats = new Set<string>(); const compiledFormats = new Set<string>();
const entryPointPackageJson = entryPoint.packageJson;
const entryPointPackageJsonPath = AbsoluteFsPath.from(resolve(entryPoint.path, 'package.json'));
for (let i = 0; i < propertiesToConsider.length; i++) { for (let i = 0; i < propertiesToConsider.length; i++) {
const property = propertiesToConsider[i] as EntryPointJsonProperty; const property = propertiesToConsider[i] as EntryPointJsonProperty;
const formatPath = entryPoint.packageJson[property]; const formatPath = entryPointPackageJson[property];
const format = getEntryPointFormat(property); const format = getEntryPointFormat(property);
// No format then this property is not supposed to be compiled. // No format then this property is not supposed to be compiled.
if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue; if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue;
if (checkMarker(entryPoint, property)) { if (hasBeenProcessed(entryPointPackageJson, property)) {
compiledFormats.add(formatPath); compiledFormats.add(formatPath);
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`); console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
continue; continue;
@ -109,7 +129,7 @@ export function mainNgcc({basePath, targetEntryPointPath,
// Either this format was just compiled or its underlying format was compiled because of a // Either this format was just compiled or its underlying format was compiled because of a
// previous property. // previous property.
if (compiledFormats.has(formatPath)) { if (compiledFormats.has(formatPath)) {
writeMarker(entryPoint, property); markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, property);
} }
} }

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {resolve} from 'canonical-path';
import {writeFileSync} from 'fs'; import {writeFileSync} from 'fs';
import {EntryPoint, EntryPointJsonProperty} from './entry_point'; import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
export const NGCC_VERSION = '0.0.0-PLACEHOLDER'; export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
@ -25,36 +26,19 @@ export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
* @throws Error if the `packageJson` property is not an object. * @throws Error if the `packageJson` property is not an object.
* @throws Error if the entry-point has already been processed with a different ngcc version. * @throws Error if the entry-point has already been processed with a different ngcc version.
*/ */
export function hasBeenProcessed(packageJson: any, format: string): boolean { export function hasBeenProcessed(
if (typeof packageJson !== 'object') { packageJson: EntryPointPackageJson, format: EntryPointJsonProperty): boolean {
throw new Error('`packageJson` parameter is invalid. It parameter must be an object.'); if (!packageJson.__processed_by_ivy_ngcc__) {
}
if (!packageJson.__modified_by_ngcc__) {
return false; return false;
} }
if (Object.keys(packageJson.__modified_by_ngcc__) if (Object.keys(packageJson.__processed_by_ivy_ngcc__)
.some(property => packageJson.__modified_by_ngcc__[property] !== NGCC_VERSION)) { .some(property => packageJson.__processed_by_ivy_ngcc__ ![property] !== NGCC_VERSION)) {
throw new Error( throw new Error(
'The ngcc compiler has changed since the last ngcc build.\n' + 'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.'); 'Please completely remove `node_modules` and try again.');
} }
return packageJson.__modified_by_ngcc__[format] === NGCC_VERSION; return packageJson.__processed_by_ivy_ngcc__[format] === NGCC_VERSION;
}
/**
* Check whether there is a marker for the given entry-point and format property, indicating that
* the given bundle has already been processed.
* @param entryPoint the entry-point to check for a marker.
* @param format the property in the package.json of the format for which we are checking for a
* marker.
* @returns true if the entry-point and format have already been processed with this ngcc version.
* @throws Error if the entry-point and format have already been processed with a different ngcc
* version.
*/
export function checkMarker(entryPoint: EntryPoint, format: EntryPointJsonProperty): boolean {
const pkg = entryPoint.packageJson;
return hasBeenProcessed(pkg, format);
} }
/** /**
@ -64,9 +48,10 @@ export function checkMarker(entryPoint: EntryPoint, format: EntryPointJsonProper
* @param entryPoint the entry-point to write a marker. * @param entryPoint the entry-point to write a marker.
* @param format the property in the package.json of the format for which we are writing the marker. * @param format the property in the package.json of the format for which we are writing the marker.
*/ */
export function writeMarker(entryPoint: EntryPoint, format: EntryPointJsonProperty) { export function markAsProcessed(
const pkg = entryPoint.packageJson; packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
if (!pkg.__modified_by_ngcc__) pkg.__modified_by_ngcc__ = {}; format: EntryPointJsonProperty) {
pkg.__modified_by_ngcc__[format] = NGCC_VERSION; if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
writeFileSync(resolve(entryPoint.path, 'package.json'), JSON.stringify(pkg), 'utf8'); packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
writeFileSync(packageJsonPath, JSON.stringify(packageJson), 'utf8');
} }

View File

@ -51,7 +51,7 @@ interface PackageJsonFormatProperties {
*/ */
export interface EntryPointPackageJson extends PackageJsonFormatProperties { export interface EntryPointPackageJson extends PackageJsonFormatProperties {
name: string; name: string;
__modified_by_ngcc__?: {[key: string]: string}; __processed_by_ivy_ngcc__?: {[key: string]: string};
} }
export type EntryPointJsonProperty = keyof(PackageJsonFormatProperties); export type EntryPointJsonProperty = keyof(PackageJsonFormatProperties);

View File

@ -13,6 +13,7 @@ const Module = require('module');
import {mainNgcc} from '../../src/main'; import {mainNgcc} from '../../src/main';
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers'; import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers';
import {EntryPointPackageJson} from '../../src/packages/entry_point';
describe('ngcc main()', () => { describe('ngcc main()', () => {
beforeEach(createMockFileSystem); beforeEach(createMockFileSystem);
@ -32,7 +33,7 @@ describe('ngcc main()', () => {
it('should only compile the given package entry-point (and its dependencies).', () => { it('should only compile the given package entry-point (and its dependencies).', () => {
mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http'}); mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http'});
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
@ -41,7 +42,7 @@ describe('ngcc main()', () => {
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
}); });
// * `common` is a dependency of `common/http`, so is compiled. // * `common` is a dependency of `common/http`, so is compiled.
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
@ -50,7 +51,7 @@ describe('ngcc main()', () => {
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
}); });
// * `core` is a dependency of `common`, so is compiled. // * `core` is a dependency of `common`, so is compiled.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
@ -60,7 +61,20 @@ describe('ngcc main()', () => {
}); });
// * `common/testing` is not a dependency of `common/http` so is not compiled. // * `common/testing` is not a dependency of `common/http` so is not compiled.
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toBeUndefined(); expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined();
});
it('should mark a non-Angular package target as processed', () => {
mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'});
// `test-package` has no Angular but is marked as processed.
expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({
es2015: '0.0.0-PLACEHOLDER',
});
// * `core` is a dependency of `test-package`, but it is not processed, since test-package
// was not processed.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
}); });
}); });
@ -75,22 +89,22 @@ describe('ngcc main()', () => {
// * the `main` property is UMD, which is not yet supported. // * the `main` property is UMD, which is not yet supported.
// * none of the ES2015 formats are compiled as they are not on the `propertiesToConsider` // * none of the ES2015 formats are compiled as they are not on the `propertiesToConsider`
// list. // list.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER', esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
@ -109,19 +123,19 @@ describe('ngcc main()', () => {
// * In the Angular packages fesm5 and module have the same underlying format, // * In the Angular packages fesm5 and module have the same underlying format,
// so both are marked as compiled. // so both are marked as compiled.
// * The `esm5` is not compiled because we stopped after the `fesm5` format. // * The `esm5` is not compiled because we stopped after the `fesm5` format.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
}); });
@ -135,6 +149,13 @@ function createMockFileSystem() {
'/node_modules/@angular': loadAngularPackages(), '/node_modules/@angular': loadAngularPackages(),
'/node_modules/rxjs': loadDirectory(resolveNpmTreeArtifact('rxjs', 'index.js')), '/node_modules/rxjs': loadDirectory(resolveNpmTreeArtifact('rxjs', 'index.js')),
'/node_modules/tslib': loadDirectory(resolveNpmTreeArtifact('tslib', 'tslib.js')), '/node_modules/tslib': loadDirectory(resolveNpmTreeArtifact('tslib', 'tslib.js')),
'/node_modules/test-package': {
'package.json': '{"name": "test-package", "es2015": "./index.js", "typings": "./index.d.ts"}',
'index.js':
'import {AppModule} from "@angular/common"; export class MyApp extends AppModule;',
'index.d.s':
'import {AppModule} from "@angular/common"; export declare class MyApp extends AppModule;',
}
}); });
spyOn(Module, '_resolveFilename').and.callFake(mockResolve); spyOn(Module, '_resolveFilename').and.callFake(mockResolve);
} }
@ -209,6 +230,6 @@ function mockResolve(request: string): string|null {
} }
} }
function loadPackage(packageName: string): any { function loadPackage(packageName: string): EntryPointPackageJson {
return JSON.parse(readFileSync(`/node_modules/${packageName}/package.json`, 'utf8')); return JSON.parse(readFileSync(`/node_modules/${packageName}/package.json`, 'utf8'));
} }

View File

@ -10,7 +10,7 @@ import {readFileSync, writeFileSync} from 'fs';
import * as mockFs from 'mock-fs'; import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path'; import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {checkMarker, hasBeenProcessed, writeMarker} from '../../src/packages/build_marker'; import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
import {EntryPoint} from '../../src/packages/entry_point'; import {EntryPoint} from '../../src/packages/entry_point';
function createMockFileSystem() { function createMockFileSystem() {
@ -94,104 +94,83 @@ function restoreRealFileSystem() {
mockFs.restore(); mockFs.restore();
} }
function createEntryPoint(path: string): EntryPoint {
const absolutePath = AbsoluteFsPath.from(path);
return {
name: 'some-package',
path: absolutePath,
package: absolutePath,
typings: AbsoluteFsPath.from('/typings'),
packageJson: JSON.parse(readFileSync(path + '/package.json', 'utf8'))
};
}
describe('Marker files', () => { describe('Marker files', () => {
beforeEach(createMockFileSystem); beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem); afterEach(restoreRealFileSystem);
describe('writeMarker', () => { const COMMON_PACKAGE_PATH = AbsoluteFsPath.from('/node_modules/@angular/common/package.json');
describe('markAsProcessed', () => {
it('should write a property in the package.json containing the version placeholder', () => { it('should write a property in the package.json containing the version placeholder', () => {
let pkg = JSON.parse(readFileSync('/node_modules/@angular/common/package.json', 'utf8')); let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__modified_by_ngcc__).toBeUndefined(); expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
expect(pkg.__modified_by_ngcc__).toBeUndefined(); expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
writeMarker(createEntryPoint('/node_modules/@angular/common'), 'fesm2015'); markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
pkg = JSON.parse(readFileSync('/node_modules/@angular/common/package.json', 'utf8')); pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__modified_by_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER'); expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
expect(pkg.__modified_by_ngcc__.esm5).toBeUndefined(); expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
writeMarker(createEntryPoint('/node_modules/@angular/common'), 'esm5'); markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'esm5');
pkg = JSON.parse(readFileSync('/node_modules/@angular/common/package.json', 'utf8')); pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__modified_by_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER'); expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
expect(pkg.__modified_by_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER'); expect(pkg.__processed_by_ivy_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER');
});
});
describe('checkMarker', () => {
it('should return false if the marker property does not exist', () => {
expect(checkMarker(createEntryPoint('/node_modules/@angular/common'), 'fesm2015'))
.toBe(false);
}); });
it('should return true if the marker property exists and contains the correct version', () => { it('should update the packageJson object in-place', () => {
const pkg = JSON.parse(readFileSync('/node_modules/@angular/common/package.json', 'utf8')); let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
pkg.__modified_by_ngcc__ = {fesm2015: '0.0.0-PLACEHOLDER'}; expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
writeFileSync('/node_modules/@angular/common/package.json', JSON.stringify(pkg), 'utf8'); markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
expect(checkMarker(createEntryPoint('/node_modules/@angular/common'), 'fesm2015')).toBe(true); expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
});
it('should throw if the marker property exists but contains the wrong version', () => {
const pkg = JSON.parse(readFileSync('/node_modules/@angular/common/package.json', 'utf8'));
pkg.__modified_by_ngcc__ = {fesm2015: 'WRONG_VERSION'};
writeFileSync('/node_modules/@angular/common/package.json', JSON.stringify(pkg), 'utf8');
expect(() => checkMarker(createEntryPoint('/node_modules/@angular/common'), 'fesm2015'))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
}); });
}); });
describe('hasBeenProcessed', () => { describe('hasBeenProcessed', () => {
it('should return true if the marker exists for the given format property', () => { it('should return true if the marker exists for the given format property', () => {
expect( expect(hasBeenProcessed(
hasBeenProcessed({__modified_by_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}}, 'fesm2015')) {name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'fesm2015'))
.toBe(true); .toBe(true);
}); });
it('should return false if the marker does not exist for the given format property', () => { it('should return false if the marker does not exist for the given format property', () => {
expect(hasBeenProcessed({__modified_by_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}}, 'module')) expect(hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'module'))
.toBe(false); .toBe(false);
}); });
it('should return false if the no markers exist', it('should return false if no markers exist',
() => { expect(hasBeenProcessed({}, 'module')).toBe(false); }); () => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
it('should throw an Error if the packageJson is not an object', () => { it('should throw an Error if the format has been compiled with a different version.', () => {
expect(() => hasBeenProcessed(undefined, 'fesm2015'))
.toThrowError('`packageJson` parameter is invalid. It parameter must be an object.');
expect( expect(
() => hasBeenProcessed( () => hasBeenProcessed(
'{"__modified_by_ngcc__": {"fesm2015": "0.0.0-PLACEHOLDER"}}', 'fesm2015')) {name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
.toThrowError('`packageJson` parameter is invalid. It parameter must be an object.');
});
it('should throw an Error if the format has been compiled with a different version.', () => {
expect(() => hasBeenProcessed({__modified_by_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
.toThrowError( .toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' + 'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.'); 'Please completely remove `node_modules` and try again.');
}); });
it('should throw an Error if any format has been compiled with a different version.', () => { it('should throw an Error if any format has been compiled with a different version.', () => {
expect(() => hasBeenProcessed({__modified_by_ngcc__: {'fesm2015': '8.0.0'}}, 'module')) expect(
() => hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
.toThrowError( .toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' + 'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.'); 'Please completely remove `node_modules` and try again.');
expect( expect(
() => hasBeenProcessed( () => hasBeenProcessed(
{__modified_by_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}}, {
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'module')) 'module'))
.toThrowError( .toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' + 'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.'); 'Please completely remove `node_modules` and try again.');
expect( expect(
() => hasBeenProcessed( () => hasBeenProcessed(
{__modified_by_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}}, {
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'fesm2015')) 'fesm2015'))
.toThrowError( .toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' + 'The ngcc compiler has changed since the last ngcc build.\n' +