refactor(ivy): ngcc - add MockFileSystem (#29643)

PR Close #29643
This commit is contained in:
Pete Bacon Darwin
2019-04-28 20:47:57 +01:00
committed by Andrew Kushnir
parent 16d7dde2ad
commit ef861958a9
14 changed files with 408 additions and 304 deletions

View File

@ -9,8 +9,8 @@ import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyResolver, SortedEntryPointsInfo} from '../../src/dependencies/dependency_resolver';
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
import {ModuleResolver} from '../../src/dependencies/module_resolver';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {EntryPoint} from '../../src/packages/entry_point';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
const _ = AbsoluteFsPath.from;
@ -19,7 +19,7 @@ describe('DependencyResolver', () => {
let host: EsmDependencyHost;
let resolver: DependencyResolver;
beforeEach(() => {
const fs = new NodeJSFileSystem();
const fs = new MockFileSystem();
host = new EsmDependencyHost(fs, new ModuleResolver(fs));
resolver = new DependencyResolver(new MockLogger(), host);
});

View File

@ -5,27 +5,23 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as mockFs from 'mock-fs';
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
import {ModuleResolver} from '../../src/dependencies/module_resolver';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {MockFileSystem} from '../helpers/mock_file_system';
const _ = AbsoluteFsPath.from;
describe('DependencyHost', () => {
let host: EsmDependencyHost;
beforeEach(() => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
host = new EsmDependencyHost(fs, new ModuleResolver(fs));
});
describe('getDependencies()', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
it('should not generate a TS AST if the source does not contain any imports or re-exports',
() => {
spyOn(ts, 'createSourceFile');
@ -98,7 +94,7 @@ describe('DependencyHost', () => {
});
it('should support `paths` alias mappings when resolving modules', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
host = new EsmDependencyHost(fs, new ModuleResolver(fs, {
baseUrl: '/dist',
paths: {
@ -115,68 +111,65 @@ describe('DependencyHost', () => {
expect(missing.size).toBe(0);
expect(deepImports.size).toBe(0);
});
function createMockFileSystem() {
mockFs({
'/no/imports/or/re-exports/index.js': '// some text but no import-like statements',
'/no/imports/or/re-exports/package.json': '{"esm2015": "./index.js"}',
'/no/imports/or/re-exports/index.metadata.json': 'MOCK METADATA',
'/external/imports/index.js': `import {X} from 'lib-1';\nimport {Y} from 'lib-1/sub-1';`,
'/external/imports/package.json': '{"esm2015": "./index.js"}',
'/external/imports/index.metadata.json': 'MOCK METADATA',
'/external/re-exports/index.js': `export {X} from 'lib-1';\nexport {Y} from 'lib-1/sub-1';`,
'/external/re-exports/package.json': '{"esm2015": "./index.js"}',
'/external/re-exports/index.metadata.json': 'MOCK METADATA',
'/external/imports-missing/index.js':
`import {X} from 'lib-1';\nimport {Y} from 'missing';`,
'/external/imports-missing/package.json': '{"esm2015": "./index.js"}',
'/external/imports-missing/index.metadata.json': 'MOCK METADATA',
'/external/deep-import/index.js': `import {Y} from 'lib-1/deep/import';`,
'/external/deep-import/package.json': '{"esm2015": "./index.js"}',
'/external/deep-import/index.metadata.json': 'MOCK METADATA',
'/internal/outer/index.js': `import {X} from '../inner';`,
'/internal/outer/package.json': '{"esm2015": "./index.js"}',
'/internal/outer/index.metadata.json': 'MOCK METADATA',
'/internal/inner/index.js': `import {Y} from 'lib-1/sub-1'; export declare class X {}`,
'/internal/circular-a/index.js':
`import {B} from '../circular-b'; import {X} from '../circular-b'; export {Y} from 'lib-1/sub-1';`,
'/internal/circular-b/index.js':
`import {A} from '../circular-a'; import {Y} from '../circular-a'; export {X} from 'lib-1';`,
'/internal/circular-a/package.json': '{"esm2015": "./index.js"}',
'/internal/circular-a/index.metadata.json': 'MOCK METADATA',
'/re-directed/index.js': `import {Z} from 'lib-1/sub-2';`,
'/re-directed/package.json': '{"esm2015": "./index.js"}',
'/re-directed/index.metadata.json': 'MOCK METADATA',
'/path-alias/index.js':
`import {TestHelper} from '@app/components';\nimport {Service} from '@app/shared';\nimport {TestHelper} from '@lib/shared/test';\nimport {X} from 'lib-1';`,
'/path-alias/package.json': '{"esm2015": "./index.js"}',
'/path-alias/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/index.js': 'export declare class X {}',
'/node_modules/lib-1/package.json': '{"esm2015": "./index.js"}',
'/node_modules/lib-1/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/deep/import/index.js': 'export declare class DeepImport {}',
'/node_modules/lib-1/sub-1/index.js': 'export declare class Y {}',
'/node_modules/lib-1/sub-1/package.json': '{"esm2015": "./index.js"}',
'/node_modules/lib-1/sub-1/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/sub-2.js': `export * from './sub-2/sub-2';`,
'/node_modules/lib-1/sub-2/sub-2.js': `export declare class Z {}';`,
'/node_modules/lib-1/sub-2/package.json': '{"esm2015": "./sub-2.js"}',
'/node_modules/lib-1/sub-2/sub-2.metadata.json': 'MOCK METADATA',
'/dist/components/index.js': `class MyComponent {};`,
'/dist/components/package.json': '{"esm2015": "./index.js"}',
'/dist/components/index.metadata.json': 'MOCK METADATA',
'/dist/shared/index.js': `import {X} from 'lib-1';\nexport class Service {}`,
'/dist/shared/package.json': '{"esm2015": "./index.js"}',
'/dist/shared/index.metadata.json': 'MOCK METADATA',
'/dist/lib/shared/test/index.js': `export class TestHelper {}`,
'/dist/lib/shared/test/package.json': '{"esm2015": "./index.js"}',
'/dist/lib/shared/test/index.metadata.json': 'MOCK METADATA',
});
}
function restoreRealFileSystem() { mockFs.restore(); }
});
function createMockFileSystem() {
return new MockFileSystem({
'/no/imports/or/re-exports/index.js': '// some text but no import-like statements',
'/no/imports/or/re-exports/package.json': '{"esm2015": "./index.js"}',
'/no/imports/or/re-exports/index.metadata.json': 'MOCK METADATA',
'/external/imports/index.js': `import {X} from 'lib-1';\nimport {Y} from 'lib-1/sub-1';`,
'/external/imports/package.json': '{"esm2015": "./index.js"}',
'/external/imports/index.metadata.json': 'MOCK METADATA',
'/external/re-exports/index.js': `export {X} from 'lib-1';\nexport {Y} from 'lib-1/sub-1';`,
'/external/re-exports/package.json': '{"esm2015": "./index.js"}',
'/external/re-exports/index.metadata.json': 'MOCK METADATA',
'/external/imports-missing/index.js': `import {X} from 'lib-1';\nimport {Y} from 'missing';`,
'/external/imports-missing/package.json': '{"esm2015": "./index.js"}',
'/external/imports-missing/index.metadata.json': 'MOCK METADATA',
'/external/deep-import/index.js': `import {Y} from 'lib-1/deep/import';`,
'/external/deep-import/package.json': '{"esm2015": "./index.js"}',
'/external/deep-import/index.metadata.json': 'MOCK METADATA',
'/internal/outer/index.js': `import {X} from '../inner';`,
'/internal/outer/package.json': '{"esm2015": "./index.js"}',
'/internal/outer/index.metadata.json': 'MOCK METADATA',
'/internal/inner/index.js': `import {Y} from 'lib-1/sub-1'; export declare class X {}`,
'/internal/circular-a/index.js':
`import {B} from '../circular-b'; import {X} from '../circular-b'; export {Y} from 'lib-1/sub-1';`,
'/internal/circular-b/index.js':
`import {A} from '../circular-a'; import {Y} from '../circular-a'; export {X} from 'lib-1';`,
'/internal/circular-a/package.json': '{"esm2015": "./index.js"}',
'/internal/circular-a/index.metadata.json': 'MOCK METADATA',
'/re-directed/index.js': `import {Z} from 'lib-1/sub-2';`,
'/re-directed/package.json': '{"esm2015": "./index.js"}',
'/re-directed/index.metadata.json': 'MOCK METADATA',
'/path-alias/index.js':
`import {TestHelper} from '@app/components';\nimport {Service} from '@app/shared';\nimport {TestHelper} from '@lib/shared/test';\nimport {X} from 'lib-1';`,
'/path-alias/package.json': '{"esm2015": "./index.js"}',
'/path-alias/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/index.js': 'export declare class X {}',
'/node_modules/lib-1/package.json': '{"esm2015": "./index.js"}',
'/node_modules/lib-1/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/deep/import/index.js': 'export declare class DeepImport {}',
'/node_modules/lib-1/sub-1/index.js': 'export declare class Y {}',
'/node_modules/lib-1/sub-1/package.json': '{"esm2015": "./index.js"}',
'/node_modules/lib-1/sub-1/index.metadata.json': 'MOCK METADATA',
'/node_modules/lib-1/sub-2.js': `export * from './sub-2/sub-2';`,
'/node_modules/lib-1/sub-2/sub-2.js': `export declare class Z {}';`,
'/node_modules/lib-1/sub-2/package.json': '{"esm2015": "./sub-2.js"}',
'/node_modules/lib-1/sub-2/sub-2.metadata.json': 'MOCK METADATA',
'/dist/components/index.js': `class MyComponent {};`,
'/dist/components/package.json': '{"esm2015": "./index.js"}',
'/dist/components/index.metadata.json': 'MOCK METADATA',
'/dist/shared/index.js': `import {X} from 'lib-1';\nexport class Service {}`,
'/dist/shared/package.json': '{"esm2015": "./index.js"}',
'/dist/shared/index.metadata.json': 'MOCK METADATA',
'/dist/lib/shared/test/index.js': `export class TestHelper {}`,
'/dist/lib/shared/test/package.json': '{"esm2015": "./index.js"}',
'/dist/lib/shared/test/index.metadata.json': 'MOCK METADATA',
});
}
describe('isStringImportOrReexport', () => {
it('should return true if the statement is an import', () => {
expect(host.isStringImportOrReexport(createStatement('import {X} from "some/x";')))

View File

@ -5,17 +5,14 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {ModuleResolver, ResolvedDeepImport, ResolvedExternalModule, ResolvedRelativeModule} from '../../src/dependencies/module_resolver';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {MockFileSystem} from '../helpers/mock_file_system';
const _ = AbsoluteFsPath.from;
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/libs': {
'local-package': {
'package.json': 'PACKAGE.JSON for local-package',
@ -68,19 +65,12 @@ function createMockFileSystem() {
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
describe('ModuleResolver', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
describe('resolveModule()', () => {
describe('with relative paths', () => {
it('should resolve sibling, child and aunt modules', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('./x', _('/libs/local-package/index.js')))
.toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js')));
expect(resolver.resolveModuleImport('./sub-folder', _('/libs/local-package/index.js')))
@ -90,16 +80,14 @@ describe('ModuleResolver', () => {
});
it('should return `null` if the resolved module relative module does not exist', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('./y', _('/libs/local-package/index.js'))).toBe(null);
});
});
describe('with non-mapped external paths', () => {
it('should resolve to the package.json of a local node_modules package', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('package-1', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/libs/local-package/node_modules/package-1')));
expect(
@ -110,8 +98,7 @@ describe('ModuleResolver', () => {
});
it('should resolve to the package.json of a higher node_modules package', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('package-2', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/libs/node_modules/package-2')));
expect(resolver.resolveModuleImport('top-package', _('/libs/local-package/index.js')))
@ -119,23 +106,20 @@ describe('ModuleResolver', () => {
});
it('should return `null` if the package cannot be found', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('missing-2', _('/libs/local-package/index.js')))
.toBe(null);
});
it('should return `null` if the package is not accessible because it is in a inner node_modules package',
() => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(resolver.resolveModuleImport('package-3', _('/libs/local-package/index.js')))
.toBe(null);
});
it('should identify deep imports into an external module', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs);
const resolver = new ModuleResolver(createMockFileSystem());
expect(
resolver.resolveModuleImport('package-1/sub-folder', _('/libs/local-package/index.js')))
.toEqual(
@ -145,9 +129,8 @@ describe('ModuleResolver', () => {
describe('with mapped path external modules', () => {
it('should resolve to the package.json of simple mapped packages', () => {
const fs = new NodeJSFileSystem();
const resolver =
new ModuleResolver(fs, {baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
const resolver = new ModuleResolver(
createMockFileSystem(), {baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
expect(resolver.resolveModuleImport('package-4', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/dist/package-4')));
@ -157,8 +140,7 @@ describe('ModuleResolver', () => {
});
it('should select the best match by the length of prefix before the *', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs, {
const resolver = new ModuleResolver(createMockFileSystem(), {
baseUrl: '/dist',
paths: {
'@lib/*': ['*'],
@ -176,7 +158,7 @@ describe('ModuleResolver', () => {
it('should follow the ordering of `paths` when matching mapped packages', () => {
let resolver: ModuleResolver;
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
resolver = new ModuleResolver(fs, {baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
expect(resolver.resolveModuleImport('package-4', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/dist/package-4')));
@ -187,17 +169,15 @@ describe('ModuleResolver', () => {
});
it('should resolve packages when the path mappings have post-fixes', () => {
const fs = new NodeJSFileSystem();
const resolver =
new ModuleResolver(fs, {baseUrl: '/dist', paths: {'*': ['sub-folder/*/post-fix']}});
const resolver = new ModuleResolver(
createMockFileSystem(), {baseUrl: '/dist', paths: {'*': ['sub-folder/*/post-fix']}});
expect(resolver.resolveModuleImport('package-5', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/dist/sub-folder/package-5/post-fix')));
});
it('should match paths against complex path matchers', () => {
const fs = new NodeJSFileSystem();
const resolver =
new ModuleResolver(fs, {baseUrl: '/dist', paths: {'@shared/*': ['sub-folder/*']}});
const resolver = new ModuleResolver(
createMockFileSystem(), {baseUrl: '/dist', paths: {'@shared/*': ['sub-folder/*']}});
expect(resolver.resolveModuleImport('@shared/package-4', _('/libs/local-package/index.js')))
.toEqual(new ResolvedExternalModule(_('/dist/sub-folder/package-4')));
expect(resolver.resolveModuleImport('package-5', _('/libs/local-package/index.js')))
@ -206,17 +186,17 @@ describe('ModuleResolver', () => {
it('should resolve path as "relative" if the mapped path is inside the current package',
() => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(fs, {baseUrl: '/dist', paths: {'@shared/*': ['*']}});
const resolver = new ModuleResolver(
createMockFileSystem(), {baseUrl: '/dist', paths: {'@shared/*': ['*']}});
expect(resolver.resolveModuleImport(
'@shared/package-4/x', _('/dist/package-4/sub-folder/index.js')))
.toEqual(new ResolvedRelativeModule(_('/dist/package-4/x.js')));
});
it('should resolve paths where the wildcard matches more than one path segment', () => {
const fs = new NodeJSFileSystem();
const resolver = new ModuleResolver(
fs, {baseUrl: '/dist', paths: {'@shared/*/post-fix': ['*/post-fix']}});
createMockFileSystem(),
{baseUrl: '/dist', paths: {'@shared/*/post-fix': ['*/post-fix']}});
expect(
resolver.resolveModuleImport(
'@shared/sub-folder/package-5/post-fix', _('/dist/package-4/sub-folder/index.js')))