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

@ -12,14 +12,16 @@ import {Decorator} from '../../../src/ngtsc/reflection';
import {DecoratorHandler, DetectResult} from '../../../src/ngtsc/transform';
import {CompiledClass, DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {Folder, MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
import {makeTestBundleProgram} from '../helpers/utils';
import {createFileSystemFromProgramFiles, makeTestBundleProgram} from '../helpers/utils';
const _ = AbsoluteFsPath.fromUnchecked;
const TEST_PROGRAM = [
{
name: 'test.js',
name: _('/test.js'),
contents: `
import {Component, Directive, Injectable} from '@angular/core';
@ -34,7 +36,7 @@ const TEST_PROGRAM = [
`,
},
{
name: 'other.js',
name: _('/other.js'),
contents: `
import {Component} from '@angular/core';
@ -46,7 +48,7 @@ const TEST_PROGRAM = [
const INTERNAL_COMPONENT_PROGRAM = [
{
name: 'entrypoint.js',
name: _('/entrypoint.js'),
contents: `
import {Component, NgModule} from '@angular/core';
import {ImportedComponent} from './component';
@ -62,7 +64,7 @@ const INTERNAL_COMPONENT_PROGRAM = [
`
},
{
name: 'component.js',
name: _('/component.js'),
contents: `
import {Component} from '@angular/core';
export class ImportedComponent {}
@ -137,7 +139,7 @@ describe('DecorationAnalyzer', () => {
const reflectionHost =
new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
const fs = new NodeJSFileSystem();
const fs = new MockFileSystem(createFileSystemFromProgramFiles(...progArgs));
const analyzer = new DecorationAnalyzer(
fs, program, options, host, program.getTypeChecker(), reflectionHost, referencesRegistry,
[AbsoluteFsPath.fromUnchecked('/')], false);

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')))

View File

@ -0,0 +1,173 @@
/**
* @license
* Copyright Google Inc. 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 {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
import {FileStats, FileSystem} from '../../src/file_system/file_system';
/**
* An in-memory file system that can be used in unit tests.
*/
export class MockFileSystem implements FileSystem {
files: Folder = {};
constructor(...folders: Folder[]) {
folders.forEach(files => this.processFiles(this.files, files));
}
exists(path: AbsoluteFsPath): boolean { return this.findFromPath(path) !== null; }
readFile(path: AbsoluteFsPath): string {
const file = this.findFromPath(path);
if (isFile(file)) {
return file;
} else {
throw new MockFileSystemError('ENOENT', path, `File "${path}" does not exist.`);
}
}
writeFile(path: AbsoluteFsPath, data: string): void {
const [folderPath, basename] = this.splitIntoFolderAndFile(path);
const folder = this.findFromPath(folderPath);
if (!isFolder(folder)) {
throw new MockFileSystemError(
'ENOENT', path, `Unable to write file "${path}". The containing folder does not exist.`);
}
folder[basename] = data;
}
readdir(path: AbsoluteFsPath): PathSegment[] {
const folder = this.findFromPath(path);
if (folder === null) {
throw new MockFileSystemError(
'ENOENT', path, `Unable to read directory "${path}". It does not exist.`);
}
if (isFile(folder)) {
throw new MockFileSystemError(
'ENOTDIR', path, `Unable to read directory "${path}". It is a file.`);
}
return Object.keys(folder) as PathSegment[];
}
lstat(path: AbsoluteFsPath): FileStats {
const fileOrFolder = this.findFromPath(path);
if (fileOrFolder === null) {
throw new MockFileSystemError('ENOENT', path, `File "${path}" does not exist.`);
}
return new MockFileStats(fileOrFolder);
}
stat(path: AbsoluteFsPath): FileStats {
const fileOrFolder = this.findFromPath(path, {followSymLinks: true});
if (fileOrFolder === null) {
throw new MockFileSystemError('ENOENT', path, `File "${path}" does not exist.`);
}
return new MockFileStats(fileOrFolder);
}
pwd(): AbsoluteFsPath { return AbsoluteFsPath.fromUnchecked('/'); }
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
this.writeFile(to, this.readFile(from));
}
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
this.writeFile(to, this.readFile(from));
const folder = this.findFromPath(AbsoluteFsPath.dirname(from)) as Folder;
const basename = PathSegment.basename(from);
delete folder[basename];
}
ensureDir(path: AbsoluteFsPath): void { this.ensureFolders(this.files, path.split('/')); }
private processFiles(current: Folder, files: Folder): void {
Object.keys(files).forEach(path => {
const segments = path.split('/');
const lastSegment = segments.pop() !;
const containingFolder = this.ensureFolders(current, segments);
const entity = files[path];
if (isFolder(entity)) {
const processedFolder = containingFolder[lastSegment] = {} as Folder;
this.processFiles(processedFolder, entity);
} else {
containingFolder[lastSegment] = entity;
}
});
}
private ensureFolders(current: Folder, segments: string[]): Folder {
for (const segment of segments) {
if (isFile(current[segment])) {
throw new Error(`Folder already exists as a file.`);
}
if (!current[segment]) {
current[segment] = {};
}
current = current[segment] as Folder;
}
return current;
}
private findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): Entity|null {
const followSymLinks = !!options && options.followSymLinks;
const segments = path.split('/');
let current = this.files;
while (segments.length) {
const next: Entity = current[segments.shift() !];
if (next === undefined) {
return null;
}
if (segments.length > 0 && (!isFolder(next))) {
return null;
}
if (isFile(next)) {
return next;
}
if (isSymLink(next)) {
return followSymLinks ?
this.findFromPath(AbsoluteFsPath.resolve(next.path, ...segments), {followSymLinks}) :
next;
}
current = next;
}
return current || null;
}
private splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] {
const segments = path.split('/');
const file = segments.pop() !;
return [AbsoluteFsPath.fromUnchecked(segments.join('/')), file];
}
}
export type Entity = Folder | File | SymLink;
export interface Folder { [pathSegments: string]: Entity; }
export type File = string;
export class SymLink {
constructor(public path: AbsoluteFsPath) {}
}
class MockFileStats implements FileStats {
constructor(private entity: Entity) {}
isFile(): boolean { return isFile(this.entity); }
isDirectory(): boolean { return isFolder(this.entity); }
isSymbolicLink(): boolean { return isSymLink(this.entity); }
}
class MockFileSystemError extends Error {
constructor(public code: string, public path: string, message: string) { super(message); }
}
function isFile(item: Entity | null): item is File {
return typeof item === 'string';
}
function isSymLink(item: Entity | null): item is SymLink {
return item instanceof SymLink;
}
function isFolder(item: Entity | null): item is Folder {
return item !== null && !isFile(item) && !isSymLink(item);
}

View File

@ -12,6 +12,7 @@ import {makeProgram} from '../../../src/ngtsc/testing/in_memory_typescript';
import {BundleProgram} from '../../src/packages/bundle_program';
import {EntryPointFormat, EntryPointJsonProperty} from '../../src/packages/entry_point';
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
import {Folder} from './mock_file_system';
export {getDeclaration} from '../../../src/ngtsc/testing/in_memory_typescript';
@ -121,3 +122,11 @@ export function convertToDirectTsLibImport(filesystem: {name: string, contents:
return {...file, contents};
});
}
export function createFileSystemFromProgramFiles(
...fileCollections: ({name: string, contents: string}[] | undefined)[]): Folder {
const folder: Folder = {};
fileCollections.forEach(
files => files && files.forEach(file => folder[file.name] = file.contents));
return folder;
}

View File

@ -5,16 +5,12 @@
* 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 {readFileSync} from 'fs';
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
import {MockFileSystem} from '../helpers/mock_file_system';
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/node_modules/@angular/common': {
'package.json': `{
"fesm2015": "./fesm2015/common.js",
@ -90,38 +86,31 @@ function createMockFileSystem() {
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
describe('Marker files', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
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', () => {
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
const fs = createMockFileSystem();
const fs = new NodeJSFileSystem();
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(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
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(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
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 fs = new NodeJSFileSystem();
let pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
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');

View File

@ -6,15 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DependencyResolver} 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 {EntryPointFinder} from '../../src/packages/entry_point_finder';
import {MockFileSystem, SymLink} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
const _ = AbsoluteFsPath.from;
@ -23,7 +21,7 @@ describe('findEntryPoints()', () => {
let resolver: DependencyResolver;
let finder: EntryPointFinder;
beforeEach(() => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
resolver =
new DependencyResolver(new MockLogger(), new EsmDependencyHost(fs, new ModuleResolver(fs)));
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
@ -31,8 +29,6 @@ describe('findEntryPoints()', () => {
});
finder = new EntryPointFinder(fs, new MockLogger(), resolver);
});
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
it('should find sub-entry-points within a package', () => {
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
@ -90,7 +86,7 @@ describe('findEntryPoints()', () => {
});
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/sub_entry_points': {
'common': {
'package.json': createPackageJson('common'),
@ -142,7 +138,7 @@ describe('findEntryPoints()', () => {
},
},
'/symlinked_folders': {
'common': mockFs.symlink({path: '/sub_entry_points/common'}),
'common': new SymLink(_('/sub_entry_points/common')),
},
'/nested_node_modules': {
'outer': {
@ -158,7 +154,6 @@ describe('findEntryPoints()', () => {
},
});
}
function restoreRealFileSystem() { mockFs.restore(); }
});
function createPackageJson(packageName: string): string {

View File

@ -6,25 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/
import {readFileSync} from 'fs';
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {FileSystem} from '../../src/file_system/file_system';
import {getEntryPointInfo} from '../../src/packages/entry_point';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
const _ = AbsoluteFsPath.fromUnchecked;
describe('getEntryPointInfo()', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
const SOME_PACKAGE = _('/some_package');
it('should return an object containing absolute paths to the formats of the specified entry-point',
() => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint = getEntryPointInfo(
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/valid_entry_point'));
expect(entryPoint).toEqual({
@ -32,20 +27,20 @@ describe('getEntryPointInfo()', () => {
package: SOME_PACKAGE,
path: _('/some_package/valid_entry_point'),
typings: _(`/some_package/valid_entry_point/valid_entry_point.d.ts`),
packageJson: loadPackageJson('/some_package/valid_entry_point'),
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 = new NodeJSFileSystem();
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 typings or types field in the package.json', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint =
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_typings'));
expect(entryPoint).toBe(null);
@ -53,7 +48,7 @@ describe('getEntryPointInfo()', () => {
it('should return an object with `compiledByAngular` set to false if there is no metadata.json file next to the typing file',
() => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint = getEntryPointInfo(
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
expect(entryPoint).toEqual({
@ -61,13 +56,13 @@ describe('getEntryPointInfo()', () => {
package: SOME_PACKAGE,
path: _('/some_package/missing_metadata'),
typings: _(`/some_package/missing_metadata/missing_metadata.d.ts`),
packageJson: loadPackageJson('/some_package/missing_metadata'),
packageJson: loadPackageJson(fs, '/some_package/missing_metadata'),
compiledByAngular: false,
});
});
it('should work if the typings field is named `types', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint = getEntryPointInfo(
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/types_rather_than_typings'));
expect(entryPoint).toEqual({
@ -75,13 +70,13 @@ describe('getEntryPointInfo()', () => {
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('/some_package/types_rather_than_typings'),
packageJson: loadPackageJson(fs, '/some_package/types_rather_than_typings'),
compiledByAngular: true,
});
});
it('should work with Angular Material style package.json', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint =
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/material_style'));
expect(entryPoint).toEqual({
@ -89,13 +84,13 @@ describe('getEntryPointInfo()', () => {
package: SOME_PACKAGE,
path: _('/some_package/material_style'),
typings: _(`/some_package/material_style/material_style.d.ts`),
packageJson: JSON.parse(readFileSync('/some_package/material_style/package.json', 'utf8')),
packageJson: loadPackageJson(fs, '/some_package/material_style'),
compiledByAngular: true,
});
});
it('should return null if the package.json is not valid JSON', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const entryPoint = getEntryPointInfo(
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
expect(entryPoint).toBe(null);
@ -103,7 +98,7 @@ describe('getEntryPointInfo()', () => {
});
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/some_package': {
'valid_entry_point': {
'package.json': createPackageJson('valid_entry_point'),
@ -150,10 +145,6 @@ function createMockFileSystem() {
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
function createPackageJson(
packageName: string, {excludes}: {excludes?: string[]} = {},
typingsProp: string = 'typings'): string {
@ -172,6 +163,6 @@ function createPackageJson(
return JSON.stringify(packageJson);
}
export function loadPackageJson(packagePath: string) {
return JSON.parse(readFileSync(packagePath + '/package.json', 'utf8'));
export function loadPackageJson(fs: FileSystem, packagePath: string) {
return JSON.parse(fs.readFile(_(packagePath + '/package.json')));
}

View File

@ -11,21 +11,21 @@ import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {EsmRenderer} from '../../src/rendering/esm_renderer';
import {makeTestEntryPointBundle} from '../helpers/utils';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
const _ = AbsoluteFsPath.fromUnchecked;
function setup(file: {name: string, contents: string}) {
function setup(file: {name: AbsoluteFsPath, contents: string}) {
const fs = new MockFileSystem();
const logger = new MockLogger();
const bundle = makeTestEntryPointBundle('es2015', 'esm2015', false, [file]) !;
const typeChecker = bundle.src.program.getTypeChecker();
const host = new Esm2015ReflectionHost(logger, false, typeChecker);
const referencesRegistry = new NgccReferencesRegistry(host);
const fs = new NodeJSFileSystem();
const decorationAnalyses = new DecorationAnalyzer(
fs, bundle.src.program, bundle.src.options, bundle.src.host,
typeChecker, host, referencesRegistry, [_('/')], false)
@ -40,7 +40,7 @@ function setup(file: {name: string, contents: string}) {
}
const PROGRAM = {
name: '/some/file.js',
name: _('/some/file.js'),
contents: `
/* A copyright notice */
import 'some-side-effect';
@ -76,7 +76,7 @@ function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
};
const PROGRAM_DECORATE_HELPER = {
name: '/some/file.js',
name: _('/some/file.js'),
contents: `
import * as tslib_1 from "tslib";
var D_1;
@ -142,7 +142,7 @@ import * as i1 from '@angular/common';`);
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
{from: _(PROGRAM.name), dtsFrom: _(PROGRAM.name), identifier: 'TopLevelComponent'},
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
]);
expect(output.toString()).toContain(`
// Some other content
@ -159,7 +159,7 @@ export {TopLevelComponent};`);
{from: _('/some/a.js'), alias: 'eComponentA1', identifier: 'ComponentA1'},
{from: _('/some/a.js'), alias: 'eComponentA2', identifier: 'ComponentA2'},
{from: _('/some/foo/b.js'), alias: 'eComponentB', identifier: 'ComponentB'},
{from: _(PROGRAM.name), alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
]);
const outputString = output.toString();
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);

View File

@ -11,25 +11,26 @@ import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
import {Esm5Renderer} from '../../src/rendering/esm5_renderer';
import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
const _ = AbsoluteFsPath.fromUnchecked;
function setup(file: {name: string, contents: string}) {
function setup(file: {name: AbsoluteFsPath, contents: string}) {
const fs = new MockFileSystem();
const logger = new MockLogger();
const bundle = makeTestEntryPointBundle('module', 'esm5', false, [file]);
const typeChecker = bundle.src.program.getTypeChecker();
const host = new Esm5ReflectionHost(logger, false, typeChecker);
const referencesRegistry = new NgccReferencesRegistry(host);
const fs = new NodeJSFileSystem();
const decorationAnalyses = new DecorationAnalyzer(
fs, bundle.src.program, bundle.src.options, bundle.src.host,
typeChecker, host, referencesRegistry, [_('/')], false)
.analyzeProgram();
const decorationAnalyses =
new DecorationAnalyzer(
fs, bundle.src.program, bundle.src.options, bundle.src.host, typeChecker, host,
referencesRegistry, [AbsoluteFsPath.fromUnchecked('/')], false)
.analyzeProgram();
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
const renderer = new Esm5Renderer(fs, logger, host, false, bundle);
return {
@ -40,7 +41,7 @@ function setup(file: {name: string, contents: string}) {
}
const PROGRAM = {
name: '/some/file.js',
name: _('/some/file.js'),
contents: `
/* A copyright notice */
import 'some-side-effect';
@ -100,7 +101,7 @@ export {A, B, C, NoIife, BadIife};`
};
const PROGRAM_DECORATE_HELPER = {
name: '/some/file.js',
name: _('/some/file.js'),
contents: `
import * as tslib_1 from "tslib";
/* A copyright notice */
@ -179,7 +180,7 @@ import * as i1 from '@angular/common';`);
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
{from: _(PROGRAM.name), dtsFrom: _(PROGRAM.name), identifier: 'TopLevelComponent'},
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
]);
expect(output.toString()).toContain(`
export {A, B, C, NoIife, BadIife};
@ -193,10 +194,10 @@ export {TopLevelComponent};`);
const {renderer} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
{from: _('/some/a.js'), alias: 'eComponentA1', identifier: 'ComponentA1'},
{from: _('/some/a.js'), alias: 'eComponentA2', identifier: 'ComponentA2'},
{from: _('/some/foo/b.js'), alias: 'eComponentB', identifier: 'ComponentB'},
{from: _(PROGRAM.name), alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
{from: _('/some/a.js'), alias: _('eComponentA1'), identifier: 'ComponentA1'},
{from: _('/some/a.js'), alias: _('eComponentA2'), identifier: 'ComponentA2'},
{from: _('/some/foo/b.js'), alias: _('eComponentB'), identifier: 'ComponentB'},
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
]);
const outputString = output.toString();
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
@ -285,14 +286,14 @@ SOME DEFINITION TEXT
const noIifeDeclaration =
getDeclaration(program, sourceFile.fileName, 'NoIife', ts.isFunctionDeclaration);
const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'};
const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: _('NoIife')};
expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT'))
.toThrowError(
'Compiled class declaration is not inside an IIFE: NoIife in /some/file.js');
const badIifeDeclaration =
getDeclaration(program, sourceFile.fileName, 'BadIife', ts.isVariableDeclaration);
const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'};
const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: _('BadIife')};
expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT'))
.toThrowError(
'Compiled class wrapper IIFE does not have a return statement: BadIife in /some/file.js');

View File

@ -5,7 +5,6 @@
* 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 fs from 'fs';
import MagicString from 'magic-string';
import * as ts from 'typescript';
import {fromObject, generateMapFileComment} from 'convert-source-map';
@ -18,11 +17,11 @@ import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {RedundantDecoratorMap, Renderer} from '../../src/rendering/renderer';
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
import {makeTestEntryPointBundle} from '../helpers/utils';
import {makeTestEntryPointBundle, createFileSystemFromProgramFiles} from '../helpers/utils';
import {Logger} from '../../src/logging/logger';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
import {FileSystem} from '../../src/file_system/file_system';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
const _ = AbsoluteFsPath.fromUnchecked;
@ -58,14 +57,15 @@ class TestRenderer extends Renderer {
function createTestRenderer(
packageName: string, files: {name: string, contents: string}[],
dtsFiles?: {name: string, contents: string}[]) {
dtsFiles?: {name: string, contents: string}[],
mappingFiles?: {name: string, contents: string}[]) {
const logger = new MockLogger();
const fs = new MockFileSystem(createFileSystemFromProgramFiles(files, dtsFiles, mappingFiles));
const isCore = packageName === '@angular/core';
const bundle = makeTestEntryPointBundle('es2015', 'esm2015', isCore, files, dtsFiles);
const typeChecker = bundle.src.program.getTypeChecker();
const host = new Esm2015ReflectionHost(logger, isCore, typeChecker, bundle.dts);
const referencesRegistry = new NgccReferencesRegistry(host);
const fs = new NodeJSFileSystem();
const decorationAnalyses = new DecorationAnalyzer(
fs, bundle.src.program, bundle.src.options, bundle.src.host,
typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
@ -200,8 +200,8 @@ describe('Renderer', () => {
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({
name: 'A',
decorators: [jasmine.objectContaining({name: 'Directive'})],
name: _('A'),
decorators: [jasmine.objectContaining({name: _('Directive')})]
}));
expect(addDefinitionsSpy.calls.first().args[2])
.toEqual(
@ -259,15 +259,15 @@ describe('Renderer', () => {
it('should merge any external source map from the original file and write the output to an external source map',
() => {
// Mock out reading the map file from disk
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
const sourceFiles = [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
}];
const mappingFiles =
[{name: INPUT_PROGRAM.name + '.map', contents: INPUT_PROGRAM_MAP.toJSON()}];
const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses} =
createTestRenderer(
'test-package', [{
...INPUT_PROGRAM,
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
}]);
createTestRenderer('test-package', sourceFiles, undefined, mappingFiles);
const result = renderer.renderProgram(
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
moduleWithProvidersAnalyses);
@ -275,7 +275,7 @@ describe('Renderer', () => {
expect(result[0].contents)
.toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('file.js.map'));
expect(result[1].path).toEqual('/src/file.js.map');
expect(result[1].contents).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toJSON());
expect(JSON.parse(result[1].contents)).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toObject());
});
});

View File

@ -5,20 +5,16 @@
* 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 {existsSync, readFileSync} from 'fs';
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {EntryPoint} from '../../src/packages/entry_point';
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
import {InPlaceFileWriter} from '../../src/writing/in_place_file_writer';
import {MockFileSystem} from '../helpers/mock_file_system';
const _ = AbsoluteFsPath.fromUnchecked;
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/package/path': {
'top-level.js': 'ORIGINAL TOP LEVEL',
'folder-1': {
@ -34,16 +30,9 @@ function createMockFileSystem() {
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
describe('InPlaceFileWriter', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
it('should write all the FileInfo to the disk', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const fileWriter = new InPlaceFileWriter(fs);
fileWriter.writeBundle({} as EntryPoint, {} as EntryPointBundle, [
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
@ -51,16 +40,16 @@ describe('InPlaceFileWriter', () => {
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
]);
expect(readFileSync('/package/path/top-level.js', 'utf8')).toEqual('MODIFIED TOP LEVEL');
expect(readFileSync('/package/path/folder-1/file-1.js', 'utf8')).toEqual('MODIFIED FILE 1');
expect(readFileSync('/package/path/folder-1/file-2.js', 'utf8')).toEqual('ORIGINAL FILE 2');
expect(readFileSync('/package/path/folder-2/file-3.js', 'utf8')).toEqual('ORIGINAL FILE 3');
expect(readFileSync('/package/path/folder-2/file-4.js', 'utf8')).toEqual('MODIFIED FILE 4');
expect(readFileSync('/package/path/folder-3/file-5.js', 'utf8')).toEqual('NEW FILE 5');
expect(fs.readFile(_('/package/path/top-level.js'))).toEqual('MODIFIED TOP LEVEL');
expect(fs.readFile(_('/package/path/folder-1/file-1.js'))).toEqual('MODIFIED FILE 1');
expect(fs.readFile(_('/package/path/folder-1/file-2.js'))).toEqual('ORIGINAL FILE 2');
expect(fs.readFile(_('/package/path/folder-2/file-3.js'))).toEqual('ORIGINAL FILE 3');
expect(fs.readFile(_('/package/path/folder-2/file-4.js'))).toEqual('MODIFIED FILE 4');
expect(fs.readFile(_('/package/path/folder-3/file-5.js'))).toEqual('NEW FILE 5');
});
it('should create backups of all files that previously existed', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const fileWriter = new InPlaceFileWriter(fs);
fileWriter.writeBundle({} as EntryPoint, {} as EntryPointBundle, [
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
@ -68,19 +57,19 @@ describe('InPlaceFileWriter', () => {
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
]);
expect(readFileSync('/package/path/top-level.js.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/package/path/top-level.js.__ivy_ngcc_bak')))
.toEqual('ORIGINAL TOP LEVEL');
expect(readFileSync('/package/path/folder-1/file-1.js.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/package/path/folder-1/file-1.js.__ivy_ngcc_bak')))
.toEqual('ORIGINAL FILE 1');
expect(existsSync('/package/path/folder-1/file-2.js.__ivy_ngcc_bak')).toBe(false);
expect(existsSync('/package/path/folder-2/file-3.js.__ivy_ngcc_bak')).toBe(false);
expect(readFileSync('/package/path/folder-2/file-4.js.__ivy_ngcc_bak', 'utf8'))
expect(fs.exists(_('/package/path/folder-1/file-2.js.__ivy_ngcc_bak'))).toBe(false);
expect(fs.exists(_('/package/path/folder-2/file-3.js.__ivy_ngcc_bak'))).toBe(false);
expect(fs.readFile(_('/package/path/folder-2/file-4.js.__ivy_ngcc_bak')))
.toEqual('ORIGINAL FILE 4');
expect(existsSync('/package/path/folder-3/file-5.js.__ivy_ngcc_bak')).toBe(false);
expect(fs.exists(_('/package/path/folder-3/file-5.js.__ivy_ngcc_bak'))).toBe(false);
});
it('should error if the backup file already exists', () => {
const fs = new NodeJSFileSystem();
const fs = createMockFileSystem();
const fileWriter = new InPlaceFileWriter(fs);
expect(
() => fileWriter.writeBundle(

View File

@ -5,24 +5,20 @@
* 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 {existsSync, readFileSync} from 'fs';
import * as mockFs from 'mock-fs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {FileSystem} from '../../src/file_system/file_system';
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo} from '../../src/packages/entry_point';
import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
import {FileWriter} from '../../src/writing/file_writer';
import {NewEntryPointFileWriter} from '../../src/writing/new_entry_point_file_writer';
import {MockFileSystem} from '../helpers/mock_file_system';
import {MockLogger} from '../helpers/mock_logger';
import {loadPackageJson} from '../packages/entry_point_spec';
const _ = AbsoluteFsPath.from;
function createMockFileSystem() {
mockFs({
return new MockFileSystem({
'/node_modules/test': {
'package.json':
'{"module": "./esm5.js", "es2015": "./es2015/index.js", "typings": "./index.d.ts"}',
@ -75,14 +71,7 @@ function createMockFileSystem() {
});
}
function restoreRealFileSystem() {
mockFs.restore();
}
describe('NewEntryPointFileWriter', () => {
beforeEach(createMockFileSystem);
afterEach(restoreRealFileSystem);
let fs: FileSystem;
let fileWriter: FileWriter;
let entryPoint: EntryPoint;
@ -91,7 +80,7 @@ describe('NewEntryPointFileWriter', () => {
describe('writeBundle() [primary entry-point]', () => {
beforeEach(() => {
fs = new NodeJSFileSystem();
fs = createMockFileSystem();
fileWriter = new NewEntryPointFileWriter(fs);
entryPoint = getEntryPointInfo(
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test')) !;
@ -107,14 +96,12 @@ describe('NewEntryPointFileWriter', () => {
},
{path: _('/node_modules/test/esm5.js.map'), contents: 'MODIFIED MAPPING DATA'},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/esm5.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/esm5.js')))
.toEqual('export function FooTop() {} // MODIFIED');
expect(readFileSync('/node_modules/test/esm5.js', 'utf8'))
.toEqual('export function FooTop() {}');
expect(readFileSync('/node_modules/test/__ivy_ngcc__/esm5.js.map', 'utf8'))
expect(fs.readFile(_('/node_modules/test/esm5.js'))).toEqual('export function FooTop() {}');
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/esm5.js.map')))
.toEqual('MODIFIED MAPPING DATA');
expect(readFileSync('/node_modules/test/esm5.js.map', 'utf8'))
.toEqual('ORIGINAL MAPPING DATA');
expect(fs.readFile(_('/node_modules/test/esm5.js.map'))).toEqual('ORIGINAL MAPPING DATA');
});
it('should also copy unmodified files in the program', () => {
@ -124,13 +111,12 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooTop {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/es2015/foo.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/foo.js')))
.toEqual('export class FooTop {} // MODIFIED');
expect(readFileSync('/node_modules/test/es2015/foo.js', 'utf8'))
.toEqual('export class FooTop {}');
expect(readFileSync('/node_modules/test/__ivy_ngcc__/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/es2015/foo.js'))).toEqual('export class FooTop {}');
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/index.js')))
.toEqual('export {FooTop} from "./foo";');
expect(readFileSync('/node_modules/test/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/es2015/index.js')))
.toEqual('export {FooTop} from "./foo";');
});
@ -141,7 +127,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export function FooTop() {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
}));
@ -151,7 +137,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooTop {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
es2015_ivy_ngcc: '__ivy_ngcc__/es2015/index.js',
}));
@ -161,27 +147,26 @@ describe('NewEntryPointFileWriter', () => {
fileWriter.writeBundle(entryPoint, esm2015bundle, [
{
path: _('/node_modules/test/index.d.ts'),
contents: 'export declare class FooTop {} // MODIFIED',
contents: 'export declare class FooTop {} // MODIFIED'
},
{path: _('/node_modules/test/index.d.ts.map'), contents: 'MODIFIED MAPPING DATA'},
]);
expect(readFileSync('/node_modules/test/index.d.ts', 'utf8'))
expect(fs.readFile(_('/node_modules/test/index.d.ts')))
.toEqual('export declare class FooTop {} // MODIFIED');
expect(readFileSync('/node_modules/test/index.d.ts.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/node_modules/test/index.d.ts.__ivy_ngcc_bak')))
.toEqual('export declare class FooTop {}');
expect(existsSync('/node_modules/test/__ivy_ngcc__/index.d.ts')).toBe(false);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/index.d.ts'))).toBe(false);
expect(readFileSync('/node_modules/test/index.d.ts.map', 'utf8'))
.toEqual('MODIFIED MAPPING DATA');
expect(readFileSync('/node_modules/test/index.d.ts.map.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/node_modules/test/index.d.ts.map'))).toEqual('MODIFIED MAPPING DATA');
expect(fs.readFile(_('/node_modules/test/index.d.ts.map.__ivy_ngcc_bak')))
.toEqual('ORIGINAL MAPPING DATA');
expect(existsSync('/node_modules/test/__ivy_ngcc__/index.d.ts.map')).toBe(false);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/index.d.ts.map'))).toBe(false);
});
});
describe('writeBundle() [secondary entry-point]', () => {
beforeEach(() => {
fs = new NodeJSFileSystem();
fs = createMockFileSystem();
fileWriter = new NewEntryPointFileWriter(fs);
entryPoint = getEntryPointInfo(
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/a')) !;
@ -196,10 +181,9 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export function FooA() {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/a/esm5.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/a/esm5.js')))
.toEqual('export function FooA() {} // MODIFIED');
expect(readFileSync('/node_modules/test/a/esm5.js', 'utf8'))
.toEqual('export function FooA() {}');
expect(fs.readFile(_('/node_modules/test/a/esm5.js'))).toEqual('export function FooA() {}');
});
it('should also copy unmodified files in the program', () => {
@ -209,13 +193,12 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooA {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/a/es2015/foo.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/a/es2015/foo.js')))
.toEqual('export class FooA {} // MODIFIED');
expect(readFileSync('/node_modules/test/a/es2015/foo.js', 'utf8'))
.toEqual('export class FooA {}');
expect(readFileSync('/node_modules/test/__ivy_ngcc__/a/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/a/es2015/foo.js'))).toEqual('export class FooA {}');
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/a/es2015/index.js')))
.toEqual('export {FooA} from "./foo";');
expect(readFileSync('/node_modules/test/a/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/a/es2015/index.js')))
.toEqual('export {FooA} from "./foo";');
});
@ -226,7 +209,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export function FooA() {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test/a')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test/a')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '../__ivy_ngcc__/a/esm5.js',
}));
@ -236,7 +219,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooA {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test/a')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test/a')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '../__ivy_ngcc__/a/esm5.js',
es2015_ivy_ngcc: '../__ivy_ngcc__/a/es2015/index.js',
}));
@ -249,17 +232,17 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export declare class FooA {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/a/index.d.ts', 'utf8'))
expect(fs.readFile(_('/node_modules/test/a/index.d.ts')))
.toEqual('export declare class FooA {} // MODIFIED');
expect(readFileSync('/node_modules/test/a/index.d.ts.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/node_modules/test/a/index.d.ts.__ivy_ngcc_bak')))
.toEqual('export declare class FooA {}');
expect(existsSync('/node_modules/test/__ivy_ngcc__/a/index.d.ts')).toBe(false);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/a/index.d.ts'))).toBe(false);
});
});
describe('writeBundle() [entry-point (with files placed outside entry-point folder)]', () => {
beforeEach(() => {
fs = new NodeJSFileSystem();
fs = createMockFileSystem();
fileWriter = new NewEntryPointFileWriter(fs);
entryPoint = getEntryPointInfo(
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !;
@ -274,10 +257,9 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export function FooB() {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/lib/esm5.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/lib/esm5.js')))
.toEqual('export function FooB() {} // MODIFIED');
expect(readFileSync('/node_modules/test/lib/esm5.js', 'utf8'))
.toEqual('export function FooB() {}');
expect(fs.readFile(_('/node_modules/test/lib/esm5.js'))).toEqual('export function FooB() {}');
});
it('should also copy unmodified files in the program', () => {
@ -287,13 +269,13 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooB {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/__ivy_ngcc__/lib/es2015/foo.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/lib/es2015/foo.js')))
.toEqual('export class FooB {} // MODIFIED');
expect(readFileSync('/node_modules/test/lib/es2015/foo.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/lib/es2015/foo.js')))
.toEqual('import {FooA} from "test/a"; import "events"; export class FooB {}');
expect(readFileSync('/node_modules/test/__ivy_ngcc__/lib/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/__ivy_ngcc__/lib/es2015/index.js')))
.toEqual('export {FooB} from "./foo"; import * from "other";');
expect(readFileSync('/node_modules/test/lib/es2015/index.js', 'utf8'))
expect(fs.readFile(_('/node_modules/test/lib/es2015/index.js')))
.toEqual('export {FooB} from "./foo"; import * from "other";');
});
@ -301,11 +283,11 @@ describe('NewEntryPointFileWriter', () => {
() => {
fileWriter.writeBundle(entryPoint, esm2015bundle, [
{
path: '/node_modules/test/lib/es2015/foo.js',
path: _('/node_modules/test/lib/es2015/foo.js'),
contents: 'export class FooB {} // MODIFIED'
},
]);
expect(existsSync('/node_modules/test/__ivy_ngcc__/a/index.d.ts')).toEqual(false);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/a/index.d.ts'))).toEqual(false);
});
it('should not copy files outside of the package', () => {
@ -315,8 +297,8 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooB {} // MODIFIED'
},
]);
expect(existsSync('/node_modules/test/other/index.d.ts')).toEqual(false);
expect(existsSync('/node_modules/test/events/events.js')).toEqual(false);
expect(fs.exists(_('/node_modules/test/other/index.d.ts'))).toEqual(false);
expect(fs.exists(_('/node_modules/test/events/events.js'))).toEqual(false);
});
it('should update the package.json properties', () => {
@ -326,7 +308,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export function FooB() {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test/b')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test/b')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '../__ivy_ngcc__/lib/esm5.js',
}));
@ -336,7 +318,7 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export class FooB {} // MODIFIED'
},
]);
expect(loadPackageJson('/node_modules/test/b')).toEqual(jasmine.objectContaining({
expect(loadPackageJson(fs, '/node_modules/test/b')).toEqual(jasmine.objectContaining({
module_ivy_ngcc: '../__ivy_ngcc__/lib/esm5.js',
es2015_ivy_ngcc: '../__ivy_ngcc__/lib/es2015/index.js',
}));
@ -349,11 +331,11 @@ describe('NewEntryPointFileWriter', () => {
contents: 'export declare class FooB {} // MODIFIED'
},
]);
expect(readFileSync('/node_modules/test/typings/index.d.ts', 'utf8'))
expect(fs.readFile(_('/node_modules/test/typings/index.d.ts')))
.toEqual('export declare class FooB {} // MODIFIED');
expect(readFileSync('/node_modules/test/typings/index.d.ts.__ivy_ngcc_bak', 'utf8'))
expect(fs.readFile(_('/node_modules/test/typings/index.d.ts.__ivy_ngcc_bak')))
.toEqual('export declare class FooB {}');
expect(existsSync('/node_modules/test/__ivy_ngcc__/typings/index.d.ts')).toBe(false);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/typings/index.d.ts'))).toBe(false);
});
});
});