feat(ngcc): support for new APF where `module` points to esm2015 output (#36944)

As of version 10, libraries following the APF will no longer contain
ESM5 output. Hence, tests in ngcc need to be updated as they currently
rely on the release output of `@angular/core`.

Additionally, we'd need to support in ngcc that the `module`
property of entry-points no longer necessarily refers to
`esm5` output, but instead can also target `esm2015`.

We currently achieve this by checking the path the `module`
property points to. We can do this because as per APF, the
folder name is known for the esm2015 output. Long-term for
more coverage, we want to sniff the format by looking for
known ES2015 constructs in the file `module` refers to.

PR Close #36944
This commit is contained in:
Paul Gschwendtner 2020-05-06 16:54:44 +02:00 committed by Alex Rickabaugh
parent d5293d2aa3
commit c98a4d6ddd
9 changed files with 231 additions and 112 deletions

View File

@ -64,42 +64,32 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'."
cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"fesm2015": "' cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"fesm2015": "'
assertSucceeded "Expected 'ngcc' to add build marker for 'fesm2015' in '@angular/common'." assertSucceeded "Expected 'ngcc' to add build marker for 'fesm2015' in '@angular/common'."
# `es2015` is an alias of `fesm2015`.
cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"es2015": "' cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"es2015": "'
assertSucceeded "Expected 'ngcc' to add build marker for 'es2015' in '@angular/common'." assertSucceeded "Expected 'ngcc' to add build marker for 'es2015' in '@angular/common'."
# - esm5 # `module` is an alias of `fesm2015`
cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"esm5": "'
assertSucceeded "Expected 'ngcc' to add build marker for 'esm5' in '@angular/common'."
# - fesm5
cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"module": "' cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"module": "'
assertSucceeded "Expected 'ngcc' to add build marker for 'module' in '@angular/common'." assertSucceeded "Expected 'ngcc' to add build marker for 'module' in '@angular/common'."
cat node_modules/@angular/common/package.json | awk 'ORS=" "' | grep '"__processed_by_ivy_ngcc__":[^}]*"fesm5": "'
assertSucceeded "Expected 'ngcc' to add build marker for 'fesm5' in '@angular/common'."
# Did it replace the PRE_R3 markers correctly? # Did it replace the PRE_R3 markers correctly?
grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/fesm2015/core.js grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/fesm2015/core.js
assertSucceeded "Expected 'ngcc' to replace 'SWITCH_COMPILE_COMPONENT__PRE_R3__' in '@angular/core' (fesm2015)." assertSucceeded "Expected 'ngcc' to replace 'SWITCH_COMPILE_COMPONENT__PRE_R3__' in '@angular/core' (fesm2015)."
grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/fesm5/core.js grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/bundles/core.umd.js
assertSucceeded "Expected 'ngcc' to replace 'SWITCH_COMPILE_COMPONENT__PRE_R3__' in '@angular/core' (fesm5)." assertSucceeded "Expected 'ngcc' to replace 'SWITCH_COMPILE_COMPONENT__PRE_R3__' in '@angular/core' (main)."
# Did it compile @angular/core/ApplicationModule correctly? # Did it compile @angular/core/ApplicationModule correctly?
grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/fesm2015/core.js grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/fesm2015/core.js
assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (fesm2015)." assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (fesm2015)."
grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/fesm5/core.js grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/bundles/core.umd.js
assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (fesm5)." assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (main)."
grep "ApplicationModule.ɵmod = ɵngcc0.ɵɵdefineNgModule" node_modules/@angular/core/esm2015/src/application_module.js grep "ApplicationModule.ɵmod = ɵngcc0.ɵɵdefineNgModule" node_modules/@angular/core/esm2015/src/application_module.js
assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (esm2015)." assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (esm2015)."
grep "ApplicationModule.ɵmod = ɵngcc0.ɵɵdefineNgModule" node_modules/@angular/core/esm5/src/application_module.js
assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (esm5)."
# Did it place the `setClassMetadata` call correctly? # Did it place the `setClassMetadata` call correctly?
cat node_modules/@angular/core/fesm2015/core.js | awk 'ORS=" "' | grep "ApplicationRef.ctorParameters.*setClassMetadata(ApplicationRef" cat node_modules/@angular/core/fesm2015/core.js | awk 'ORS=" "' | grep "ApplicationRef.ctorParameters.*setClassMetadata(ApplicationRef"
assertSucceeded "Expected 'ngcc' to place 'setClassMetadata' after static properties like 'ctorParameters' in '@angular/core' (fesm2015)." assertSucceeded "Expected 'ngcc' to place 'setClassMetadata' after static properties like 'ctorParameters' in '@angular/core' (fesm2015)."

View File

@ -45,6 +45,7 @@
"@angular-devkit/build-optimizer": "0.901.0", "@angular-devkit/build-optimizer": "0.901.0",
"@angular-devkit/core": "9.1.0", "@angular-devkit/core": "9.1.0",
"@angular-devkit/schematics": "9.1.0", "@angular-devkit/schematics": "9.1.0",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.6", "@babel/core": "^7.8.6",
"@babel/generator": "^7.8.6", "@babel/generator": "^7.8.6",
"@babel/template": "^7.8.6", "@babel/template": "^7.8.6",

View File

@ -27,7 +27,7 @@ export function parseCommandLineOptions(args: string[]): NgccOptions {
alias: 'properties', alias: 'properties',
array: true, array: true,
describe: describe:
'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' + 'An array of names of properties in package.json to compile (e.g. `module` or `main`)\n' +
'Each of these properties should hold the path to a bundle-format.\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 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.' 'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.'
@ -136,4 +136,4 @@ export function parseCommandLineOptions(args: string[]): NgccOptions {
errorOnFailedEntryPoint, errorOnFailedEntryPoint,
tsConfigPath tsConfigPath
}; };
} }

