feat(ivy): ngcc - support only compiling the first format property to match (#29092)

By default ngcc will compile all the format properties specified. With this
change you can configure ngcc so that it will stop compiling an entry-point
after the first property that matches the `propertiesToConsider`.

PR Close #29092
This commit is contained in:
Pete Bacon Darwin 2019-03-20 13:47:58 +00:00 committed by Matias Niemelä
parent c9f7cdaafd
commit 229f035969
3 changed files with 119 additions and 51 deletions

View File

@ -38,6 +38,11 @@ if (require.main === module) {
alias: 'target',
describe: 'A path to a single entry-point to compile (plus its dependencies).',
})
.option('first-only', {
describe:
'If specified then only the first matching package.json property will be compiled',
type: 'boolean'
})
.help()
.parse(args);
@ -50,8 +55,9 @@ if (require.main === module) {
const propertiesToConsider: EntryPointJsonProperty[] = options['p'];
const targetEntryPointPath =
options['t'] ? AbsoluteFsPath.from(path.resolve(options['t'])) : undefined;
const compileAllFormats = !options['first-only'];
try {
mainNgcc({baseSourcePath, propertiesToConsider, targetEntryPointPath});
mainNgcc({baseSourcePath, propertiesToConsider, targetEntryPointPath, compileAllFormats});
process.exitCode = 0;
} catch (e) {
console.error(e.stack || e.message);

View File

@ -31,6 +31,11 @@ export interface NgccOptions {
* Each of properties contain a path to particular bundle format for a given entry-point.
*/
propertiesToConsider?: EntryPointJsonProperty[];
/**
* Whether to compile all specified formats or to stop compiling this entry-point at the first
* matching format. Defaults to `true`.
*/
compileAllFormats?: boolean;
}
const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
@ -43,8 +48,8 @@ const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015'];
*
* @param options The options telling ngcc what to compile and how.
*/
export function mainNgcc({baseSourcePath, targetEntryPointPath, propertiesToConsider}: NgccOptions):
void {
export function mainNgcc({baseSourcePath, targetEntryPointPath, propertiesToConsider,
compileAllFormats = true}: NgccOptions): void {
const transformer = new Transformer(baseSourcePath, baseSourcePath);
const host = new DependencyHost();
const resolver = new DependencyResolver(host);
@ -74,7 +79,9 @@ export function mainNgcc({baseSourcePath, targetEntryPointPath, propertiesToCons
continue;
}
if (!compiledFormats.has(formatPath)) {
// We don't break if this if statement fails because we still want to mark
// the property as processed even if its underlying format has been built already.
if (!compiledFormats.has(formatPath) && (compileAllFormats || compiledFormats.size === 0)) {
const bundle = makeEntryPointBundle(
entryPoint.path, formatPath, entryPoint.typings, isCore, format,
compiledFormats.size === 0);
@ -86,7 +93,7 @@ export function mainNgcc({baseSourcePath, targetEntryPointPath, propertiesToCons
console.warn(
`Skipping ${entryPoint.name} : ${format} (no valid entry point file for this format).`);
}
} else {
} else if (!compileAllFormats) {
console.warn(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
}

View File

@ -31,57 +31,106 @@ describe('ngcc main()', () => {
.not.toThrow();
});
it('should only compile the given package entry-point (and its dependencies)', () => {
mainNgcc({
baseSourcePath: NODE_MODULES,
targetEntryPointPath: AbsoluteFsPath.from(`${NODE_MODULES}/@angular/common/http`)
});
describe('with targetEntryPointPath', () => {
it('should only compile the given package entry-point (and its dependencies).', () => {
mainNgcc({
baseSourcePath: NODE_MODULES,
targetEntryPointPath: AbsoluteFsPath.from('/node_modules/@angular/common/http')
});
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER'
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `common` is a dependency of `common/http`, so is compiled.
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `core` is a dependency of `common`, so is compiled.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
});
// * `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').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER'
});
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
esm5: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER'
});
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toBeUndefined();
});
it('should only build the format properties specified for each entry-point', () => {
mainNgcc({baseSourcePath: NODE_MODULES, propertiesToConsider: ['main', 'esm5', 'module']});
describe('with propertiesToConsider', () => {
it('should only compile the entry-point formats given in the `propertiesToConsider` list',
() => {
mainNgcc({
baseSourcePath: NODE_MODULES,
propertiesToConsider: ['main', 'esm5', 'module', 'fesm5']
});
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
// * the `main` property is UMD, which is not yet supported.
// * none of the ES2015 formats are compiled as they are not on the `propertiesToConsider`
// list.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
});
});
});
describe('with compileAllFormats set to false', () => {
it('should only compile the first matching format', () => {
mainNgcc({
baseSourcePath: NODE_MODULES,
propertiesToConsider: ['main', 'module', 'fesm5', 'esm5'],
compileAllFormats: false
});
// * The `main` is UMD, which is not yet supported, and so is not compiled.
// * In the Angular packages fesm5 and module have the same underlying format,
// so both are marked as compiled.
// * The `esm5` is not compiled because we stopped after the `fesm5` format.
expect(loadPackage('@angular/core').__modified_by_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common').__modified_by_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/testing').__modified_by_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
});
});
});
});
@ -135,6 +184,12 @@ interface Directory {
[pathSegment: string]: string|Directory;
}
/**
* A mock implementation of the node.js Module._resolveFilename function,
* which we are spying on to support mocking out the file-system in these tests.
*
* @param request the path to a module that needs resolving.
*/
function mockResolve(request: string): string|null {
if (existsSync(request)) {
const stat = statSync(request);