ngtsc has a robust suite of testing utilities, designed for in-memory testing of a TypeScript compiler. Previously these utilities lived in the `test` directory for the compiler-cli package. This commit moves those utilities to an `ngtsc/testing` package, enabling them to be depended on separately and opening the door for using them from the upcoming language server testing infrastructure. As part of this refactoring, the `fake_core` package (a lightweight API replacement for @angular/core) is expanded to include functionality needed for Language Service test use cases. PR Close #39594
		
			
				
	
	
		
			787 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			787 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google LLC All Rights Reserved.
 | |
|  *
 | |
|  * 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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join, relative} from '../../../src/ngtsc/file_system';
 | |
| import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
 | |
| import {MockLogger} from '../../../src/ngtsc/logging/testing';
 | |
| import {loadTestFiles} from '../../../src/ngtsc/testing';
 | |
| import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration';
 | |
| import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat, getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point';
 | |
| 
 | |
| runInEachFileSystem(() => {
 | |
|   describe('getEntryPointInfo()', () => {
 | |
|     let SOME_PACKAGE: AbsoluteFsPath;
 | |
|     let _: typeof absoluteFrom;
 | |
|     let fs: FileSystem;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       _ = absoluteFrom;
 | |
|       SOME_PACKAGE = _('/project/node_modules/some_package');
 | |
|       fs = getFileSystem();
 | |
|     });
 | |
| 
 | |
|     it('should return an object containing absolute paths to the formats of the specified entry-point',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            {
 | |
|              name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
 | |
|              contents: createPackageJson('valid_entry_point')
 | |
|            },
 | |
|            {
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
 | |
|              contents: 'some meta data'
 | |
|            },
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/valid_entry_point'));
 | |
|          expect(entryPoint).toEqual({
 | |
|            name: 'some_package/valid_entry_point',
 | |
|            path: _('/project/node_modules/some_package/valid_entry_point'),
 | |
|            packageName: 'some_package',
 | |
|            packagePath: SOME_PACKAGE,
 | |
|            packageJson: loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'),
 | |
|            typings:
 | |
|                _(`/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts`),
 | |
|            compiledByAngular: true,
 | |
|            ignoreMissingDependencies: false,
 | |
|            generateDeepReexports: false,
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     it('should return `IGNORED_ENTRY_POINT` if configured to ignore the specified entry-point', () => {
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
 | |
|           contents: createPackageJson('valid_entry_point'),
 | |
|         },
 | |
|         {
 | |
|           name: _(
 | |
|               '/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
 | |
|           contents: 'some meta data',
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       spyOn(config, 'getPackageConfig')
 | |
|           .and.returnValue(new ProcessedNgccPackageConfig(
 | |
|               _('/project/node_modules/some_package'),
 | |
|               {entryPoints: {'./valid_entry_point': {ignore: true}}}));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/valid_entry_point'));
 | |
|       expect(entryPoint).toBe(IGNORED_ENTRY_POINT);
 | |
|     });
 | |
| 
 | |
|     it('should retrieve the entry-point\'s version from the package\'s `package.json`', () => {
 | |
|       const entryPointPath = join(SOME_PACKAGE, 'valid_entry_point');
 | |
| 
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/ngcc.config.js'),
 | |
|           contents: `
 | |
|             module.exports = {
 | |
|               packages: {
 | |
|                 'some_package@3': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '3'}}},
 | |
|                 },
 | |
|                 'some_package@2': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '2'}}},
 | |
|                 },
 | |
|                 'some_package@1': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '1'}}},
 | |
|                 },
 | |
|               },
 | |
|             };
 | |
|           `,
 | |
|         },
 | |
|         {
 | |
|           name: join(SOME_PACKAGE, 'package.json'),
 | |
|           contents: createPackageJson('', {version: '1.0.0'}),
 | |
|         },
 | |
|         {
 | |
|           name: join(entryPointPath, 'package.json'),
 | |
|           contents: createPackageJson('valid_entry_point', {version: '2.0.0'}),
 | |
|         },
 | |
|         {
 | |
|           name: join(entryPointPath, 'valid_entry_point.metadata.json'),
 | |
|           contents: 'some meta data',
 | |
|         },
 | |
|       ]);
 | |
| 
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const info: EntryPoint =
 | |
|           getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as any;
 | |
| 
 | |
|       expect(info.packageJson).toEqual(jasmine.objectContaining({packageVersion: '1'}));
 | |
|     });
 | |
| 
 | |
|     it('should use `null` for version if it cannot be retrieved from a `package.json`', () => {
 | |
|       const entryPointPath = join(SOME_PACKAGE, 'valid_entry_point');
 | |
| 
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/ngcc.config.js'),
 | |
|           contents: `
 | |
|             module.exports = {
 | |
|               packages: {
 | |
|                 'some_package@3': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '3'}}},
 | |
|                 },
 | |
|                 'some_package@2': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '2'}}},
 | |
|                 },
 | |
|                 'some_package@1': {
 | |
|                   entryPoints: {valid_entry_point: {override: {packageVersion: '1'}}},
 | |
|                 },
 | |
|               },
 | |
|             };
 | |
|           `,
 | |
|         },
 | |
|         {
 | |
|           name: join(SOME_PACKAGE, 'package.json'),
 | |
|           contents: createPackageJson(''),
 | |
|         },
 | |
|         {
 | |
|           name: join(entryPointPath, 'package.json'),
 | |
|           contents: createPackageJson('valid_entry_point'),
 | |
|         },
 | |
|         {
 | |
|           name: join(entryPointPath, 'valid_entry_point.metadata.json'),
 | |
|           contents: 'some meta data',
 | |
|         },
 | |
|       ]);
 | |
| 
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const info: EntryPoint =
 | |
|           getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as any;
 | |
| 
 | |
|       expect(info.packageJson).toEqual(jasmine.objectContaining({packageVersion: '3'}));
 | |
|     });
 | |