View File

@ -207,6 +207,15 @@ export function getEntryPointFormat(
} }
return sniffModuleFormat(fs, join(entryPoint.path, mainFile)); return sniffModuleFormat(fs, join(entryPoint.path, mainFile));
case 'module': case 'module':
const moduleFilePath = entryPoint.packageJson['module'];
// As of version 10, the `module` property in `package.json` should point to
// the ESM2015 format output as per Angular Package format specification. This
// means that the `module` property captures multiple formats, as old libraries
// built with the old APF can still be processed. We detect the format by checking
// the paths that should be used as per APF specification.
if (typeof moduleFilePath === 'string' && moduleFilePath.includes('esm2015')) {
return `esm2015`;
}
return 'esm5'; return 'esm5';
default: default:
return undefined; return undefined;

View File

@ -1,3 +1,4 @@
load("@npm//@babel/cli:index.bzl", "babel")
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
@ -62,11 +63,33 @@ ts_library(
], ],
) )
# As of version 10, the release packages do not contain esm2015 output anymore. The ngcc
# integration tests intend to test ES5 features though, so we downlevel the flat esm2015
# file to ES5 using Babel. We can then link that into the mock file system as if the Angular
# core package is still built with previous APF versions where esm5 output was shipped. This
# allows us to ensure that ngcc properly processes libraries with esm5 output. **Note**: We are
# using Babel instead of `tsc` as TypeScript does not allow us to downlevel the file without
# setting the module resolution to either `amd` or `system`. We want to preserve ES modules.
babel(
name = "fesm5_angular_core",
outs = ["fesm5_angular_core.js"],
args = [
"$(execpath //packages/core:npm_package)/fesm2015/core.js",
"--presets @babel/preset-env",
"--out-file $(execpath fesm5_angular_core.js)",
],
data = [
"//packages/core:npm_package",
"@npm//@babel/preset-env",
],
)
jasmine_node_test( jasmine_node_test(
name = "integration", name = "integration",
timeout = "long", timeout = "long",
bootstrap = ["//tools/testing:node_no_angular_es5"], bootstrap = ["//tools/testing:node_no_angular_es5"],
data = [ data = [
":fesm5_angular_core",
"//packages/common:npm_package", "//packages/common:npm_package",
"//packages/core:npm_package", "//packages/core:npm_package",
"@npm//rxjs", "@npm//rxjs",

View File

@ -7,6 +7,7 @@
*/ */
/// <reference types="node" /> /// <reference types="node" />
import {readFileSync} from 'fs';
import * as os from 'os'; import * as os from 'os';
import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system'; import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system';
@ -21,7 +22,7 @@ import {Transformer} from '../../src/packages/transformer';
import {DirectPackageJsonUpdater, PackageJsonUpdater} from '../../src/writing/package_json_updater'; import {DirectPackageJsonUpdater, PackageJsonUpdater} from '../../src/writing/package_json_updater';
import {MockLogger} from '../helpers/mock_logger'; import {MockLogger} from '../helpers/mock_logger';
import {compileIntoApf, compileIntoFlatEs5Package} from './util'; import {compileIntoApf, compileIntoFlatEs2015Package, compileIntoFlatEs5Package} from './util';
const testFiles = loadStandardTestFiles({fakeCore: false, rxjs: true}); const testFiles = loadStandardTestFiles({fakeCore: false, rxjs: true});
@ -41,18 +42,44 @@ runInEachFileSystem(() => {
spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'} as any]); spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'} as any]);
}); });
/**
* Sets up the esm5 format in the Angular core package. By default, package output
* no longer contains esm5 output, so we process the fesm2015 file into ES5 and
* link it as if its the ESM5 output.
*/
function setupAngularCoreEsm5() {
const pkgPath = _('/node_modules/@angular/core');
const pkgJsonPath = fs.join(pkgPath, 'package.json');
const pkgJson = JSON.parse(fs.readFile(pkgJsonPath));
fs.ensureDir(fs.join(pkgPath, 'fesm5'));
fs.writeFile(
fs.join(pkgPath, 'fesm5/core.js'),
readFileSync(require.resolve('../fesm5_angular_core.js'), 'utf8'));
pkgJson.esm5 = './fesm5/core.js';
pkgJson.fesm5 = './fesm5/core.js';
fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
}
it('should run ngcc without errors for esm2015', () => { it('should run ngcc without errors for esm2015', () => {
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']})) expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']}))
.not.toThrow(); .not.toThrow();
}); });
it('should run ngcc without errors for esm5', () => { it('should run ngcc without errors for esm5', () => {
setupAngularCoreEsm5();
expect(() => mainNgcc({ expect(() => mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['esm5'], propertiesToConsider: ['esm5'],
targetEntryPointPath: '@angular/core',
logger: new MockLogger(), logger: new MockLogger(),
})) }))
.not.toThrow(); .not.toThrow();
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeDefined();
}); });
it('should run ngcc without errors when "main" property is not present', () => { it('should run ngcc without errors when "main" property is not present', () => {
@ -114,6 +141,7 @@ runInEachFileSystem(() => {
}); });
it('should generate correct metadata for decorated getter/setter properties', () => { it('should generate correct metadata for decorated getter/setter properties', () => {
setupAngularCoreEsm5();
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs5Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Directive, Input, NgModule} from '@angular/core'; import {Directive, Input, NgModule} from '@angular/core';
@ -134,7 +162,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm5'],
}); });
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)).replace(/\s+/g, ' '); const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)).replace(/\s+/g, ' ');
@ -150,6 +178,7 @@ runInEachFileSystem(() => {
it(`should be able to process spread operator inside objects for ${ it(`should be able to process spread operator inside objects for ${
target} format (imported helpers)`, target} format (imported helpers)`,
() => { () => {
setupAngularCoreEsm5();
compileIntoApf( compileIntoApf(
'test-package', { 'test-package', {
'/index.ts': ` '/index.ts': `
@ -190,6 +219,7 @@ runInEachFileSystem(() => {
it(`should be able to process emitted spread operator inside objects for ${ it(`should be able to process emitted spread operator inside objects for ${
target} format (emitted helpers)`, target} format (emitted helpers)`,
() => { () => {
setupAngularCoreEsm5();
compileIntoApf( compileIntoApf(
'test-package', { 'test-package', {
'/index.ts': ` '/index.ts': `
@ -225,6 +255,7 @@ runInEachFileSystem(() => {
}); });
it('should not add `const` in ES5 generated code', () => { it('should not add `const` in ES5 generated code', () => {
setupAngularCoreEsm5();
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs5Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Directive, Input, NgModule} from '@angular/core'; import {Directive, Input, NgModule} from '@angular/core';
@ -246,7 +277,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm5'],
}); });
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
@ -280,7 +311,7 @@ runInEachFileSystem(() => {
` `
}); });
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Directive, Input, NgModule} from '@angular/core'; import {Directive, Input, NgModule} from '@angular/core';
import * as lib from 'lib'; import * as lib from 'lib';
@ -308,7 +339,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
@ -358,7 +389,7 @@ runInEachFileSystem(() => {
}); });
it('should add ɵfac but not duplicate ɵprov properties on injectables', () => { it('should add ɵfac but not duplicate ɵprov properties on injectables', () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Injectable, ɵɵdefineInjectable} from '@angular/core'; import {Injectable, ɵɵdefineInjectable} from '@angular/core';
export const TestClassToken = 'TestClassToken'; export const TestClassToken = 'TestClassToken';
@ -374,7 +405,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
const after = fs.readFile(_(`/node_modules/test-package/index.js`)); const after = fs.readFile(_(`/node_modules/test-package/index.js`));
@ -389,7 +420,7 @@ runInEachFileSystem(() => {
// This is necessary to ensure XPipeDef.fac is defined when delegated from injectable def // This is necessary to ensure XPipeDef.fac is defined when delegated from injectable def
it('should always generate factory def (fac) before injectable def (prov)', () => { it('should always generate factory def (fac) before injectable def (prov)', () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Injectable, Pipe, PipeTransform} from '@angular/core'; import {Injectable, Pipe, PipeTransform} from '@angular/core';
@ -406,7 +437,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
@ -508,6 +539,7 @@ runInEachFileSystem(() => {
}); });
it('should use `$localize` calls rather than tagged templates in ES5 generated code', () => { it('should use `$localize` calls rather than tagged templates in ES5 generated code', () => {
setupAngularCoreEsm5();
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs5Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Component, Input, NgModule} from '@angular/core'; import {Component, Input, NgModule} from '@angular/core';
@ -529,7 +561,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm5'],
}); });
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
@ -611,9 +643,7 @@ runInEachFileSystem(() => {
main: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
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',
esm2015: '0.0.0-PLACEHOLDER', esm2015: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}; };
@ -710,7 +740,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: '@angular/common/http/testing', targetEntryPointPath: '@angular/common/http/testing',
propertiesToConsider: ['fesm2015', 'esm5', 'esm2015'], propertiesToConsider: ['fesm2015', 'main', 'esm2015'],
logger, logger,
}); });
expect(logger.logs.debug).not.toContain([ expect(logger.logs.debug).not.toContain([
@ -727,7 +757,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: '@angular/common/http/testing', targetEntryPointPath: '@angular/common/http/testing',
propertiesToConsider: ['esm5', 'esm2015'], propertiesToConsider: ['main', 'esm2015'],
compileAllFormats: false, compileAllFormats: false,
logger, logger,
}); });
@ -784,7 +814,7 @@ runInEachFileSystem(() => {
} }
it('should clean up outdated artifacts', () => { it('should clean up outdated artifacts', () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'index.ts': ` 'index.ts': `
import {Directive} from '@angular/core'; import {Directive} from '@angular/core';
@ -795,7 +825,7 @@ runInEachFileSystem(() => {
}); });
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
logger: new MockLogger(), logger: new MockLogger(),
}); });
@ -811,12 +841,12 @@ runInEachFileSystem(() => {
// Now run ngcc again to see that it cleans out the outdated artifacts // Now run ngcc again to see that it cleans out the outdated artifacts
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
logger: new MockLogger(), logger: new MockLogger(),
}); });
const newPackageJson = loadPackage('test-package', _('/node_modules')); const newPackageJson = loadPackage('test-package', _('/node_modules'));
expect(newPackageJson.__processed_by_ivy_ngcc__).toEqual({ expect(newPackageJson.__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER', esm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(newPackageJson.module_ivy_ngcc).toBeUndefined(); expect(newPackageJson.module_ivy_ngcc).toBeUndefined();
@ -843,38 +873,41 @@ runInEachFileSystem(() => {
() => { () => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['main', 'esm5', 'module', 'fesm5'], propertiesToConsider: ['main', 'module'],
logger: new MockLogger(), logger: new MockLogger(),
}); });
// The ES2015 formats are not compiled as they are not in `propertiesToConsider`. // The ES2015 formats are not compiled as they are not in `propertiesToConsider`.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
main: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
// `module` and `es2015` are aliases of `fesm2015`.
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
main: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
// `module` and `es2015` are aliases of `fesm2015`.
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
main: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
// `module` and `es2015` are aliases for `fesm2015`.
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER',
main: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
// `module` and `es2015` are aliases for `fesm2015`.
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
}); });
@ -893,6 +926,8 @@ runInEachFileSystem(() => {
expect(logs).not.toContain(['Skipping @angular/common : es2015 (already compiled).']); expect(logs).not.toContain(['Skipping @angular/common : es2015 (already compiled).']);
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
// `module` and `es2015` are aliases of `fesm2015`.
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
@ -913,30 +948,34 @@ runInEachFileSystem(() => {
it('should only compile the first matching format', () => { it('should only compile the first matching format', () => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['module', 'fesm5', 'esm5'], propertiesToConsider: ['module', 'fesm2015', 'main'],
compileAllFormats: false, compileAllFormats: false,
logger: new MockLogger(), logger: new MockLogger(),
}); });
// * In the Angular packages fesm5 and module have the same underlying format, // * In the Angular packages fesm2015, module and `es2015` have the same
// so both are marked as compiled. // underlying format, so both are marked as compiled.
// * The `esm5` is not compiled because we stopped after the `fesm5` format. // * The `main` is not compiled because we stopped after the `fesm2015` format.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER', module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
@ -946,27 +985,25 @@ runInEachFileSystem(() => {
() => { () => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['module'], propertiesToConsider: ['main'],
compileAllFormats: false, compileAllFormats: false,
logger: new MockLogger(), logger: new MockLogger(),
}); });
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
fesm5: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
// If ngcc tries to write out the typings files again, this will throw an exception. // If ngcc tries to write out the typings files again, this will throw an exception.
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
propertiesToConsider: ['esm5'], propertiesToConsider: ['esm2015'],
compileAllFormats: false, compileAllFormats: false,
logger: new MockLogger(), logger: new MockLogger(),
}); });
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
esm5: '0.0.0-PLACEHOLDER', main: '0.0.0-PLACEHOLDER',
fesm5: '0.0.0-PLACEHOLDER', esm2015: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
}); });
@ -978,34 +1015,33 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
createNewEntryPointFormats: true, createNewEntryPointFormats: true,
propertiesToConsider: ['esm5'], propertiesToConsider: ['esm2015'],
logger: new MockLogger(), logger: new MockLogger(),
}); });
// Updates the package.json // Updates the package.json
expect(loadPackage('@angular/common').esm5).toEqual('./esm5/common.js'); expect(loadPackage('@angular/common').esm2015).toEqual('./esm2015/common.js');
expect((loadPackage('@angular/common') as any).esm5_ivy_ngcc) expect((loadPackage('@angular/common') as any).esm2015_ivy_ngcc)
.toEqual('__ivy_ngcc__/esm5/common.js'); .toEqual('__ivy_ngcc__/esm2015/common.js');
// Doesn't touch original files // Doesn't touch original files
expect(fs.readFile(_(`/node_modules/@angular/common/esm5/src/common_module.js`))) expect(fs.readFile(_(`/node_modules/@angular/common/esm2015/src/common_module.js`)))
.not.toMatch(ANGULAR_CORE_IMPORT_REGEX); .not.toMatch(ANGULAR_CORE_IMPORT_REGEX);
// Or create a backup of the original // Or create a backup of the original
expect( expect(fs.exists(
fs.exists(_(`/node_modules/@angular/common/esm5/src/common_module.js.__ivy_ngcc_bak`))) _(`/node_modules/@angular/common/esm2015/src/common_module.js.__ivy_ngcc_bak`)))
.toBe(false); .toBe(false);
// Creates new files // Creates new files
expect( expect(fs.readFile(
fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/common_module.js`))) _(`/node_modules/@angular/common/__ivy_ngcc__/esm2015/src/common_module.js`)))
.toMatch(ANGULAR_CORE_IMPORT_REGEX); .toMatch(ANGULAR_CORE_IMPORT_REGEX);
// Copies over files (unchanged) that did not need compiling // Copies over files (unchanged) that did not need compiling
expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`))) expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm2015/src/version.js`)))
.toBeTrue(); .toBeTrue();
expect(fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`))) expect(fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm2015/src/version.js`)))
.toEqual(fs.readFile(_(`/node_modules/@angular/common/esm5/src/version.js`))); .toEqual(fs.readFile(_(`/node_modules/@angular/common/esm2015/src/version.js`)));
// Overwrites .d.ts files (as usual) // Overwrites .d.ts files (as usual)
expect(fs.readFile(_(`/node_modules/@angular/common/common.d.ts`))) expect(fs.readFile(_(`/node_modules/@angular/common/common.d.ts`)))
@ -1017,69 +1053,71 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules/@angular/core', basePath: '/node_modules/@angular/core',
createNewEntryPointFormats: true, createNewEntryPointFormats: true,
propertiesToConsider: ['fesm2015', 'fesm5'], propertiesToConsider: ['fesm2015', 'main'],
}); });
const pkg: any = loadPackage('@angular/core'); const pkg: any = loadPackage('@angular/core');
// `es2015` is an alias of `fesm2015`. // `es2015` and `module` are aliases of `fesm2015`.
expect(pkg.fesm2015).toEqual('./fesm2015/core.js'); expect(pkg.fesm2015).toEqual('./fesm2015/core.js');
expect(pkg.es2015).toEqual('./fesm2015/core.js'); expect(pkg.es2015).toEqual('./fesm2015/core.js');
expect(pkg.module).toEqual('./fesm2015/core.js');
expect(pkg.fesm2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js'); expect(pkg.fesm2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js');
expect(pkg.es2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js'); expect(pkg.es2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js');
expect(pkg.module_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js');
// `module` is an alias of `fesm5`. expect(pkg.main).toEqual('./bundles/core.umd.js');
expect(pkg.fesm5).toEqual('./fesm5/core.js'); expect(pkg.main_ivy_ngcc).toEqual('__ivy_ngcc__/bundles/core.umd.js');
expect(pkg.module).toEqual('./fesm5/core.js');
expect(pkg.fesm5_ivy_ngcc).toEqual('__ivy_ngcc__/fesm5/core.js');
expect(pkg.module_ivy_ngcc).toEqual('__ivy_ngcc__/fesm5/core.js');
}); });
it('should update `package.json` deterministically (regardless of entry-point processing order)', it('should update `package.json` deterministically (regardless of entry-point processing order)',
() => { () => {
// Ensure formats are not marked as processed in `package.json` at the beginning. // Ensure formats are not marked as processed in `package.json` at the beginning.
let pkg = loadPackage('@angular/core'); let pkg = loadPackage('@angular/core');
expectNotToHaveProp(pkg, 'esm5_ivy_ngcc'); expectNotToHaveProp(pkg, 'main_ivy_ngcc');
expectNotToHaveProp(pkg, 'esm2015_ivy_ngcc');
expectNotToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectNotToHaveProp(pkg, 'fesm2015_ivy_ngcc');
expectNotToHaveProp(pkg, 'fesm5_ivy_ngcc'); expectNotToHaveProp(pkg, 'module_ivy_ngcc');
expectNotToHaveProp(pkg, '__processed_by_ivy_ngcc__'); expectNotToHaveProp(pkg, '__processed_by_ivy_ngcc__');
// Process `fesm2015` and update `package.json`. // Process `fesm2015` and update `package.json`.
pkg = processFormatAndUpdatePackageJson('fesm2015'); pkg = processFormatAndUpdatePackageJson('fesm2015');
expectNotToHaveProp(pkg, 'esm5_ivy_ngcc'); expectNotToHaveProp(pkg, 'main_ivy_ngcc');
expectNotToHaveProp(pkg, 'esm2015_ivy_ngcc');
expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
expectNotToHaveProp(pkg, 'fesm5_ivy_ngcc'); expectToHaveProp(pkg, 'module_ivy_ngcc');
expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'fesm2015'); expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'fesm2015');
// Process `fesm5` and update `package.json`. // Process `esm2015` and update `package.json`.
pkg = processFormatAndUpdatePackageJson('fesm5'); pkg = processFormatAndUpdatePackageJson('esm2015');
expectNotToHaveProp(pkg, 'esm5_ivy_ngcc'); expectNotToHaveProp(pkg, 'main_ivy_ngcc');
expectToHaveProp(pkg, 'esm2015_ivy_ngcc');
expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
expectToHaveProp(pkg, 'fesm5_ivy_ngcc'); expectToHaveProp(pkg, 'module_ivy_ngcc');
expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'fesm5'); expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'esm2015');
// Process `esm5` and update `package.json`. // Process `main` and update `package.json`.
pkg = processFormatAndUpdatePackageJson('esm5'); pkg = processFormatAndUpdatePackageJson('main');
expectToHaveProp(pkg, 'esm5_ivy_ngcc'); expectToHaveProp(pkg, 'main_ivy_ngcc');
expectToHaveProp(pkg, 'esm2015_ivy_ngcc');
expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
expectToHaveProp(pkg, 'fesm5_ivy_ngcc'); expectToHaveProp(pkg, 'module_ivy_ngcc');
expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'esm5'); expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'main');
// Ensure the properties are in deterministic order (regardless of processing order). // Ensure the properties are in deterministic order (regardless of processing order).
const pkgKeys = stringifyKeys(pkg); const pkgKeys = stringifyKeys(pkg);
expect(pkgKeys).toContain('|esm5_ivy_ngcc|esm5|'); expect(pkgKeys).toContain('|main_ivy_ngcc|main|');
expect(pkgKeys).toContain('|fesm2015_ivy_ngcc|fesm2015|'); expect(pkgKeys).toContain('|fesm2015_ivy_ngcc|fesm2015|');
expect(pkgKeys).toContain('|fesm5_ivy_ngcc|fesm5|'); expect(pkgKeys).toContain('|esm2015_ivy_ngcc|esm2015|');
// NOTE: // NOTE:
// Along with the first format that is processed, the typings are processed as well. // Along with the first format that is processed, the typings are processed as well.
// Also, once a property has been processed, alias properties as also marked as // Also, once a property has been processed, alias properties as also marked as
// processed. Aliases properties are properties that point to the same entry-point file. // processed. Aliases properties are properties that point to the same entry-point file.
// For example: // For example:
// - `fesm2015` <=> `es2015` // - `fesm2015` <=> `module <=> es2015`
// - `fesm5` <=> `module`
expect(stringifyKeys(pkg.__processed_by_ivy_ngcc__!)) expect(stringifyKeys(pkg.__processed_by_ivy_ngcc__!))
.toBe('|es2015|esm5|fesm2015|fesm5|module|typings|'); .toBe('|es2015|esm2015|fesm2015|main|module|typings|');
// Helpers // Helpers
function expectNotToHaveProp(obj: object, prop: string) { function expectNotToHaveProp(obj: object, prop: string) {
@ -1118,11 +1156,11 @@ runInEachFileSystem(() => {
fs.writeFile(_('/yarn.lock'), 'DUMMY YARN LOCK FILE'); fs.writeFile(_('/yarn.lock'), 'DUMMY YARN LOCK FILE');
// Populate the manifest file // Populate the manifest file
mainNgcc( mainNgcc(
{basePath: '/node_modules', propertiesToConsider: ['esm5'], logger: new MockLogger()}); {basePath: '/node_modules', propertiesToConsider: ['main'], logger: new MockLogger()});
// Check that common/testing ES5 was processed // Check that common/testing ES5 was processed
let commonTesting = let commonTesting =
JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json')));
expect(hasBeenProcessed(commonTesting, 'esm5')).toBe(true); expect(hasBeenProcessed(commonTesting, 'main')).toBe(true);
expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(false); expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(false);
// Modify the manifest to test that is has no effect // Modify the manifest to test that is has no effect
let manifest: EntryPointManifestFile = let manifest: EntryPointManifestFile =
@ -1141,7 +1179,7 @@ runInEachFileSystem(() => {
// Check that common/testing ES2015 is now processed, despite the manifest not listing it // Check that common/testing ES2015 is now processed, despite the manifest not listing it
commonTesting = commonTesting =
JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json')));
expect(hasBeenProcessed(commonTesting, 'esm5')).toBe(true); expect(hasBeenProcessed(commonTesting, 'main')).toBe(true);
expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(true); expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(true);
// Check that the newly computed manifest has written to disk, containing the path that we // Check that the newly computed manifest has written to disk, containing the path that we
// had removed earlier. // had removed earlier.
@ -1383,6 +1421,8 @@ runInEachFileSystem(() => {
logger logger
}); });
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
// `module` and `es2015` are aliases for `fesm2015`.
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
@ -1523,6 +1563,7 @@ runInEachFileSystem(() => {
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
}); });
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
@ -1550,6 +1591,7 @@ runInEachFileSystem(() => {
mainNgcc({basePath: '/node_modules', propertiesToConsider: ['es2015']}); mainNgcc({basePath: '/node_modules', propertiesToConsider: ['es2015']});
// We process core but not core/testing. // We process core but not core/testing.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
@ -1558,6 +1600,7 @@ runInEachFileSystem(() => {
// We do not compile common but we do compile its sub-entry-points. // We do not compile common but we do compile its sub-entry-points.
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toBeUndefined(); expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toBeUndefined();
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
module: '0.0.0-PLACEHOLDER',
es2015: '0.0.0-PLACEHOLDER', es2015: '0.0.0-PLACEHOLDER',
fesm2015: '0.0.0-PLACEHOLDER', fesm2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER',
@ -1629,7 +1672,7 @@ runInEachFileSystem(() => {
describe('undecorated child class migration', () => { describe('undecorated child class migration', () => {
it('should generate a directive definition with CopyDefinitionFeature for an undecorated child directive', it('should generate a directive definition with CopyDefinitionFeature for an undecorated child directive',
() => { () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Directive, NgModule} from '@angular/core'; import {Directive, NgModule} from '@angular/core';
@ -1651,7 +1694,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
@ -1670,7 +1713,7 @@ runInEachFileSystem(() => {
it('should generate a component definition with CopyDefinitionFeature for an undecorated child component', it('should generate a component definition with CopyDefinitionFeature for an undecorated child component',
() => { () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Component, NgModule} from '@angular/core'; import {Component, NgModule} from '@angular/core';
@ -1692,7 +1735,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
@ -1710,7 +1753,7 @@ runInEachFileSystem(() => {
it('should generate directive definitions with CopyDefinitionFeature for undecorated child directives in a long inheritance chain', it('should generate directive definitions with CopyDefinitionFeature for undecorated child directives in a long inheritance chain',
() => { () => {
compileIntoFlatEs5Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `
import {Directive, NgModule} from '@angular/core'; import {Directive, NgModule} from '@angular/core';
@ -1735,7 +1778,7 @@ runInEachFileSystem(() => {
mainNgcc({ mainNgcc({
basePath: '/node_modules', basePath: '/node_modules',
targetEntryPointPath: 'test-package', targetEntryPointPath: 'test-package',
propertiesToConsider: ['module'], propertiesToConsider: ['esm2015'],
}); });
const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`)); const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`));

View File

@ -33,7 +33,28 @@ export function compileIntoFlatEs5Package(pkgName: string, sources: PackageSourc
compileIntoFlatPackage(pkgName, sources, { compileIntoFlatPackage(pkgName, sources, {
target: ts.ScriptTarget.ES5, target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.ESNext, module: ts.ModuleKind.ESNext,
formatProperty: 'module', formatProperty: 'esm5',
});
}
/**
* Instead of writing packaged code by hand, and manually describing the layout of the package,
* this function transpiles the TypeScript sources into a flat file structure using the ES2015
* format. In this package layout, all compiled sources are at the root of the package, with
* `.d.ts` files next to the `.js` files. Each `.js` also has a corresponding `.metadata.js`
* file alongside with it.
*
* All generated code is written into the `node_modules` in the top-level filesystem, ready for use
* in testing ngcc.
*
* @param pkgName The name of the package to compile.
* @param sources The TypeScript sources to compile.
*/
export function compileIntoFlatEs2015Package(pkgName: string, sources: PackageSources): void {
compileIntoFlatPackage(pkgName, sources, {
target: ts.ScriptTarget.ES2015,
module: ts.ModuleKind.ESNext,
formatProperty: 'esm2015',
}); });
} }
@ -184,7 +205,7 @@ export function compileIntoApf(
version: '0.0.1', version: '0.0.1',
esm5: './esm5/index.js', esm5: './esm5/index.js',
esm2015: './esm2015/index.js', esm2015: './esm2015/index.js',
module: './esm5/index.js', module: './esm2015/index.js',
typings: './index.d.ts', typings: './index.d.ts',
}; };

