refactor(ivy): implement a virtual file-system layer in ngtsc + ngcc (#30921)
To improve cross platform support, all file access (and path manipulation) is now done through a well known interface (`FileSystem`). For testing a number of `MockFileSystem` implementations are provided. These provide an in-memory file-system which emulates operating systems like OS/X, Unix and Windows. The current file system is always available via the static method, `FileSystem.getFileSystem()`. This is also used by a number of static methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass `FileSystem` objects around all the time. The result of this is that one must be careful to ensure that the file-system has been initialized before using any of these static methods. To prevent this happening accidentally the current file system always starts out as an instance of `InvalidFileSystem`, which will throw an error if any of its methods are called. You can set the current file-system by calling `FileSystem.setFileSystem()`. During testing you can call the helper function `initMockFileSystem(os)` which takes a string name of the OS to emulate, and will also monkey-patch aspects of the TypeScript library to ensure that TS is also using the current file-system. Finally there is the `NgtscCompilerHost` to be used for any TypeScript compilation, which uses a given file-system. All tests that interact with the file-system should be tested against each of the mock file-systems. A series of helpers have been provided to support such tests: * `runInEachFileSystem()` - wrap your tests in this helper to run all the wrapped tests in each of the mock file-systems. * `addTestFilesToFileSystem()` - use this to add files and their contents to the mock file system for testing. * `loadTestFilesFromDisk()` - use this to load a mirror image of files on disk into the in-memory mock file-system. * `loadFakeCore()` - use this to load a fake version of `@angular/core` into the mock file-system. All ngcc and ngtsc source and tests now use this virtual file-system setup. PR Close #30921
This commit is contained in:

committed by
Kara Erickson

parent
1e7e065423
commit
7186f9c016
@ -5,168 +5,159 @@
|
||||
* 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 {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
|
||||
import {MockFileSystem} from '../helpers/mock_file_system';
|
||||
|
||||
function createMockFileSystem() {
|
||||
return new MockFileSystem({
|
||||
'/node_modules/@angular/common': {
|
||||
'package.json': `{
|
||||
"fesm2015": "./fesm2015/common.js",
|
||||
"fesm5": "./fesm5/common.js",
|
||||
"typings": "./common.d.ts"
|
||||
}`,
|
||||
'fesm2015': {
|
||||
'common.js': 'DUMMY CONTENT',
|
||||
'http.js': 'DUMMY CONTENT',
|
||||
'http/testing.js': 'DUMMY CONTENT',
|
||||
'testing.js': 'DUMMY CONTENT',
|
||||
},
|
||||
'http': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../fesm2015/http.js",
|
||||
"fesm5": "../fesm5/http.js",
|
||||
"typings": "./http.d.ts"
|
||||
}`,
|
||||
'testing': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../../fesm2015/http/testing.js",
|
||||
"fesm5": "../../fesm5/http/testing.js",
|
||||
"typings": "../http/testing.d.ts"
|
||||
}`,
|
||||
runInEachFileSystem(() => {
|
||||
describe('Marker files', () => {
|
||||
let _: typeof absoluteFrom;
|
||||
beforeEach(() => {
|
||||
_ = absoluteFrom;
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/@angular/common/package.json'),
|
||||
contents:
|
||||
`{"fesm2015": "./fesm2015/common.js", "fesm5": "./fesm5/common.js", "typings": "./common.d.ts"}`
|
||||
},
|
||||
},
|
||||
'other': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
'testing': {
|
||||
'package.json': `{
|
||||
"fesm2015": "../fesm2015/testing.js",
|
||||
"fesm5": "../fesm5/testing.js",
|
||||
"typings": "../testing.d.ts"
|
||||
}`,
|
||||
},
|
||||
'node_modules': {
|
||||
'tslib': {
|
||||
'package.json': '{ }',
|
||||
'node_modules': {
|
||||
'other-lib': {
|
||||
'package.json': '{ }',
|
||||
},
|
||||
},
|
||||
{name: _('/node_modules/@angular/common/fesm2015/common.js'), contents: 'DUMMY CONTENT'},
|
||||
{name: _('/node_modules/@angular/common/fesm2015/http.js'), contents: 'DUMMY CONTENT'},
|
||||
{
|
||||
name: _('/node_modules/@angular/common/fesm2015/http/testing.js'),
|
||||
contents: 'DUMMY CONTENT'
|
||||
},
|
||||
},
|
||||
},
|
||||
'/node_modules/@angular/no-typings': {
|
||||
'package.json': `{
|
||||
"fesm2015": "./fesm2015/index.js"
|
||||
}`,
|
||||
'fesm2015': {
|
||||
'index.js': 'DUMMY CONTENT',
|
||||
'index.d.ts': 'DUMMY CONTENT',
|
||||
},
|
||||
},
|
||||
'/node_modules/@angular/other': {
|
||||
'not-package.json': '{ "fesm2015": "./fesm2015/other.js" }',
|
||||
'package.jsonot': '{ "fesm5": "./fesm5/other.js" }',
|
||||
},
|
||||
'/node_modules/@angular/other2': {
|
||||
'node_modules_not': {
|
||||
'lib1': {
|
||||
'package.json': '{ }',
|
||||
{name: _('/node_modules/@angular/common/fesm2015/testing.js'), contents: 'DUMMY CONTENT'},
|
||||
{
|
||||
name: _('/node_modules/@angular/common/http/package.json'),
|
||||
contents:
|
||||
`{"fesm2015": "../fesm2015/http.js", "fesm5": "../fesm5/http.js", "typings": "./http.d.ts"}`
|
||||
},
|
||||
},
|
||||
'not_node_modules': {
|
||||
'lib2': {
|
||||
'package.json': '{ }',
|
||||
{
|
||||
name: _('/node_modules/@angular/common/http/testing/package.json'),
|
||||
contents:
|
||||
`{"fesm2015": "../../fesm2015/http/testing.js", "fesm5": "../../fesm5/http/testing.js", "typings": "../http/testing.d.ts" }`
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe('Marker files', () => {
|
||||
const COMMON_PACKAGE_PATH = AbsoluteFsPath.from('/node_modules/@angular/common/package.json');
|
||||
|
||||
describe('markAsProcessed', () => {
|
||||
it('should write a property in the package.json containing the version placeholder', () => {
|
||||
const fs = createMockFileSystem();
|
||||
|
||||
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'esm5');
|
||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER');
|
||||
{name: _('/node_modules/@angular/common/other/package.json'), contents: '{ }'},
|
||||
{
|
||||
name: _('/node_modules/@angular/common/testing/package.json'),
|
||||
contents:
|
||||
`{"fesm2015": "../fesm2015/testing.js", "fesm5": "../fesm5/testing.js", "typings": "../testing.d.ts"}`
|
||||
},
|
||||
{name: _('/node_modules/@angular/common/node_modules/tslib/package.json'), contents: '{ }'},
|
||||
{
|
||||
name: _(
|
||||
'/node_modules/@angular/common/node_modules/tslib/node_modules/other-lib/package.json'),
|
||||
contents: '{ }'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/@angular/no-typings/package.json'),
|
||||
contents: `{ "fesm2015": "./fesm2015/index.js" }`
|
||||
},
|
||||
{name: _('/node_modules/@angular/no-typings/fesm2015/index.js'), contents: 'DUMMY CONTENT'},
|
||||
{
|
||||
name: _('/node_modules/@angular/no-typings/fesm2015/index.d.ts'),
|
||||
contents: 'DUMMY CONTENT'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/@angular/other/not-package.json'),
|
||||
contents: '{ "fesm2015": "./fesm2015/other.js" }'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/@angular/other/package.jsonot'),
|
||||
contents: '{ "fesm5": "./fesm5/other.js" }'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/@angular/other2/node_modules_not/lib1/package.json'),
|
||||
contents: '{ }'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/@angular/other2/not_node_modules/lib2/package.json'),
|
||||
contents: '{ }'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update the packageJson object in-place', () => {
|
||||
const fs = createMockFileSystem();
|
||||
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
});
|
||||
describe('markAsProcessed', () => {
|
||||
it('should write a property in the package.json containing the version placeholder', () => {
|
||||
const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json');
|
||||
const fs = getFileSystem();
|
||||
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
|
||||
describe('hasBeenProcessed', () => {
|
||||
it('should return true if the marker exists for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'fesm2015'))
|
||||
.toBe(true);
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'esm5');
|
||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
|
||||
it('should update the packageJson object in-place', () => {
|
||||
const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json');
|
||||
const fs = getFileSystem();
|
||||
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
});
|
||||
it('should return false if the marker does not exist for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'module'))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should return false if no markers exist',
|
||||
() => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
|
||||
it('should throw an Error if the format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
});
|
||||
it('should throw an Error if any format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
|
||||
describe('hasBeenProcessed', () => {
|
||||
it('should return true if the marker exists for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'fesm2015'))
|
||||
.toBe(true);
|
||||
});
|
||||
it('should return false if the marker does not exist for the given format property', () => {
|
||||
expect(hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
|
||||
'module'))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should return false if no markers exist',
|
||||
() => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
|
||||
it('should throw an Error if the format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
});
|
||||
it('should throw an Error if any format has been compiled with a different version.', () => {
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'module'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
expect(
|
||||
() => hasBeenProcessed(
|
||||
{
|
||||
name: 'test',
|
||||
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
|
||||
},
|
||||
'fesm2015'))
|
||||
.toThrowError(
|
||||
'The ngcc compiler has changed since the last ngcc build.\n' +
|
||||
'Please completely remove `node_modules` and try again.');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,118 +5,161 @@
|
||||
* 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 {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
|
||||
import {MockFileSystem} from '../helpers/mock_file_system';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
runInEachFileSystem(() => {
|
||||
describe('entry point bundle', () => {
|
||||
|
||||
function createMockFileSystem() {
|
||||
return new MockFileSystem({
|
||||
'/node_modules/test': {
|
||||
'package.json':
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}',
|
||||
'index.d.ts': 'export * from "./public_api";',
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'index.metadata.json': '...',
|
||||
'public_api.d.ts': `
|
||||
function setupMockFileSystem(): void {
|
||||
const _ = absoluteFrom;
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/test/package.json'),
|
||||
contents:
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{name: _('/node_modules/test/index.d.ts'), contents: 'export * from "./public_api";'},
|
||||
{name: _('/node_modules/test/index.js'), contents: 'export * from "./public_api";'},
|
||||
{name: _('/node_modules/test/index.metadata.json'), contents: '...'},
|
||||
{
|
||||
name: _('/node_modules/test/public_api.d.ts'),
|
||||
contents: `
|
||||
export * from "test/secondary";
|
||||
export * from "./nested";
|
||||
export declare class TestClass {};
|
||||
`,
|
||||
'public_api.js': `
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/public_api.js'),
|
||||
contents: `
|
||||
export * from "test/secondary";
|
||||
export * from "./nested";
|
||||
export const TestClass = function() {};
|
||||
`,
|
||||
'root.d.ts': `
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/root.d.ts'),
|
||||
contents: `
|
||||
import * from 'other';
|
||||
export declare class RootClass {};
|
||||
`,
|
||||
'root.js': `
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/root.js'),
|
||||
contents: `
|
||||
import * from 'other';
|
||||
export const RootClass = function() {};
|
||||
`,
|
||||
'nested': {
|
||||
'index.d.ts': 'export * from "../root";',
|
||||
'index.js': 'export * from "../root";',
|
||||
},
|
||||
'es2015': {
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'public_api.js': 'export class TestClass {};',
|
||||
'root.js': `
|
||||
`
|
||||
},
|
||||
{name: _('/node_modules/test/nested/index.d.ts'), contents: 'export * from "../root";'},
|
||||
{name: _('/node_modules/test/nested/index.js'), contents: 'export * from "../root";'},
|
||||
{name: _('/node_modules/test/es2015/index.js'), contents: 'export * from "./public_api";'},
|
||||
{
|
||||
name: _('/node_modules/test/es2015/public_api.js'),
|
||||
contents: 'export class TestClass {};'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/es2015/root.js'),
|
||||
contents: `
|
||||
import * from 'other';
|
||||
export class RootClass {};
|
||||
`,
|
||||
'nested': {
|
||||
'index.js': 'export * from "../root";',
|
||||
`
|
||||
},
|
||||
},
|
||||
'secondary': {
|
||||
'package.json':
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}',
|
||||
'index.d.ts': 'export * from "./public_api";',
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'index.metadata.json': '...',
|
||||
'public_api.d.ts': 'export declare class SecondaryClass {};',
|
||||
'public_api.js': 'export class SecondaryClass {};',
|
||||
'es2015': {
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'public_api.js': 'export class SecondaryClass {};',
|
||||
{
|
||||
name: _('/node_modules/test/es2015/nested/index.js'),
|
||||
contents: 'export * from "../root";'
|
||||
},
|
||||
},
|
||||
},
|
||||
'/node_modules/other': {
|
||||
'package.json':
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}',
|
||||
'index.d.ts': 'export * from "./public_api";',
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'index.metadata.json': '...',
|
||||
'public_api.d.ts': 'export declare class OtherClass {};',
|
||||
'public_api.js': 'export class OtherClass {};',
|
||||
'es2015': {
|
||||
'index.js': 'export * from "./public_api";',
|
||||
'public_api.js': 'export class OtherClass {};',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
{
|
||||
name: _('/node_modules/test/secondary/package.json'),
|
||||
contents:
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/index.d.ts'),
|
||||
contents: 'export * from "./public_api";'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/index.js'),
|
||||
contents: 'export * from "./public_api";'
|
||||
},
|
||||
{name: _('/node_modules/test/secondary/index.metadata.json'), contents: '...'},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/public_api.d.ts'),
|
||||
contents: 'export declare class SecondaryClass {};'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/public_api.js'),
|
||||
contents: 'export class SecondaryClass {};'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/es2015/index.js'),
|
||||
contents: 'export * from "./public_api";'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/secondary/es2015/public_api.js'),
|
||||
contents: 'export class SecondaryClass {};'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/other/package.json'),
|
||||
contents:
|
||||
'{"module": "./index.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{name: _('/node_modules/other/index.d.ts'), contents: 'export * from "./public_api";'},
|
||||
{name: _('/node_modules/other/index.js'), contents: 'export * from "./public_api";'},
|
||||
{name: _('/node_modules/other/index.metadata.json'), contents: '...'},
|
||||
{
|
||||
name: _('/node_modules/other/public_api.d.ts'),
|
||||
contents: 'export declare class OtherClass {};'
|
||||
},
|
||||
{name: _('/node_modules/other/public_api.js'), contents: 'export class OtherClass {};'},
|
||||
{name: _('/node_modules/other/es2015/index.js'), contents: 'export * from "./public_api";'},
|
||||
{
|
||||
name: _('/node_modules/other/es2015/public_api.js'),
|
||||
contents: 'export class OtherClass {};'
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
describe('entry point bundle', () => {
|
||||
// https://github.com/angular/angular/issues/29939
|
||||
it('should resolve JavaScript sources instead of declaration files if they are adjacent', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const esm5bundle = makeEntryPointBundle(
|
||||
fs, '/node_modules/test', './index.js', './index.d.ts', false, 'esm5', 'esm5', true) !;
|
||||
// https://github.com/angular/angular/issues/29939
|
||||
it('should resolve JavaScript sources instead of declaration files if they are adjacent',
|
||||
() => {
|
||||
setupMockFileSystem();
|
||||
const fs = getFileSystem();
|
||||
const esm5bundle = makeEntryPointBundle(
|
||||
fs, '/node_modules/test', './index.js', './index.d.ts', false, 'esm5', 'esm5', true) !;
|
||||
|
||||
expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
// Modules from the entry-point itself should be source files
|
||||
'/node_modules/test/index.js',
|
||||
'/node_modules/test/public_api.js',
|
||||
'/node_modules/test/nested/index.js',
|
||||
'/node_modules/test/root.js',
|
||||
expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
// Modules from the entry-point itself should be source files
|
||||
'/node_modules/test/index.js',
|
||||
'/node_modules/test/public_api.js',
|
||||
'/node_modules/test/nested/index.js',
|
||||
'/node_modules/test/root.js',
|
||||
|
||||
// Modules from a secondary entry-point should be declaration files
|
||||
'/node_modules/test/secondary/public_api.d.ts',
|
||||
'/node_modules/test/secondary/index.d.ts',
|
||||
// Modules from a secondary entry-point should be declaration files
|
||||
'/node_modules/test/secondary/public_api.d.ts',
|
||||
'/node_modules/test/secondary/index.d.ts',
|
||||
|
||||
// Modules resolved from "other" should be declaration files
|
||||
'/node_modules/other/public_api.d.ts',
|
||||
'/node_modules/other/index.d.ts',
|
||||
].map(p => _(p).toString())));
|
||||
// Modules resolved from "other" should be declaration files
|
||||
'/node_modules/other/public_api.d.ts',
|
||||
'/node_modules/other/index.d.ts',
|
||||
].map(p => absoluteFrom(p).toString())));
|
||||
|
||||
expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
// All modules in the dts program should be declaration files
|
||||
'/node_modules/test/index.d.ts',
|
||||
'/node_modules/test/public_api.d.ts',
|
||||
'/node_modules/test/nested/index.d.ts',
|
||||
'/node_modules/test/root.d.ts',
|
||||
'/node_modules/test/secondary/public_api.d.ts',
|
||||
'/node_modules/test/secondary/index.d.ts',
|
||||
'/node_modules/other/public_api.d.ts',
|
||||
'/node_modules/other/index.d.ts',
|
||||
].map(p => _(p).toString())));
|
||||
expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
// All modules in the dts program should be declaration files
|
||||
'/node_modules/test/index.d.ts',
|
||||
'/node_modules/test/public_api.d.ts',
|
||||
'/node_modules/test/nested/index.d.ts',
|
||||
'/node_modules/test/root.d.ts',
|
||||
'/node_modules/test/secondary/public_api.d.ts',
|
||||
'/node_modules/test/secondary/index.d.ts',
|
||||
'/node_modules/other/public_api.d.ts',
|
||||
'/node_modules/other/index.d.ts',
|
||||
].map(p => absoluteFrom(p).toString())));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,200 +5,206 @@
|
||||
* 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 {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
|
||||
import {MockFileSystem, SymLink} from '../helpers/mock_file_system';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
runInEachFileSystem(() => {
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
let resolver: DependencyResolver;
|
||||
let finder: EntryPointFinder;
|
||||
beforeEach(() => {
|
||||
const fs = createMockFileSystem();
|
||||
resolver = new DependencyResolver(
|
||||
fs, new MockLogger(), {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))});
|
||||
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
||||
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
||||
describe('findEntryPoints()', () => {
|
||||
let resolver: DependencyResolver;
|
||||
let finder: EntryPointFinder;
|
||||
let _: typeof absoluteFrom;
|
||||
|
||||
beforeEach(() => {
|
||||
const fs = getFileSystem();
|
||||
_ = absoluteFrom;
|
||||
setupMockFileSystem();
|
||||
resolver = new DependencyResolver(
|
||||
fs, new MockLogger(), {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))});
|
||||
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
||||
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
||||
});
|
||||
finder = new EntryPointFinder(fs, new MockLogger(), resolver);
|
||||
});
|
||||
finder = new EntryPointFinder(fs, new MockLogger(), resolver);
|
||||
});
|
||||
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http/testing')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find packages inside a namespace', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/namespaced'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http/testing')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find entry-points via `pathMappings', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(
|
||||
_('/pathMappings/node_modules'), undefined,
|
||||
{baseUrl: _('/pathMappings'), paths: {'my-lib': ['dist/my-lib']}});
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib')],
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib/sub-lib')],
|
||||
[
|
||||
_('/pathMappings/node_modules/@angular/common'),
|
||||
_('/pathMappings/node_modules/@angular/common')
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no packages', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_packages'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no valid entry-points', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_valid_entry_points'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders starting with .', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/dotted_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders that are symlinked', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/symlinked_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/nested_node_modules'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/nested_node_modules/outer'), _('/nested_node_modules/outer')],
|
||||
// Note that the inner entry point does not get included as part of the outer package
|
||||
[
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
function createMockFileSystem() {
|
||||
return new MockFileSystem({
|
||||
'/sub_entry_points': {
|
||||
'common': {
|
||||
'package.json': createPackageJson('common'),
|
||||
'common.metadata.json': 'metadata info',
|
||||
'http': {
|
||||
'package.json': createPackageJson('http'),
|
||||
'http.metadata.json': 'metadata info',
|
||||
'testing': {
|
||||
'package.json': createPackageJson('testing'),
|
||||
'testing.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
'testing': {
|
||||
'package.json': createPackageJson('testing'),
|
||||
'testing.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
},
|
||||
'/pathMappings': {
|
||||
'dist': {
|
||||
'my-lib': {
|
||||
'package.json': createPackageJson('my-lib'),
|
||||
'my-lib.metadata.json': 'metadata info',
|
||||
'sub-lib': {
|
||||
'package.json': createPackageJson('sub-lib'),
|
||||
'sub-lib.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'common': {
|
||||
'package.json': createPackageJson('common'),
|
||||
'common.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
'/namespaced': {
|
||||
'@angular': {
|
||||
'common': {
|
||||
'package.json': createPackageJson('common'),
|
||||
'common.metadata.json': 'metadata info',
|
||||
'http': {
|
||||
'package.json': createPackageJson('http'),
|
||||
'http.metadata.json': 'metadata info',
|
||||
'testing': {
|
||||
'package.json': createPackageJson('testing'),
|
||||
'testing.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
'testing': {
|
||||
'package.json': createPackageJson('testing'),
|
||||
'testing.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/no_packages': {'should_not_be_found': {}},
|
||||
'/no_valid_entry_points': {
|
||||
'some_package': {
|
||||
'package.json': '{}',
|
||||
},
|
||||
},
|
||||
'/dotted_folders': {
|
||||
'.common': {
|
||||
'package.json': createPackageJson('common'),
|
||||
'common.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
'/symlinked_folders': {
|
||||
'common': new SymLink(_('/sub_entry_points/common')),
|
||||
},
|
||||
'/nested_node_modules': {
|
||||
'outer': {
|
||||
'package.json': createPackageJson('outer'),
|
||||
'outer.metadata.json': 'metadata info',
|
||||
'node_modules': {
|
||||
'inner': {
|
||||
'package.json': createPackageJson('inner'),
|
||||
'inner.metadata.json': 'metadata info',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http/testing')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find packages inside a namespace', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/namespaced'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http/testing')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find entry-points via `pathMappings', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(
|
||||
_('/pathMappings/node_modules'), undefined,
|
||||
{baseUrl: _('/pathMappings'), paths: {'my-lib': ['dist/my-lib']}});
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib')],
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib/sub-lib')],
|
||||
[
|
||||
_('/pathMappings/node_modules/@angular/common'),
|
||||
_('/pathMappings/node_modules/@angular/common')
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no packages', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_packages'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no valid entry-points', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_valid_entry_points'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders starting with .', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/dotted_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders that are symlinked', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/symlinked_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/nested_node_modules'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/nested_node_modules/outer'), _('/nested_node_modules/outer')],
|
||||
// Note that the inner entry point does not get included as part of the outer package
|
||||
[
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
function setupMockFileSystem(): void {
|
||||
loadTestFiles([
|
||||
{name: _('/sub_entry_points/common/package.json'), contents: createPackageJson('common')},
|
||||
{name: _('/sub_entry_points/common/common.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/package.json'),
|
||||
contents: createPackageJson('http')
|
||||
},
|
||||
{name: _('/sub_entry_points/common/http/http.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{name: _('/pathMappings/dist/my-lib/package.json'), contents: createPackageJson('my-lib')},
|
||||
{name: _('/pathMappings/dist/my-lib/my-lib.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/pathMappings/dist/my-lib/sub-lib/package.json'),
|
||||
contents: createPackageJson('sub-lib')
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/dist/my-lib/sub-lib/sub-lib.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/node_modules/@angular/common/package.json'),
|
||||
contents: createPackageJson('common')
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/node_modules/@angular/common/common.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/package.json'),
|
||||
contents: createPackageJson('common')
|
||||
},
|
||||
{name: _('/namespaced/@angular/common/common.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/package.json'),
|
||||
contents: createPackageJson('http')
|
||||
},
|
||||
{name: _('/namespaced/@angular/common/http/http.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{name: _('/no_valid_entry_points/some_package/package.json'), contents: '{}'},
|
||||
{name: _('/dotted_folders/.common/package.json'), contents: createPackageJson('common')},
|
||||
{name: _('/dotted_folders/.common/common.metadata.json'), contents: 'metadata info'},
|
||||
{name: _('/nested_node_modules/outer/package.json'), contents: createPackageJson('outer')},
|
||||
{name: _('/nested_node_modules/outer/outer.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/nested_node_modules/outer/node_modules/inner/package.json'),
|
||||
contents: createPackageJson('inner')
|
||||
},
|
||||
{
|
||||
name: _('/nested_node_modules/outer/node_modules/inner/inner.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
]);
|
||||
const fs = getFileSystem();
|
||||
|
||||
fs.ensureDir(_('/no_packages/should_not_be_found'));
|
||||
|
||||
fs.ensureDir(_('/symlinked_folders'));
|
||||
fs.symlink(_('/sub_entry_points/common'), _('/symlinked_folders/common'));
|
||||
}
|
||||
});
|
||||
|
||||
function createPackageJson(packageName: string): string {
|
||||
const packageJson: any = {
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
esm2015: `./esm2015/${packageName}.js`,
|
||||
fesm5: `./fesm2015/${packageName}.js`,
|
||||
esm5: `./esm2015/${packageName}.js`,
|
||||
main: `./bundles/${packageName}.umd.js`,
|
||||
};
|
||||
return JSON.stringify(packageJson);
|
||||
}
|
||||
});
|
||||
|
||||
function createPackageJson(packageName: string): string {
|
||||
const packageJson: any = {
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
esm2015: `./esm2015/${packageName}.js`,
|
||||
fesm5: `./fesm2015/${packageName}.js`,
|
||||
esm5: `./esm2015/${packageName}.js`,
|
||||
main: `./bundles/${packageName}.umd.js`,
|
||||
};
|
||||
return JSON.stringify(packageJson);
|
||||
}
|
||||
|
@ -6,163 +6,186 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../../src/file_system/file_system';
|
||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {getEntryPointInfo} from '../../src/packages/entry_point';
|
||||
import {MockFileSystem} from '../helpers/mock_file_system';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
runInEachFileSystem(() => {
|
||||
describe('getEntryPointInfo()', () => {
|
||||
let SOME_PACKAGE: AbsoluteFsPath;
|
||||
let _: typeof absoluteFrom;
|
||||
let fs: FileSystem;
|
||||
|
||||
describe('getEntryPointInfo()', () => {
|
||||
const SOME_PACKAGE = _('/some_package');
|
||||
beforeEach(() => {
|
||||
setupMockFileSystem();
|
||||
SOME_PACKAGE = absoluteFrom('/some_package');
|
||||
_ = absoluteFrom;
|
||||
fs = getFileSystem();
|
||||
});
|
||||
|
||||
it('should return an object containing absolute paths to the formats of the specified entry-point',
|
||||
() => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/valid_entry_point'));
|
||||
expect(entryPoint).toEqual({
|
||||
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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/valid_entry_point'),
|
||||
compiledByAngular: true,
|
||||
it('should return an object containing absolute paths to the formats of the specified entry-point',
|
||||
() => {
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/valid_entry_point'));
|
||||
expect(entryPoint).toEqual({
|
||||
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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/valid_entry_point'),
|
||||
compiledByAngular: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if there is no package.json at the entry-point path', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_package_json'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
it('should return null if there is no package.json at the entry-point path', () => {
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_package_json'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null if there is no typings or types field in the package.json', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint =
|
||||
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_typings'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
it('should return null if there is no typings or types field in the package.json', () => {
|
||||
const entryPoint =
|
||||
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_typings'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
|
||||
it('should return an object with `compiledByAngular` set to false if there is no metadata.json file next to the typing file',
|
||||
() => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
|
||||
expect(entryPoint).toEqual({
|
||||
name: 'some-package/missing_metadata',
|
||||
package: SOME_PACKAGE,
|
||||
path: _('/some_package/missing_metadata'),
|
||||
typings: _(`/some_package/missing_metadata/missing_metadata.d.ts`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/missing_metadata'),
|
||||
compiledByAngular: false,
|
||||
it('should return an object with `compiledByAngular` set to false if there is no metadata.json file next to the typing file',
|
||||
() => {
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
|
||||
expect(entryPoint).toEqual({
|
||||
name: 'some-package/missing_metadata',
|
||||
package: SOME_PACKAGE,
|
||||
path: _('/some_package/missing_metadata'),
|
||||
typings: _(`/some_package/missing_metadata/missing_metadata.d.ts`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/missing_metadata'),
|
||||
compiledByAngular: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work if the typings field is named `types', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), 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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/types_rather_than_typings'),
|
||||
compiledByAngular: true,
|
||||
it('should work if the typings field is named `types', () => {
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), 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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/types_rather_than_typings'),
|
||||
compiledByAngular: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with Angular Material style package.json', () => {
|
||||
const entryPoint =
|
||||
getEntryPointInfo(fs, new MockLogger(), 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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/material_style'),
|
||||
compiledByAngular: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if the package.json is not valid JSON', () => {
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with Angular Material style package.json', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint =
|
||||
getEntryPointInfo(fs, new MockLogger(), 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`),
|
||||
packageJson: loadPackageJson(fs, '/some_package/material_style'),
|
||||
compiledByAngular: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if the package.json is not valid JSON', () => {
|
||||
const fs = createMockFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
function createMockFileSystem() {
|
||||
return new MockFileSystem({
|
||||
'/some_package': {
|
||||
'valid_entry_point': {
|
||||
'package.json': createPackageJson('valid_entry_point'),
|
||||
'valid_entry_point.metadata.json': 'some meta data',
|
||||
function setupMockFileSystem(): void {
|
||||
const _ = absoluteFrom;
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/some_package/valid_entry_point/package.json'),
|
||||
contents: createPackageJson('valid_entry_point')
|
||||
},
|
||||
'missing_package_json': {
|
||||
// no package.json!
|
||||
'missing_package_json.metadata.json': 'some meta data',
|
||||
{
|
||||
name: _('/some_package/valid_entry_point/valid_entry_point.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
'missing_typings': {
|
||||
'package.json': createPackageJson('missing_typings', {excludes: ['typings']}),
|
||||
'missing_typings.metadata.json': 'some meta data',
|
||||
// no package.json!
|
||||
{
|
||||
name: _('/some_package/missing_package_json/missing_package_json.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
'types_rather_than_typings': {
|
||||
'package.json': createPackageJson('types_rather_than_typings', {}, 'types'),
|
||||
'types_rather_than_typings.metadata.json': 'some meta data',
|
||||
{
|
||||
name: _('/some_package/missing_typings/package.json'),
|
||||
contents: createPackageJson('missing_typings', {excludes: ['typings']})
|
||||
},
|
||||
'missing_esm2015': {
|
||||
'package.json': createPackageJson('missing_fesm2015', {excludes: ['esm2015', 'fesm2015']}),
|
||||
'missing_esm2015.metadata.json': 'some meta data',
|
||||
{
|
||||
name: _('/some_package/missing_typings/missing_typings.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
'missing_metadata': {
|
||||
'package.json': createPackageJson('missing_metadata'),
|
||||
// no metadata.json!
|
||||
{
|
||||
name: _('/some_package/types_rather_than_typings/package.json'),
|
||||
contents: createPackageJson('types_rather_than_typings', {}, 'types')
|
||||
},
|
||||
'material_style': {
|
||||
'package.json': `{
|
||||
{
|
||||
name: _('/some_package/types_rather_than_typings/types_rather_than_typings.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
{
|
||||
name: _('/some_package/missing_esm2015/package.json'),
|
||||
contents: createPackageJson('missing_fesm2015', {excludes: ['esm2015', 'fesm2015']})
|
||||
},
|
||||
{
|
||||
name: _('/some_package/missing_esm2015/missing_esm2015.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
// no metadata.json!
|
||||
{
|
||||
name: _('/some_package/missing_metadata/package.json'),
|
||||
contents: createPackageJson('missing_metadata')
|
||||
},
|
||||
{
|
||||
name: _('/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"
|
||||
}`,
|
||||
'material_style.metadata.json': 'some meta data',
|
||||
}`
|
||||
},
|
||||
'unexpected_symbols': {
|
||||
// package.json might not be a valid JSON
|
||||
// for example, @schematics/angular contains a package.json blueprint
|
||||
// with unexpected symbols
|
||||
'package.json':
|
||||
'{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}',
|
||||
{
|
||||
name: _('/some_package/material_style/material_style.metadata.json'),
|
||||
contents: 'some meta data'
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createPackageJson(
|
||||
packageName: string, {excludes}: {excludes?: string[]} = {},
|
||||
typingsProp: string = 'typings'): string {
|
||||
const packageJson: any = {
|
||||
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 (excludes) {
|
||||
excludes.forEach(exclude => delete packageJson[exclude]);
|
||||
// package.json might not be a valid JSON
|
||||
// for example, @schematics/angular contains a package.json blueprint
|
||||
// with unexpected symbols
|
||||
{
|
||||
name: _('/some_package/unexpected_symbols/package.json'),
|
||||
contents: '{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}'
|
||||
},
|
||||
]);
|
||||
}
|
||||
return JSON.stringify(packageJson);
|
||||
}
|
||||
|
||||
function createPackageJson(
|
||||
packageName: string, {excludes}: {excludes?: string[]} = {},
|
||||
typingsProp: string = 'typings'): string {
|
||||
const packageJson: any = {
|
||||
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 (excludes) {
|
||||
excludes.forEach(exclude => delete packageJson[exclude]);
|
||||
}
|
||||
return JSON.stringify(packageJson);
|
||||
}
|
||||
});
|
||||
|
||||
export function loadPackageJson(fs: FileSystem, packagePath: string) {
|
||||
return JSON.parse(fs.readFile(_(packagePath + '/package.json')));
|
||||
return JSON.parse(fs.readFile(fs.resolve(packagePath + '/package.json')));
|
||||
}
|
||||
|
Reference in New Issue
Block a user