From 55ea8da6ebf5863d345749a0d7c2c385931e6368 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 20 Mar 2019 13:47:59 +0000 Subject: [PATCH] refactor(ivy): ngcc - clean up the public API and export `hasBeenProcessed` helper (#29092) Now the public API does not contain internal types, such as `AbsoluteFsPath` and `EntryPointJsonProperty`. Instead we just accept strings and then guard them in `mainNgcc` as appropriate. A new public API function (`hasBeenProcessed`) has been exported to allow programmatic checking of the build marker when the package.json contents are already known. PR Close #29092 --- packages/compiler-cli/ngcc/index.ts | 3 +- packages/compiler-cli/ngcc/main-ngcc.ts | 23 ++++++----- packages/compiler-cli/ngcc/src/main.ts | 38 +++++++++++-------- .../ngcc/src/packages/build_marker.ts | 36 +++++++++++++----- .../ngcc/test/integration/ngcc_spec.ts | 16 +++----- 5 files changed, 68 insertions(+), 48 deletions(-) diff --git a/packages/compiler-cli/ngcc/index.ts b/packages/compiler-cli/ngcc/index.ts index b7c62ac522..84365f40f6 100644 --- a/packages/compiler-cli/ngcc/index.ts +++ b/packages/compiler-cli/ngcc/index.ts @@ -6,4 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export {mainNgcc} from './src/main'; +export {NgccOptions, mainNgcc as process} from './src/main'; +export {hasBeenProcessed} from './src/packages/build_marker'; diff --git a/packages/compiler-cli/ngcc/main-ngcc.ts b/packages/compiler-cli/ngcc/main-ngcc.ts index 0f42a84a0a..50d7d1ed6a 100644 --- a/packages/compiler-cli/ngcc/main-ngcc.ts +++ b/packages/compiler-cli/ngcc/main-ngcc.ts @@ -9,10 +9,7 @@ import * as path from 'canonical-path'; import * as yargs from 'yargs'; -import {AbsoluteFsPath} from '../src/ngtsc/path'; - import {mainNgcc} from './src/main'; -import {EntryPointJsonProperty} from './src/packages/entry_point'; // CLI entry point if (require.main === module) { @@ -21,7 +18,8 @@ if (require.main === module) { yargs .option('s', { alias: 'source', - describe: 'A path to the `node_modules` folder to compile.', + describe: + 'A path (relative to the working directory) of the `node_modules` folder to process.', default: './node_modules' }) .option('f', {alias: 'formats', hidden: true, array: true}) @@ -29,14 +27,15 @@ if (require.main === module) { alias: 'properties', array: true, describe: - 'An array of names of properties in package.json (e.g. `module` or `es2015`)\n' + - 'These properties should hold a path to a bundle-format to compile.\n' + + 'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' + + 'Each of these properties should hold the path to a bundle-format.\n' + 'If provided, only the specified properties are considered for processing.\n' + 'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.' }) .option('t', { alias: 'target', - describe: 'A path to a single entry-point to compile (plus its dependencies).', + describe: + 'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).', }) .option('first-only', { describe: @@ -51,13 +50,13 @@ if (require.main === module) { 'The formats option (-f/--formats) has been removed. Consider the properties option (-p/--properties) instead.'); process.exit(1); } - const baseSourcePath = AbsoluteFsPath.from(path.resolve(options['s'] || './node_modules')); - const propertiesToConsider: EntryPointJsonProperty[] = options['p']; - const targetEntryPointPath = - options['t'] ? AbsoluteFsPath.from(path.resolve(options['t'])) : undefined; + const baseSourcePath = path.resolve(options['s'] || './node_modules'); + const propertiesToConsider: string[] = options['p']; + const targetEntryPointPath = options['t'] ? options['t'] : undefined; const compileAllFormats = !options['first-only']; try { - mainNgcc({baseSourcePath, propertiesToConsider, targetEntryPointPath, compileAllFormats}); + mainNgcc( + {basePath: baseSourcePath, propertiesToConsider, targetEntryPointPath, compileAllFormats}); process.exitCode = 0; } catch (e) { console.error(e.stack || e.message); diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index c424966727..4fc3b7d585 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath} from '../../src/ngtsc/path'; +import {resolve} from 'canonical-path'; + +import {AbsoluteFsPath, PathSegment} from '../../src/ngtsc/path'; import {checkMarker, writeMarker} from './packages/build_marker'; import {DependencyHost} from './packages/dependency_host'; @@ -21,21 +23,23 @@ import {Transformer} from './packages/transformer'; * The options to configure the ngcc compiler. */ export interface NgccOptions { - /** The path to the node_modules folder that contains the packages to compile. */ - baseSourcePath: AbsoluteFsPath; + /** The absolute path to the `node_modules` folder that contains the packages to process. */ + basePath: string; /** - * The path, relative to `baseSourcePath` of the primary package to be compiled. - * All its dependencies will need to be compiled too. + * The path, relative to `basePath` to the primary package to be processed. + * + * All its dependencies will need to be processed too. */ - targetEntryPointPath?: AbsoluteFsPath; + targetEntryPointPath?: string; /** - * Which entry-point properties in the package.json to consider when compiling. - * Each of properties contain a path to particular bundle format for a given entry-point. + * Which entry-point properties in the package.json to consider when processing an entry-point. + * Each property should hold a path to the particular bundle format for the entry-point. + * Defaults to all the properties in the package.json. */ - propertiesToConsider?: EntryPointJsonProperty[]; + propertiesToConsider?: string[]; /** - * Whether to compile all specified formats or to stop compiling this entry-point at the first - * matching format. Defaults to `true`. + * Whether to process all formats specified by (`propertiesToConsider`) or to stop processing + * this entry-point at the first matching format. Defaults to `true`. */ compileAllFormats?: boolean; } @@ -50,15 +54,19 @@ const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015']; * * @param options The options telling ngcc what to compile and how. */ -export function mainNgcc({baseSourcePath, targetEntryPointPath, +export function mainNgcc({basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES, compileAllFormats = true}: NgccOptions): void { - const transformer = new Transformer(baseSourcePath, baseSourcePath); + const transformer = new Transformer(basePath, basePath); const host = new DependencyHost(); const resolver = new DependencyResolver(host); const finder = new EntryPointFinder(resolver); - const {entryPoints} = finder.findEntryPoints(baseSourcePath, targetEntryPointPath); + const absoluteTargetEntryPointPath = targetEntryPointPath ? + AbsoluteFsPath.from(resolve(basePath, targetEntryPointPath)) : + undefined; + const {entryPoints} = + finder.findEntryPoints(AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath); entryPoints.forEach(entryPoint => { // Are we compiling the Angular core? @@ -67,7 +75,7 @@ export function mainNgcc({baseSourcePath, targetEntryPointPath, const compiledFormats = new Set(); for (let i = 0; i < propertiesToConsider.length; i++) { - const property = propertiesToConsider[i]; + const property = propertiesToConsider[i] as EntryPointJsonProperty; const formatPath = entryPoint.packageJson[property]; const format = getEntryPointFormat(property); diff --git a/packages/compiler-cli/ngcc/src/packages/build_marker.ts b/packages/compiler-cli/ngcc/src/packages/build_marker.ts index e85e7474aa..53288f1bcd 100644 --- a/packages/compiler-cli/ngcc/src/packages/build_marker.ts +++ b/packages/compiler-cli/ngcc/src/packages/build_marker.ts @@ -14,17 +14,20 @@ import {EntryPoint, EntryPointJsonProperty} from './entry_point'; export const NGCC_VERSION = '0.0.0-PLACEHOLDER'; /** - * Check whether there is a build marker for the given entry-point and format property. - * @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 built with this ngcc version. - * @throws Error if the entry-point and format have already been built with a different ngcc + * Check whether ngcc has already processed a given entry-point format. + * + * The entry-point is defined by the package.json contents provided. + * The format is defined by the provided property name of the path to the bundle in the package.json + * + * @param packageJson The parsed contents of the package.json file for the entry-point. + * @param format The entry-point format property in the package.json to check. + * @returns true if the entry-point and format have already been processed with this ngcc version. + * @throws Error if the entry-point has already been processed with a different ngcc * version. */ -export function checkMarker(entryPoint: EntryPoint, format: EntryPointJsonProperty): boolean { - const pkg = entryPoint.packageJson; - const compiledVersion = pkg.__modified_by_ngcc__ && pkg.__modified_by_ngcc__[format]; +export function hasBeenProcessed(packageJson: any, format: string): boolean { + const compiledVersion = + packageJson && packageJson.__modified_by_ngcc__ && packageJson.__modified_by_ngcc__[format]; if (compiledVersion && compiledVersion !== NGCC_VERSION) { throw new Error( 'The ngcc compiler has changed since the last ngcc build.\n' + @@ -33,6 +36,21 @@ export function checkMarker(entryPoint: EntryPoint, format: EntryPointJsonProper return !!compiledVersion; } +/** + * 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); +} + /** * Write a build marker for the given entry-point and format property, to indicate that it has * been compiled by this version of ngcc. diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index 810d0a9464..81b9f59227 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -13,30 +13,24 @@ const Module = require('module'); import {mainNgcc} from '../../src/main'; import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers'; -import {AbsoluteFsPath} from '../../../src/ngtsc/path'; - -const NODE_MODULES = AbsoluteFsPath.from('/node_modules'); describe('ngcc main()', () => { beforeEach(createMockFileSystem); afterEach(restoreRealFileSystem); it('should run ngcc without errors for esm2015', () => { - expect(() => mainNgcc({baseSourcePath: NODE_MODULES, propertiesToConsider: ['esm2015']})) + expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']})) .not.toThrow(); }); it('should run ngcc without errors for esm5', () => { - expect(() => mainNgcc({baseSourcePath: NODE_MODULES, propertiesToConsider: ['esm5']})) + expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm5']})) .not.toThrow(); }); 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') - }); + mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http'}); expect(loadPackage('@angular/common/http').__modified_by_ngcc__).toEqual({ module: '0.0.0-PLACEHOLDER', @@ -74,7 +68,7 @@ describe('ngcc main()', () => { it('should only compile the entry-point formats given in the `propertiesToConsider` list', () => { mainNgcc({ - baseSourcePath: NODE_MODULES, + basePath: '/node_modules', propertiesToConsider: ['main', 'esm5', 'module', 'fesm5'] }); @@ -107,7 +101,7 @@ describe('ngcc main()', () => { describe('with compileAllFormats set to false', () => { it('should only compile the first matching format', () => { mainNgcc({ - baseSourcePath: NODE_MODULES, + basePath: '/node_modules', propertiesToConsider: ['main', 'module', 'fesm5', 'esm5'], compileAllFormats: false });