View File

@ -429,6 +429,12 @@ runInEachFileSystem(() => {
expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5'); expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5');
}); });
it('should return `esm2015` format for `module` property if it points to esm2015 output',
() => {
entryPoint.packageJson['module'] = '../fesm2015/valid-entry-point.js';
expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm2015');
});
(['browser', 'main'] as EntryPointJsonProperty[]).forEach(browserOrMain => { (['browser', 'main'] as EntryPointJsonProperty[]).forEach(browserOrMain => {
it('should return `esm5` for `' + browserOrMain + it('should return `esm5` for `' + browserOrMain +
'` if the file contains import or export statements', '` if the file contains import or export statements',

View File

@ -153,6 +153,22 @@
universal-analytics "0.4.20" universal-analytics "0.4.20"
uuid "7.0.2" uuid "7.0.2"
"@babel/cli@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c"
integrity sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==
dependencies:
commander "^4.0.1"
convert-source-map "^1.1.0"
fs-readdir-recursive "^1.1.0"
glob "^7.0.0"
lodash "^4.17.13"
make-dir "^2.1.0"
slash "^2.0.0"
source-map "^0.5.0"
optionalDependencies:
chokidar "^2.1.8"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
version "7.8.3" version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@ -3984,7 +4000,7 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8:
through2 "^3.0.0" through2 "^3.0.0"
trim-off-newlines "^1.0.0" trim-off-newlines "^1.0.0"
convert-source-map@^1.5.1, convert-source-map@^1.7.0: convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@ -6039,6 +6055,11 @@ fs-promise@0.3.1:
dependencies: dependencies:
any-promise "~0.1.0" any-promise "~0.1.0"
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
fs-write-stream-atomic@^1.0.8: fs-write-stream-atomic@^1.0.8:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@ -12626,6 +12647,11 @@ slash@^1.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
slash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slash@^3.0.0: slash@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"