| 
 | |
|     it('should override the properties on package.json if the entry-point is configured', () => {
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
 | |
|           contents: createPackageJson('valid_entry_point'),
 | |
|         },
 | |
|         {
 | |
|           name: _(
 | |
|               '/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
 | |
|           contents: 'some meta data',
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const override = {
 | |
|         typings: './some_other.d.ts',
 | |
|         esm2015: './some_other.js',
 | |
|       };
 | |
|       spyOn(config, 'getPackageConfig')
 | |
|           .and.returnValue(new ProcessedNgccPackageConfig(
 | |
|               _('/project/node_modules/some_package'),
 | |
|               {entryPoints: {'./valid_entry_point': {override}}}));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/valid_entry_point'));
 | |
|       const overriddenPackageJson = {
 | |
|         ...loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'),
 | |
|         ...override
 | |
|       };
 | |
|       expect(entryPoint).toEqual({
 | |
|         name: 'some_package/valid_entry_point',
 | |
|         path: _('/project/node_modules/some_package/valid_entry_point'),
 | |
|         packageName: 'some_package',
 | |
|         packagePath: SOME_PACKAGE,
 | |
|         packageJson: overriddenPackageJson,
 | |
|         typings: _('/project/node_modules/some_package/valid_entry_point/some_other.d.ts'),
 | |
|         compiledByAngular: true,
 | |
|         ignoreMissingDependencies: false,
 | |
|         generateDeepReexports: false,
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should return `NO_ENTRY_POINT` if there is no package.json at the entry-point path', () => {
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _(
 | |
|               '/project/node_modules/some_package/missing_package_json/missing_package_json.metadata.json'),
 | |
|           contents: 'some meta data'
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/missing_package_json'));
 | |
|       expect(entryPoint).toBe(NO_ENTRY_POINT);
 | |
|     });
 | |
| 
 | |
|     it('should return a configured entry-point if there is no package.json at the entry-point path',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            // no package.json!
 | |
|            {
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/missing_package_json/missing_package_json.metadata.json'),
 | |
|              contents: 'some meta data',
 | |
|            },
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          const override =
 | |
|              JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']}));
 | |
|          spyOn(config, 'getPackageConfig')
 | |
|              .and.returnValue(new ProcessedNgccPackageConfig(
 | |
|                  _('/project/node_modules/some_package/'),
 | |
|                  {entryPoints: {'./missing_package_json': {override}}}));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/missing_package_json'));
 | |
|          expect(entryPoint).toEqual({
 | |
|            name: 'some_package/missing_package_json',
 | |
|            path: _('/project/node_modules/some_package/missing_package_json'),
 | |
|            packageName: 'some_package',
 | |
|            packagePath: SOME_PACKAGE,
 | |
|            packageJson: {name: 'some_package/missing_package_json', ...override},
 | |
|            typings: _(
 | |
|                '/project/node_modules/some_package/missing_package_json/missing_package_json.d.ts'),
 | |
|            compiledByAngular: true,
 | |
|            ignoreMissingDependencies: false,
 | |
|            generateDeepReexports: false,
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     [false, true].forEach(isScoped => {
 | |
|       const nameWithScope = (baseName: string) => `${isScoped ? '@some-scope/' : ''}${baseName}`;
 | |
|       const getPackageName = (packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath) => {
 | |
|         const config = new NgccConfiguration(fs, _('/project'));
 | |
|         const logger = new MockLogger();
 | |
|         const entryPoint = getEntryPointInfo(fs, config, logger, packagePath, entryPointPath);
 | |
| 
 | |
|         if (!isEntryPoint(entryPoint)) {
 | |
|           return fail(`Expected an entry point but got ${entryPoint}`);
 | |
|         }
 | |
| 
 | |
|         return entryPoint.packageName;
 | |
|       };
 | |
|       const setUpPackageWithEntryPointPackageJson =
 | |
|           (entryPointName: string, entryPointPath: AbsoluteFsPath) => {
 | |
|             // Ensure a `package.json` exists for the entry-point (containing `entryPointName`).
 | |
|             loadTestFiles([
 | |
|               {
 | |
|                 name: join(entryPointPath, 'package.json'),
 | |
|                 contents: JSON.stringify({name: entryPointName, typings: './index.d.ts'}),
 | |
|               },
 | |
|             ]);
 | |
|           };
 | |
|       const setUpPackageWithoutEntryPointPackageJson =
 | |
|           (packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath) => {
 | |
|             // Ensure there is an ngcc config for the entry-point providing a `typings` field  to
 | |
|             // avoid returning `INCOMPATIBLE_ENTRY_POINT` (since there is no `package.json`).
 | |
|             loadTestFiles([
 | |
|               {
 | |
|                 name: join(packagePath, 'ngcc.config.js'),
 | |
|                 contents: `
 | |
|                   module.exports = {
 | |
|                     entryPoints: {
 | |
|                       '${relative(packagePath, entryPointPath)}': {
 | |
|                         override: {typings: './index.d.ts'},
 | |
|                       },
 | |
|                     },
 | |
|                   };
 | |
|                 `,
 | |
|               },
 | |
|             ]);
 | |
|           };
 | |
| 
 | |
|       describe(`should compute the containing ${isScoped ? 'scoped ' : ''}package's name`, () => {
 | |
|         it('for a primary entry-point with a `package.json`', () => {
 | |
|           const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = packagePath;
 | |
|           const expectedPackageName = nameWithScope('package-json-package-name');
 | |
| 
 | |
|           setUpPackageWithEntryPointPackageJson(expectedPackageName, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a primary entry-point without a `package.json`', () => {
 | |
|           const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = packagePath;
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a secondary entry-point with a `package.json`', () => {
 | |
|           const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = join(packagePath, 'some-entry-point');
 | |
|           const expectedPackageName = nameWithScope('package-json-package-name');
 | |
| 
 | |
|           setUpPackageWithEntryPointPackageJson(
 | |
|               `${expectedPackageName}/some-entry-point`, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a secondary entry-point without a `package.json`', () => {
 | |
|           const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = join(packagePath, 'some-entry-point');
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a primary entry-point without a `package.json` in nested `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/node_modules/other-package/node_modules/${
 | |
|               nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = packagePath;
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a secondary entry-point without a `package.json` in nested `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/node_modules/other-package/node_modules/${
 | |
|               nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = join(packagePath, 'some-entry-point');
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a primary entry-point with a `package.json` outside `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = packagePath;
 | |
|           const expectedPackageName = nameWithScope('package-json-package-name');
 | |
| 
 | |
|           setUpPackageWithEntryPointPackageJson(expectedPackageName, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a primary entry-point without a `package.json` outside `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = packagePath;
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a secondary entry-point with a `package.json` outside `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = join(packagePath, 'some-entry-point');
 | |
|           const expectedPackageName = nameWithScope('package-json-package-name');
 | |
| 
 | |
|           setUpPackageWithEntryPointPackageJson(expectedPackageName, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
| 
 | |
|         it('for a secondary entry-point without a `package.json` outside `node_modules/`', () => {
 | |
|           const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`);
 | |
|           const entryPointPath = join(packagePath, 'some-entry-point');
 | |
|           const expectedPackageName = nameWithScope('on-disk-package-name');
 | |
| 
 | |
|           setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath);
 | |
| 
 | |
|           expect(getPackageName(packagePath, entryPointPath)).toBe(expectedPackageName);
 | |
|         });
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should return `INCOMPATIBLE_ENTRY_POINT` if there is no typings or types field in the package.json',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            {
 | |
|              name: _('/project/node_modules/some_package/missing_typings/package.json'),
 | |
|              contents: createPackageJson('missing_typings', {excludes: ['typings']})
 | |
|            },
 | |
|            {
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/missing_typings/missing_typings.metadata.json'),
 | |
|              contents: 'some meta data'
 | |
|            },
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/missing_typings'));
 | |
|          expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT);
 | |
|        });
 | |
| 
 | |
|     it('should return `INCOMPATIBLE_ENTRY_POINT` if the typings or types field is not a string in the package.json',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            {
 | |
|              name: _('/project/node_modules/some_package/typings_array/package.json'),
 | |
|              contents: createPackageJson('typings_array', {typingsIsArray: true})
 | |
|            },
 | |
|            {
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/typings_array/missing_typings.metadata.json'),
 | |
|              contents: 'some meta data'
 | |
|            },
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/typings_array'));
 | |
|          expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT);
 | |
|        });
 | |
| 
 | |
|     for (let prop of SUPPORTED_FORMAT_PROPERTIES) {
 | |
|       // Ignore the UMD format
 | |
|       if (prop === 'main' || prop === 'browser') continue;
 | |
|       // Let's give 'module' a specific path, otherwise compute it based on the property.
 | |
|       const typingsPath = prop === 'module' ? 'index' : `${prop}/missing_typings`;
 | |
| 
 | |
|       it(`should return an object if it can guess the typings path from the "${prop}" field`,
 | |
|          () => {
 | |
|            loadTestFiles([
 | |
|              {
 | |
|                name: _('/project/node_modules/some_package/missing_typings/package.json'),
 | |
|                contents: createPackageJson('missing_typings', {excludes: ['typings']}),
 | |
|              },
 | |
|              {
 | |
|                name: _(`/project/node_modules/some_package/missing_typings/${
 | |
|                    typingsPath}.metadata.json`),
 | |
|                contents: 'some meta data',
 | |
|              },
 | |
|              {
 | |
|                name: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`),
 | |
|                contents: '// some typings file',
 | |
|              },
 | |
|            ]);
 | |
|            const config = new NgccConfiguration(fs, _('/project'));
 | |
|            const entryPoint = getEntryPointInfo(
 | |
|                fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|                _('/project/node_modules/some_package/missing_typings'));
 | |
|            expect(entryPoint).toEqual({
 | |
|              name: 'some_package/missing_typings',
 | |
|              path: _('/project/node_modules/some_package/missing_typings'),
 | |
|              packageName: 'some_package',
 | |
|              packagePath: SOME_PACKAGE,
 | |
|              packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_typings'),
 | |
|              typings: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`),
 | |
|              compiledByAngular: true,
 | |
|              ignoreMissingDependencies: false,
 | |
|              generateDeepReexports: false,
 | |
|            });
 | |
|          });
 | |
|     }
 | |
| 
 | |
|     it('should return an object with `compiledByAngular` set to false if there is no metadata.json file next to the typing file',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            {
 | |
|              name: _('/project/node_modules/some_package/missing_metadata/package.json'),
 | |
|              contents: createPackageJson('missing_metadata')
 | |
|            },
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/missing_metadata'));
 | |
|          expect(entryPoint).toEqual({
 | |
|            name: 'some_package/missing_metadata',
 | |
|            path: _('/project/node_modules/some_package/missing_metadata'),
 | |
|            packageName: 'some_package',
 | |
|            packagePath: SOME_PACKAGE,
 | |
|            packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_metadata'),
 | |
|            typings: _(`/project/node_modules/some_package/missing_metadata/missing_metadata.d.ts`),
 | |
|            compiledByAngular: false,
 | |
|            ignoreMissingDependencies: false,
 | |
|            generateDeepReexports: false,
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     it('should return an object with `compiledByAngular` set to true if there is no metadata.json file but the entry-point has a configuration',
 | |
|        () => {
 | |
|          loadTestFiles([
 | |
|            {
 | |
|              name: _('/project/node_modules/some_package/missing_metadata/package.json'),
 | |
|              contents: createPackageJson('missing_metadata'),
 | |
|            },
 | |
|            // no metadata.json!
 | |
|          ]);
 | |
|          const config = new NgccConfiguration(fs, _('/project'));
 | |
|          spyOn(config, 'getPackageConfig')
 | |
|              .and.returnValue(new ProcessedNgccPackageConfig(
 | |
|                  _('/project/node_modules/some_package'),
 | |
|                  {entryPoints: {'./missing_metadata': {}}}));
 | |
|          const entryPoint = getEntryPointInfo(
 | |
|              fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|              _('/project/node_modules/some_package/missing_metadata'));
 | |
|          expect(entryPoint).toEqual({
 | |
|            name: 'some_package/missing_metadata',
 | |
|            path: _('/project/node_modules/some_package/missing_metadata'),
 | |
|            packageName: 'some_package',
 | |
|            packagePath: SOME_PACKAGE,
 | |
|            packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_metadata'),
 | |
|            typings: _('/project/node_modules/some_package/missing_metadata/missing_metadata.d.ts'),
 | |
|            compiledByAngular: true,
 | |
|            ignoreMissingDependencies: false,
 | |
|            generateDeepReexports: false,
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     it('should work if the typings field is named `types', () => {
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/types_rather_than_typings/package.json'),
 | |
|           contents: createPackageJson('types_rather_than_typings', {typingsProp: 'types'})
 | |
|         },
 | |
|         {
 | |
|           name: _(
 | |
|               '/project/node_modules/some_package/types_rather_than_typings/types_rather_than_typings.metadata.json'),
 | |
|           contents: 'some meta data'
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/types_rather_than_typings'));
 | |
|       expect(entryPoint).toEqual({
 | |
|         name: 'some_package/types_rather_than_typings',
 | |
|         path: _('/project/node_modules/some_package/types_rather_than_typings'),
 | |
|         packageName: 'some_package',
 | |
|         packagePath: SOME_PACKAGE,
 | |
|         packageJson:
 | |
|             loadPackageJson(fs, '/project/node_modules/some_package/types_rather_than_typings'),
 | |
|         typings: _(
 | |
|             `/project/node_modules/some_package/types_rather_than_typings/types_rather_than_typings.d.ts`),
 | |
|         compiledByAngular: true,
 | |
|         ignoreMissingDependencies: false,
 | |
|         generateDeepReexports: false,
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should work with Angular Material style package.json', () => {
 | |
|       loadTestFiles([
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/material_style/package.json'),
 | |
|           contents: `{
 | |
|             "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"
 | |
|           }`
 | |
|         },
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/material_style/material_style.metadata.json'),
 | |
|           contents: 'some meta data'
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/material_style'));
 | |
|       expect(entryPoint).toEqual({
 | |
|         name: 'some_package/material_style',
 | |
|         path: _('/project/node_modules/some_package/material_style'),
 | |
|         packageName: 'some_package',
 | |
|         packagePath: SOME_PACKAGE,
 | |
|         packageJson: loadPackageJson(fs, '/project/node_modules/some_package/material_style'),
 | |
|         typings: _(`/project/node_modules/some_package/material_style/material_style.d.ts`),
 | |
|         compiledByAngular: true,
 | |
|         ignoreMissingDependencies: false,
 | |
|         generateDeepReexports: false,
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should return `INCOMPATIBLE_ENTRY_POINT` if the package.json is not valid JSON', () => {
 | |
|       loadTestFiles([
 | |
|         // package.json might not be a valid JSON
 | |
|         // for example, @schematics/angular contains a package.json blueprint
 | |
|         // with unexpected symbols
 | |
|         {
 | |
|           name: _('/project/node_modules/some_package/unexpected_symbols/package.json'),
 | |
|           contents: '{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}'
 | |
|         },
 | |
|       ]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const entryPoint = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/unexpected_symbols'));
 | |
|       expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('getEntryPointFormat()', () => {
 | |
|     let SOME_PACKAGE: AbsoluteFsPath;
 | |
|     let _: typeof absoluteFrom;
 | |
|     let fs: FileSystem;
 | |
|     let entryPoint: EntryPoint;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       _ = absoluteFrom;
 | |
|       SOME_PACKAGE = _('/project/node_modules/some_package');
 | |
|       fs = getFileSystem();
 | |
|       loadTestFiles([{
 | |
|         name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
 | |
|         contents: createPackageJson('valid_entry_point')
 | |
|       }]);
 | |
|       const config = new NgccConfiguration(fs, _('/project'));
 | |
|       const result = getEntryPointInfo(
 | |
|           fs, config, new MockLogger(), SOME_PACKAGE,
 | |
|           _('/project/node_modules/some_package/valid_entry_point'));
 | |
|       if (!isEntryPoint(result)) {
 | |
|         return fail(`Expected an entry point but got ${result}`);
 | |
|       }
 | |
|       entryPoint = result;
 | |
|     });
 | |
| 
 | |
|     it('should return `esm2015` format for `fesm2015` property', () => {
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'fesm2015')).toBe('esm2015');
 | |
|     });
 | |
| 
 | |
|     it('should return `esm5` format for `fesm5` property', () => {
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'fesm5')).toBe('esm5');
 | |
|     });
 | |
| 
 | |
|     it('should return `esm2015` format for `es2015` property', () => {
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'es2015')).toBe('esm2015');
 | |
|     });
 | |
| 
 | |
|     it('should return `esm2015` format for `esm2015` property', () => {
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'esm2015')).toBe('esm2015');
 | |
|     });
 | |
| 
 | |
|     it('should return `esm5` format for `esm5` property', () => {
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'esm5')).toBe('esm5');
 | |
|     });
 | |
| 
 | |
|     it('should return `esm5` format for `module` property', () => {
 | |
|       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 => {
 | |
|       it('should return `esm5` for `' + browserOrMain +
 | |
|              '` if the file contains import or export statements',
 | |
|          () => {
 | |
|            const name = _(
 | |
|                '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js');
 | |
|            loadTestFiles([{name, contents: `import * as core from '@angular/core;`}]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
 | |
| 
 | |
|            loadTestFiles([{name, contents: `import {Component} from '@angular/core;`}]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
 | |
| 
 | |
|            loadTestFiles([{name, contents: `export function foo() {}`}]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
 | |
| 
 | |
|            loadTestFiles([{name, contents: `export * from 'abc';`}]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
 | |
|          });
 | |
| 
 | |
|       it('should return `umd` for `' + browserOrMain +
 | |
|              '` if the file contains a UMD wrapper function',
 | |
|          () => {
 | |
|            loadTestFiles([{
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
 | |
|              contents: `
 | |
|         (function (global, factory) {
 | |
|           typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
 | |
|           typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
 | |
|           (global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
 | |
|         }(this, function (exports, core) { 'use strict'; }));
 | |
|       `
 | |
|            }]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
 | |
|          });
 | |
| 
 | |
|       it('should return `commonjs` for `' + browserOrMain +
 | |
|              '` if the file does not contain a UMD wrapper function',
 | |
|          () => {
 | |
|            loadTestFiles([{
 | |
|              name: _(
 | |
|                  '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
 | |
|              contents: `
 | |
|           const core = require('@angular/core);
 | |
|           module.exports = {};
 | |
|         `
 | |
|            }]);
 | |
|            expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('commonjs');
 | |
|          });
 | |
| 
 | |
|       it('should resolve the format path with suitable postfixes', () => {
 | |
|         loadTestFiles([{
 | |
|           name: _(
 | |
|               '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
 | |
|           contents: `
 | |
|         (function (global, factory) {
 | |
|           typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
 | |
|           typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
 | |
|           (global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
 | |
|         }(this, function (exports, core) { 'use strict'; }));
 | |
|       `
 | |
|         }]);
 | |
| 
 | |
|         entryPoint.packageJson.main = './bundles/valid_entry_point/index';
 | |
|         expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
 | |
| 
 | |
|         entryPoint.packageJson.main = './bundles/valid_entry_point';
 | |
|         expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should return `undefined` if the `browser` property is not a string', () => {
 | |
|       entryPoint.packageJson.browser = {} as any;
 | |
|       expect(getEntryPointFormat(fs, entryPoint, 'browser')).toBeUndefined();
 | |
|     });
 | |
|   });
 | |
| });
 | |
| 
 | |
| export function createPackageJson(
 | |
|     entryPointName: string, {excludes, typingsProp = 'typings', typingsIsArray, version}: {
 | |
|       excludes?: string[],
 | |
|       typingsProp?: string,
 | |
|       typingsIsArray?: boolean,
 | |
|       version?: string
 | |
|     } = {}): string {
 | |
|   const packageJson: any = {
 | |
|     name: (entryPointName === '') ? 'some_package' : `some_package/${entryPointName}`,
 | |
|     version,
 | |
|     [typingsProp]: typingsIsArray ? [`./${entryPointName}.d.ts`] : `./${entryPointName}.d.ts`,
 | |
|     fesm2015: `./fesm2015/${entryPointName}.js`,
 | |
|     esm2015: `./esm2015/${entryPointName}.js`,
 | |
|     es2015: `./es2015/${entryPointName}.js`,
 | |
|     fesm5: `./fesm5/${entryPointName}.js`,
 | |
|     esm5: `./esm5/${entryPointName}.js`,
 | |
|     main: `./bundles/${entryPointName}/index.js`,
 | |
|     browser: `./bundles/${entryPointName}/index.js`,
 | |
|     module: './index.js',
 | |
|   };
 | |
|   if (excludes) {
 | |
|     excludes.forEach(exclude => delete packageJson[exclude]);
 | |
|   }
 | |
|   return JSON.stringify(packageJson);
 | |
| }
 | |
| 
 | |
| export function loadPackageJson(fs: FileSystem, packagePath: string) {
 | |
|   return JSON.parse(fs.readFile(fs.resolve(packagePath + '/package.json')));
 | |
| }
 |