diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts index d4338b5d7e..092d1f65b8 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts @@ -8,6 +8,7 @@ import {DepGraph} from 'dependency-graph'; import {AbsoluteFsPath} from '../../../src/ngtsc/path'; +import {FileSystem} from '../file_system/file_system'; import {Logger} from '../logging/logger'; import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointFormat} from '../packages/entry_point'; import {DependencyHost} from './dependency_host'; @@ -65,7 +66,8 @@ export interface SortedEntryPointsInfo extends DependencyDiagnostics { entryPoin */ export class DependencyResolver { constructor( - private logger: Logger, private hosts: Partial>) {} + private fs: FileSystem, private logger: Logger, + private hosts: Partial>) {} /** * Sort the array of entry points so that the dependant entry points always come later than * their dependencies in the array. @@ -115,7 +117,7 @@ export class DependencyResolver { // Now add the dependencies between them angularEntryPoints.forEach(entryPoint => { - const formatInfo = getEntryPointFormatInfo(entryPoint); + const formatInfo = this.getEntryPointFormatInfo(entryPoint); const host = this.hosts[formatInfo.format]; if (!host) { throw new Error( @@ -164,22 +166,22 @@ export class DependencyResolver { }); } } -} -function getEntryPointFormatInfo(entryPoint: EntryPoint): - {format: EntryPointFormat, path: AbsoluteFsPath} { - const properties = Object.keys(entryPoint.packageJson); - for (let i = 0; i < properties.length; i++) { - const property = properties[i] as EntryPointJsonProperty; - const format = getEntryPointFormat(property); + private getEntryPointFormatInfo(entryPoint: EntryPoint): + {format: EntryPointFormat, path: AbsoluteFsPath} { + const properties = Object.keys(entryPoint.packageJson); + for (let i = 0; i < properties.length; i++) { + const property = properties[i] as EntryPointJsonProperty; + const format = getEntryPointFormat(this.fs, entryPoint, property); - if (format === 'esm2015' || format === 'esm5' || format === 'umd') { - const formatPath = entryPoint.packageJson[property] !; - return {format, path: AbsoluteFsPath.resolve(entryPoint.path, formatPath)}; + if (format === 'esm2015' || format === 'esm5' || format === 'umd' || format === 'commonjs') { + const formatPath = entryPoint.packageJson[property] !; + return {format, path: AbsoluteFsPath.resolve(entryPoint.path, formatPath)}; + } } + throw new Error( + `There is no appropriate source code format in '${entryPoint.path}' entry-point.`); } - throw new Error( - `There is no appropriate source code format in '${entryPoint.path}' entry-point.`); } interface DependencyGraph extends DependencyDiagnostics { diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index bf98e04b1c..ec03384e23 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -7,6 +7,7 @@ */ import {AbsoluteFsPath} from '../../src/ngtsc/path'; +import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host'; import {DependencyResolver} from './dependencies/dependency_resolver'; import {EsmDependencyHost} from './dependencies/esm_dependency_host'; import {ModuleResolver} from './dependencies/module_resolver'; @@ -64,7 +65,7 @@ export interface NgccOptions { pathMappings?: PathMappings; } -const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015', 'umd']; +const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015', 'umd', 'commonjs']; /** * This is the main entry-point into ngcc (aNGular Compatibility Compiler). @@ -83,8 +84,13 @@ export function mainNgcc( const moduleResolver = new ModuleResolver(fs, pathMappings); const esmDependencyHost = new EsmDependencyHost(fs, moduleResolver); const umdDependencyHost = new UmdDependencyHost(fs, moduleResolver); - const resolver = new DependencyResolver( - logger, {esm5: esmDependencyHost, esm2015: esmDependencyHost, umd: umdDependencyHost}); + const commonJsDependencyHost = new CommonJsDependencyHost(fs, moduleResolver); + const resolver = new DependencyResolver(fs, logger, { + esm5: esmDependencyHost, + esm2015: esmDependencyHost, + umd: umdDependencyHost, + commonjs: commonJsDependencyHost + }); const finder = new EntryPointFinder(fs, logger, resolver); const fileWriter = getFileWriter(fs, createNewEntryPointFormats); @@ -127,7 +133,7 @@ export function mainNgcc( for (let i = 0; i < propertiesToConsider.length; i++) { const property = propertiesToConsider[i] as EntryPointJsonProperty; const formatPath = entryPointPackageJson[property]; - const format = getEntryPointFormat(property); + const format = getEntryPointFormat(fs, entryPoint, property); // No format then this property is not supposed to be compiled. if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue; diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/ngcc/src/packages/entry_point.ts index dcf72cb22a..d0e485fb8d 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point.ts @@ -5,8 +5,10 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../../src/ngtsc/path'; import {FileSystem} from '../file_system/file_system'; +import {parseStatementForUmdModule} from '../host/umd_host'; import {Logger} from '../logging/logger'; /** @@ -107,7 +109,8 @@ export function getEntryPointInfo( * @param property The property to convert to a format. * @returns An entry-point format or `undefined` if none match the given property. */ -export function getEntryPointFormat(property: string): EntryPointFormat|undefined { +export function getEntryPointFormat( + fs: FileSystem, entryPoint: EntryPoint, property: string): EntryPointFormat|undefined { switch (property) { case 'fesm2015': return 'esm2015'; @@ -120,7 +123,8 @@ export function getEntryPointFormat(property: string): EntryPointFormat|undefine case 'esm5': return 'esm5'; case 'main': - return 'umd'; + const pathToMain = AbsoluteFsPath.join(entryPoint.path, entryPoint.packageJson['main'] !); + return isUmdModule(fs, pathToMain) ? 'umd' : 'commonjs'; case 'module': return 'esm5'; default: @@ -143,3 +147,10 @@ function loadEntryPointPackage( return null; } } + +function isUmdModule(fs: FileSystem, sourceFilePath: AbsoluteFsPath): boolean { + const sourceFile = + ts.createSourceFile(sourceFilePath, fs.readFile(sourceFilePath), ts.ScriptTarget.ES5); + return sourceFile.statements.length > 0 && + parseStatementForUmdModule(sourceFile.statements[0]) !== null; +} diff --git a/packages/compiler-cli/ngcc/src/packages/transformer.ts b/packages/compiler-cli/ngcc/src/packages/transformer.ts index 4b87430c40..25a972f223 100644 --- a/packages/compiler-cli/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/ngcc/src/packages/transformer.ts @@ -13,11 +13,13 @@ import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry'; import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer'; import {FileSystem} from '../file_system/file_system'; +import {CommonJsReflectionHost} from '../host/commonjs_host'; import {Esm2015ReflectionHost} from '../host/esm2015_host'; import {Esm5ReflectionHost} from '../host/esm5_host'; import {NgccReflectionHost} from '../host/ngcc_host'; import {UmdReflectionHost} from '../host/umd_host'; import {Logger} from '../logging/logger'; +import {CommonJsRenderingFormatter} from '../rendering/commonjs_rendering_formatter'; import {DtsRenderer} from '../rendering/dts_renderer'; import {Esm5RenderingFormatter} from '../rendering/esm5_rendering_formatter'; import {EsmRenderingFormatter} from '../rendering/esm_rendering_formatter'; @@ -97,6 +99,9 @@ export class Transformer { case 'umd': return new UmdReflectionHost( this.logger, isCore, bundle.src.program, bundle.src.host, bundle.dts); + case 'commonjs': + return new CommonJsReflectionHost( + this.logger, isCore, bundle.src.program, bundle.src.host, bundle.dts); default: throw new Error(`Reflection host for "${bundle.format}" not yet implemented.`); } @@ -114,6 +119,8 @@ export class Transformer { throw new Error('UmdRenderer requires a UmdReflectionHost'); } return new UmdRenderingFormatter(host, isCore); + case 'commonjs': + return new CommonJsRenderingFormatter(host, isCore); default: throw new Error(`Renderer for "${bundle.format}" not yet implemented.`); } diff --git a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts index 1faf99947d..733e9ccccf 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts @@ -25,7 +25,7 @@ describe('DependencyResolver', () => { fs = new MockFileSystem(); moduleResolver = new ModuleResolver(fs); host = new EsmDependencyHost(fs, moduleResolver); - resolver = new DependencyResolver(new MockLogger(), {esm5: host, esm2015: host}); + resolver = new DependencyResolver(fs, new MockLogger(), {esm5: host, esm2015: host}); }); describe('sortEntryPointsByDependency()', () => { const first = { @@ -117,7 +117,7 @@ describe('DependencyResolver', () => { }); it('should error if there is no appropriate DependencyHost for the given formats', () => { - resolver = new DependencyResolver(new MockLogger(), {esm2015: host}); + resolver = new DependencyResolver(fs, new MockLogger(), {esm2015: host}); expect(() => resolver.sortEntryPointsByDependency([first])) .toThrowError( `Could not find a suitable format for computing dependencies of entry-point: '${first.path}'.`); @@ -152,7 +152,8 @@ describe('DependencyResolver', () => { it('should use the appropriate DependencyHost for each entry-point', () => { const esm5Host = new EsmDependencyHost(fs, moduleResolver); const esm2015Host = new EsmDependencyHost(fs, moduleResolver); - resolver = new DependencyResolver(new MockLogger(), {esm5: esm5Host, esm2015: esm2015Host}); + resolver = + new DependencyResolver(fs, new MockLogger(), {esm5: esm5Host, esm2015: esm2015Host}); spyOn(esm5Host, 'findDependencies').and.callFake(createFakeComputeDependencies(dependencies)); spyOn(esm2015Host, 'findDependencies') .and.callFake(createFakeComputeDependencies(dependencies)); diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_finder_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_finder_spec.ts index 4669e206c3..5f7dc3ae74 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_finder_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_finder_spec.ts @@ -23,7 +23,7 @@ describe('findEntryPoints()', () => { beforeEach(() => { const fs = createMockFileSystem(); resolver = new DependencyResolver( - new MockLogger(), {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))}); + fs, new MockLogger(), {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))}); spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => { return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []}; });