From 360be02c0e6737d16fba9eedb8932842ff7236ae Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 10 Oct 2018 14:18:01 +0100 Subject: [PATCH] fix(ivy): ngcc - support Angular Material package.json format (#26403) The Material project uses slightly different properties to the core Angular project for specifying the different format entry-point. This commit ensures that we map these properties correctly for both types of project. PR Close #26403 --- .../src/ngcc/src/packages/entry_point.ts | 34 +++++++--- .../ngcc/test/packages/entry_point_spec.ts | 65 ++++++++++++++++--- 2 files changed, 79 insertions(+), 20 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts index af2a5e6fc4..7b9bec669d 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts @@ -41,11 +41,13 @@ interface EntryPointPackageJson { name: string; fesm2015?: string; fesm5?: string; + es2015?: string; // if exists then it is actually FESM2015 esm2015?: string; esm5?: string; - main?: string; - types?: string; - typings?: string; + main?: string; // UMD + module?: string; // if exists then it is actually FESM5 + types?: string; // Synonymous to `typings` property - see https://bit.ly/2OgWp2H + typings?: string; // TypeScript .d.ts files } /** @@ -60,13 +62,23 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi return null; } - // According to https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html, - // `types` and `typings` are interchangeable. - const {name, fesm2015, fesm5, esm2015, esm5, main, types, typings = types}: - EntryPointPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + // If there is `esm2015` then `es2015` will be FESM2015, otherwise ESM2015. + // If there is `esm5` then `module` will be FESM5, otherwise it will be ESM5. + const { + name, + module: modulePath, + types, + typings = types, // synonymous + es2015, + fesm2015 = es2015, // synonymous + fesm5 = modulePath, // synonymous + esm2015, + esm5, + main + }: EntryPointPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - // Minimum requirement is that we have esm2015 format and typings. - if (!typings || !esm2015) { + // Minimum requirement is that we have typings and one of esm2015 or fesm2015 formats. + if (!typings || !(fesm2015 || esm2015)) { return null; } @@ -81,9 +93,11 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi package: pkgPath, path: entryPoint, typings: path.resolve(entryPoint, typings), - esm2015: path.resolve(entryPoint, esm2015), }; + if (esm2015) { + entryPointInfo.esm2015 = path.resolve(entryPoint, esm2015); + } if (fesm2015) { entryPointInfo.fesm2015 = path.resolve(entryPoint, fesm2015); } diff --git a/packages/compiler-cli/src/ngcc/test/packages/entry_point_spec.ts b/packages/compiler-cli/src/ngcc/test/packages/entry_point_spec.ts index 61b5178b0d..3cae131478 100644 --- a/packages/compiler-cli/src/ngcc/test/packages/entry_point_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/packages/entry_point_spec.ts @@ -18,7 +18,7 @@ describe('getEntryPointInfo()', () => { () => { const entryPoint = getEntryPointInfo('/some_package', '/some_package/valid_entry_point'); expect(entryPoint).toEqual({ - name: 'some-package', + name: 'some-package/valid_entry_point', package: '/some_package', path: '/some_package/valid_entry_point', typings: `/some_package/valid_entry_point/valid_entry_point.d.ts`, @@ -35,12 +35,12 @@ describe('getEntryPointInfo()', () => { expect(entryPoint).toBe(null); }); - it('should return null if there is no typings field in the package.json', () => { + it('should return null if there is no typings or types field in the package.json', () => { const entryPoint = getEntryPointInfo('/some_package', '/some_package/missing_typings'); expect(entryPoint).toBe(null); }); - it('should return null if there is no esm2015 field in the package.json', () => { + it('should return null if there is no esm2015 nor fesm2015 field in the package.json', () => { const entryPoint = getEntryPointInfo('/some_package', '/some_package/missing_esm2015'); expect(entryPoint).toBe(null); }); @@ -49,6 +49,35 @@ describe('getEntryPointInfo()', () => { const entryPoint = getEntryPointInfo('/some_package', '/some_package/missing_metadata.json'); expect(entryPoint).toBe(null); }); + + it('should work if the typings field is named `types', () => { + const entryPoint = + getEntryPointInfo('/some_package', '/some_package/types_rather_than_typings'); + expect(entryPoint).toEqual({ + name: 'some-package/types_rather_than_typings', + package: '/some_package', + path: '/some_package/types_rather_than_typings', + typings: `/some_package/types_rather_than_typings/types_rather_than_typings.d.ts`, + fesm2015: `/some_package/types_rather_than_typings/fesm2015/types_rather_than_typings.js`, + esm2015: `/some_package/types_rather_than_typings/esm2015/types_rather_than_typings.js`, + fesm5: `/some_package/types_rather_than_typings/fesm2015/types_rather_than_typings.js`, + esm5: `/some_package/types_rather_than_typings/esm2015/types_rather_than_typings.js`, + umd: `/some_package/types_rather_than_typings/bundles/types_rather_than_typings.umd.js`, + }); + }); + + it('should work with Angular Material style package.json', () => { + const entryPoint = getEntryPointInfo('/some_package', '/some_package/material_style'); + expect(entryPoint).toEqual({ + name: 'some_package/material_style', + package: '/some_package', + path: '/some_package/material_style', + typings: `/some_package/material_style/material_style.d.ts`, + fesm2015: `/some_package/material_style/esm2015/material_style.js`, + fesm5: `/some_package/material_style/esm5/material_style.es5.js`, + umd: `/some_package/material_style/bundles/material_style.umd.js`, + }); + }); }); function createMockFileSystem() { @@ -63,16 +92,30 @@ function createMockFileSystem() { 'missing_package_json.metadata.json': 'some meta data', }, 'missing_typings': { - 'package.json': createPackageJson('missing_typings', {exclude: 'typings'}), + 'package.json': createPackageJson('missing_typings', {excludes: ['typings']}), 'missing_typings.metadata.json': 'some meta data', }, + 'types_rather_than_typings': { + 'package.json': createPackageJson('types_rather_than_typings', {}, 'types'), + 'types_rather_than_typings.metadata.json': 'some meta data', + }, 'missing_esm2015': { - 'package.json': createPackageJson('missing_esm2015', {exclude: 'esm2015'}), + 'package.json': createPackageJson('missing_fesm2015', {excludes: ['esm2015', 'fesm2015']}), 'missing_esm2015.metadata.json': 'some meta data', }, 'missing_metadata': { 'package.json': createPackageJson('missing_metadata'), // no metadata.json! + }, + 'material_style': { + 'package.json': `{ + "name": "some_package/material_style", + "typings": "./material_style.d.ts", + "main": "./bundles/material_style.umd.js", + "module": "./esm5/material_style.es5.js", + "es2015": "./esm2015/material_style.js" + }`, + 'material_style.metadata.json': 'some meta data' } } }); @@ -82,18 +125,20 @@ function restoreRealFileSystem() { mockFs.restore(); } -function createPackageJson(packageName: string, {exclude}: {exclude?: string} = {}): string { +function createPackageJson( + packageName: string, {excludes}: {excludes?: string[]} = {}, + typingsProp: string = 'typings'): string { const packageJson: any = { - name: 'some-package', - typings: `./${packageName}.d.ts`, + name: `some-package/${packageName}`, + [typingsProp]: `./${packageName}.d.ts`, fesm2015: `./fesm2015/${packageName}.js`, esm2015: `./esm2015/${packageName}.js`, fesm5: `./fesm2015/${packageName}.js`, esm5: `./esm2015/${packageName}.js`, main: `./bundles/${packageName}.umd.js`, }; - if (exclude) { - delete packageJson[exclude]; + if (excludes) { + excludes.forEach(exclude => delete packageJson[exclude]); } return JSON.stringify(packageJson); }