feat(ivy): ngcc - turn on CommonJS support (#30200)

PR Close #30200
This commit is contained in:
Pete Bacon Darwin 2019-04-29 18:51:52 +01:00 committed by Jason Aden
parent c7a9987067
commit e20b92ba37
6 changed files with 51 additions and 24 deletions

View File

@ -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<Record<EntryPointFormat, DependencyHost>>) {}
private fs: FileSystem, private logger: Logger,
private hosts: Partial<Record<EntryPointFormat, DependencyHost>>) {}
/**
* 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 {

View File

@ -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;

View File

@ -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;
}

View File

@ -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.`);
}

View File

@ -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));

View File

@ -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: []};
});