refactor(ivy): ngcc - implement abstract FileSystem (#29643)
This commit introduces a new interface, which abstracts access to the underlying `FileSystem`. There is initially one concrete implementation, `NodeJsFileSystem`, which is simply wrapping the `fs` library of NodeJs. Going forward, we can provide a `MockFileSystem` for test, which should allow us to stop using `mock-fs` for most of the unit tests. We could also implement a `CachedFileSystem` that may improve the performance of ngcc. PR Close #29643
This commit is contained in:
parent
1fd2cc6340
commit
16d7dde2ad
|
@ -6,9 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ConstantPool} from '@angular/compiler';
|
||||
import {NOOP_PERF_RECORDER} from '@angular/compiler-cli/src/ngtsc/perf';
|
||||
import * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
|
||||
|
@ -19,6 +17,7 @@ import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
|||
import {AbsoluteFsPath, LogicalFileSystem} from '../../../src/ngtsc/path';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
|
||||
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {DecoratedClass} from '../host/decorated_class';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
||||
|
@ -53,9 +52,10 @@ export interface MatchingHandler<A, M> {
|
|||
* Simple class that resolves and loads files directly from the filesystem.
|
||||
*/
|
||||
class NgccResourceLoader implements ResourceLoader {
|
||||
constructor(private fs: FileSystem) {}
|
||||
canPreload = false;
|
||||
preload(): undefined|Promise<void> { throw new Error('Not implemented.'); }
|
||||
load(url: string): string { return fs.readFileSync(url, 'utf8'); }
|
||||
load(url: string): string { return this.fs.readFile(AbsoluteFsPath.resolve(url)); }
|
||||
resolve(url: string, containingFile: string): string {
|
||||
return path.resolve(path.dirname(containingFile), url);
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class NgccResourceLoader implements ResourceLoader {
|
|||
* This Analyzer will analyze the files that have decorated classes that need to be transformed.
|
||||
*/
|
||||
export class DecorationAnalyzer {
|
||||
resourceManager = new NgccResourceLoader();
|
||||
resourceManager = new NgccResourceLoader(this.fs);
|
||||
metaRegistry = new LocalMetadataRegistry();
|
||||
dtsMetaReader = new DtsMetadataReader(this.typeChecker, this.reflectionHost);
|
||||
fullMetaReader = new CompoundMetadataReader([this.metaRegistry, this.dtsMetaReader]);
|
||||
|
@ -73,8 +73,8 @@ export class DecorationAnalyzer {
|
|||
new LocalIdentifierStrategy(),
|
||||
new AbsoluteModuleStrategy(this.program, this.typeChecker, this.options, this.host),
|
||||
// TODO(alxhub): there's no reason why ngcc needs the "logical file system" logic here, as ngcc
|
||||
// projects only ever have one rootDir. Instead, ngcc should just switch its emitted imort based
|
||||
// on whether a bestGuessOwningModule is present in the Reference.
|
||||
// projects only ever have one rootDir. Instead, ngcc should just switch its emitted import
|
||||
// based on whether a bestGuessOwningModule is present in the Reference.
|
||||
new LogicalProjectStrategy(this.typeChecker, new LogicalFileSystem(this.rootDirs)),
|
||||
]);
|
||||
dtsModuleScopeResolver =
|
||||
|
@ -110,7 +110,7 @@ export class DecorationAnalyzer {
|
|||
];
|
||||
|
||||
constructor(
|
||||
private program: ts.Program, private options: ts.CompilerOptions,
|
||||
private fs: FileSystem, private program: ts.Program, private options: ts.CompilerOptions,
|
||||
private host: ts.CompilerHost, private typeChecker: ts.TypeChecker,
|
||||
private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry,
|
||||
private rootDirs: AbsoluteFsPath[], private isCore: boolean) {}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {Declaration} from '../../../src/ngtsc/reflection';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {hasNameIdentifier, isDefined} from '../utils';
|
||||
|
@ -14,8 +15,8 @@ import {NgccReferencesRegistry} from './ngcc_references_registry';
|
|||
|
||||
export interface ExportInfo {
|
||||
identifier: string;
|
||||
from: string;
|
||||
dtsFrom?: string|null;
|
||||
from: AbsoluteFsPath;
|
||||
dtsFrom?: AbsoluteFsPath|null;
|
||||
alias?: string|null;
|
||||
}
|
||||
export type PrivateDeclarationsAnalyses = ExportInfo[];
|
||||
|
@ -93,11 +94,12 @@ export class PrivateDeclarationsAnalyzer {
|
|||
});
|
||||
|
||||
return Array.from(privateDeclarations.keys()).map(id => {
|
||||
const from = id.getSourceFile().fileName;
|
||||
const from = AbsoluteFsPath.fromSourceFile(id.getSourceFile());
|
||||
const declaration = privateDeclarations.get(id) !;
|
||||
const alias = exportAliasDeclarations.get(id) || null;
|
||||
const dtsDeclaration = this.host.getDtsDeclaration(declaration.node);
|
||||
const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName;
|
||||
const dtsFrom =
|
||||
dtsDeclaration && AbsoluteFsPath.fromSourceFile(dtsDeclaration.getSourceFile());
|
||||
|
||||
return {identifier: id.text, from, dtsFrom, alias};
|
||||
});
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
* 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 * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {DependencyHost, DependencyInfo} from './dependency_host';
|
||||
import {ModuleResolver, ResolvedDeepImport, ResolvedRelativeModule} from './module_resolver';
|
||||
|
||||
|
@ -19,7 +17,7 @@ import {ModuleResolver, ResolvedDeepImport, ResolvedRelativeModule} from './modu
|
|||
* Helper functions for computing dependencies.
|
||||
*/
|
||||
export class EsmDependencyHost implements DependencyHost {
|
||||
constructor(private moduleResolver: ModuleResolver) {}
|
||||
constructor(private fs: FileSystem, private moduleResolver: ModuleResolver) {}
|
||||
|
||||
/**
|
||||
* Find all the dependencies for the entry-point at the given path.
|
||||
|
@ -54,7 +52,7 @@ export class EsmDependencyHost implements DependencyHost {
|
|||
private recursivelyFindDependencies(
|
||||
file: AbsoluteFsPath, dependencies: Set<AbsoluteFsPath>, missing: Set<string>,
|
||||
deepImports: Set<string>, alreadySeen: Set<AbsoluteFsPath>): void {
|
||||
const fromContents = fs.readFileSync(file, 'utf8');
|
||||
const fromContents = this.fs.readFile(file);
|
||||
if (!this.hasImportOrReexportStatements(fromContents)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {PathMappings, isRelativePath} from '../utils';
|
||||
|
||||
|
||||
/**
|
||||
* This is a very cut-down implementation of the TypeScript module resolution strategy.
|
||||
*
|
||||
|
@ -28,7 +26,9 @@ import {PathMappings, isRelativePath} from '../utils';
|
|||
export class ModuleResolver {
|
||||
private pathMappings: ProcessedPathMapping[];
|
||||
|
||||
constructor(pathMappings?: PathMappings, private relativeExtensions = ['.js', '/index.js']) {
|
||||
constructor(private fs: FileSystem, pathMappings?: PathMappings, private relativeExtensions = [
|
||||
'.js', '/index.js'
|
||||
]) {
|
||||
this.pathMappings = pathMappings ? this.processPathMappings(pathMappings) : [];
|
||||
}
|
||||
|
||||
|
@ -139,11 +139,11 @@ export class ModuleResolver {
|
|||
* to the `path` and checking if the file exists on disk.
|
||||
* @returns An absolute path to the first matching existing file, or `null` if none exist.
|
||||
*/
|
||||
private resolvePath(path: string, postFixes: string[]): AbsoluteFsPath|null {
|
||||
private resolvePath(path: AbsoluteFsPath, postFixes: string[]): AbsoluteFsPath|null {
|
||||
for (const postFix of postFixes) {
|
||||
const testPath = path + postFix;
|
||||
if (fs.existsSync(testPath)) {
|
||||
return AbsoluteFsPath.from(testPath);
|
||||
const testPath = AbsoluteFsPath.fromUnchecked(path + postFix);
|
||||
if (this.fs.exists(testPath)) {
|
||||
return testPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -155,7 +155,7 @@ export class ModuleResolver {
|
|||
* This is achieved by checking for the existence of `${modulePath}/package.json`.
|
||||
*/
|
||||
private isEntryPoint(modulePath: AbsoluteFsPath): boolean {
|
||||
return fs.existsSync(AbsoluteFsPath.join(modulePath, 'package.json'));
|
||||
return this.fs.exists(AbsoluteFsPath.join(modulePath, 'package.json'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,7 +227,7 @@ export class ModuleResolver {
|
|||
let folder = path;
|
||||
while (folder !== '/') {
|
||||
folder = AbsoluteFsPath.dirname(folder);
|
||||
if (fs.existsSync(AbsoluteFsPath.join(folder, 'package.json'))) {
|
||||
if (this.fs.exists(AbsoluteFsPath.join(folder, 'package.json'))) {
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @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';
|
||||
|
||||
/**
|
||||
* A basic interface to abstract the underlying file-system.
|
||||
*
|
||||
* This makes it easier to provide mock file-systems in unit tests,
|
||||
* but also to create clever file-systems that have features such as caching.
|
||||
*/
|
||||
export interface FileSystem {
|
||||
exists(path: AbsoluteFsPath): boolean;
|
||||
readFile(path: AbsoluteFsPath): string;
|
||||
writeFile(path: AbsoluteFsPath, data: string): void;
|
||||
readdir(path: AbsoluteFsPath): PathSegment[];
|
||||
lstat(path: AbsoluteFsPath): FileStats;
|
||||
stat(path: AbsoluteFsPath): FileStats;
|
||||
pwd(): AbsoluteFsPath;
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||
ensureDir(path: AbsoluteFsPath): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about an object in the FileSystem.
|
||||
* This is analogous to the `fs.Stats` class in Node.js.
|
||||
*/
|
||||
export interface FileStats {
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @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 * as fs from 'fs';
|
||||
import {cp, mkdir, mv} from 'shelljs';
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from './file_system';
|
||||
|
||||
/**
|
||||
* A wrapper around the Node.js file-system (i.e the `fs` package).
|
||||
*/
|
||||
export class NodeJSFileSystem implements FileSystem {
|
||||
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); }
|
||||
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); }
|
||||
writeFile(path: AbsoluteFsPath, data: string): void {
|
||||
return fs.writeFileSync(path, data, 'utf8');
|
||||
}
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; }
|
||||
lstat(path: AbsoluteFsPath): fs.Stats { return fs.lstatSync(path); }
|
||||
stat(path: AbsoluteFsPath): fs.Stats { return fs.statSync(path); }
|
||||
pwd() { return AbsoluteFsPath.fromUnchecked(process.cwd()); }
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { cp(from, to); }
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { mv(from, to); }
|
||||
ensureDir(path: AbsoluteFsPath): void { mkdir('-p', path); }
|
||||
}
|
|
@ -5,15 +5,13 @@
|
|||
* 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 {resolve} from 'canonical-path';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../src/ngtsc/path';
|
||||
|
||||
import {DependencyResolver} from './dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from './dependencies/module_resolver';
|
||||
import {FileSystem} from './file_system/file_system';
|
||||
import {NodeJSFileSystem} from './file_system/node_js_file_system';
|
||||
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
||||
import {Logger} from './logging/logger';
|
||||
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
||||
|
@ -26,8 +24,6 @@ import {FileWriter} from './writing/file_writer';
|
|||
import {InPlaceFileWriter} from './writing/in_place_file_writer';
|
||||
import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The options to configure the ngcc compiler.
|
||||
*/
|
||||
|
@ -80,20 +76,20 @@ export function mainNgcc(
|
|||
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
|
||||
compileAllFormats = true, createNewEntryPointFormats = false,
|
||||
logger = new ConsoleLogger(LogLevel.info), pathMappings}: NgccOptions): void {
|
||||
const transformer = new Transformer(logger);
|
||||
const moduleResolver = new ModuleResolver(pathMappings);
|
||||
const host = new EsmDependencyHost(moduleResolver);
|
||||
const fs = new NodeJSFileSystem();
|
||||
const transformer = new Transformer(fs, logger);
|
||||
const moduleResolver = new ModuleResolver(fs, pathMappings);
|
||||
const host = new EsmDependencyHost(fs, moduleResolver);
|
||||
const resolver = new DependencyResolver(logger, host);
|
||||
const finder = new EntryPointFinder(logger, resolver);
|
||||
const fileWriter = getFileWriter(createNewEntryPointFormats);
|
||||
const finder = new EntryPointFinder(fs, logger, resolver);
|
||||
const fileWriter = getFileWriter(fs, createNewEntryPointFormats);
|
||||
|
||||
const absoluteTargetEntryPointPath = targetEntryPointPath ?
|
||||
AbsoluteFsPath.from(resolve(basePath, targetEntryPointPath)) :
|
||||
undefined;
|
||||
const absoluteTargetEntryPointPath =
|
||||
targetEntryPointPath ? AbsoluteFsPath.resolve(basePath, targetEntryPointPath) : undefined;
|
||||
|
||||
if (absoluteTargetEntryPointPath &&
|
||||
hasProcessedTargetEntryPoint(
|
||||
absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
fs, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
logger.info('The target entry-point has already been processed');
|
||||
return;
|
||||
}
|
||||
|
@ -102,7 +98,7 @@ export function mainNgcc(
|
|||
AbsoluteFsPath.from(basePath), absoluteTargetEntryPointPath, pathMappings);
|
||||
|
||||
if (absoluteTargetEntryPointPath && entryPoints.length === 0) {
|
||||
markNonAngularPackageAsProcessed(absoluteTargetEntryPointPath, propertiesToConsider);
|
||||
markNonAngularPackageAsProcessed(fs, absoluteTargetEntryPointPath, propertiesToConsider);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -138,8 +134,8 @@ export function mainNgcc(
|
|||
// the property as processed even if its underlying format has been built already.
|
||||
if (!compiledFormats.has(formatPath) && (compileAllFormats || isFirstFormat)) {
|
||||
const bundle = makeEntryPointBundle(
|
||||
entryPoint.path, formatPath, entryPoint.typings, isCore, property, format, processDts,
|
||||
pathMappings);
|
||||
fs, entryPoint.path, formatPath, entryPoint.typings, isCore, property, format,
|
||||
processDts, pathMappings);
|
||||
if (bundle) {
|
||||
logger.info(`Compiling ${entryPoint.name} : ${property} as ${format}`);
|
||||
const transformedFiles = transformer.transform(bundle);
|
||||
|
@ -156,9 +152,9 @@ export function mainNgcc(
|
|||
// Either this format was just compiled or its underlying format was compiled because of a
|
||||
// previous property.
|
||||
if (compiledFormats.has(formatPath)) {
|
||||
markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, property);
|
||||
markAsProcessed(fs, entryPointPackageJson, entryPointPackageJsonPath, property);
|
||||
if (processDts) {
|
||||
markAsProcessed(entryPointPackageJson, entryPointPackageJsonPath, 'typings');
|
||||
markAsProcessed(fs, entryPointPackageJson, entryPointPackageJsonPath, 'typings');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,14 +166,15 @@ export function mainNgcc(
|
|||
});
|
||||
}
|
||||
|
||||
function getFileWriter(createNewEntryPointFormats: boolean): FileWriter {
|
||||
return createNewEntryPointFormats ? new NewEntryPointFileWriter() : new InPlaceFileWriter();
|
||||
function getFileWriter(fs: FileSystem, createNewEntryPointFormats: boolean): FileWriter {
|
||||
return createNewEntryPointFormats ? new NewEntryPointFileWriter(fs) : new InPlaceFileWriter(fs);
|
||||
}
|
||||
|
||||
function hasProcessedTargetEntryPoint(
|
||||
targetPath: AbsoluteFsPath, propertiesToConsider: string[], compileAllFormats: boolean) {
|
||||
const packageJsonPath = AbsoluteFsPath.from(resolve(targetPath, 'package.json'));
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
||||
compileAllFormats: boolean) {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(targetPath, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
||||
|
||||
for (const property of propertiesToConsider) {
|
||||
if (packageJson[property]) {
|
||||
|
@ -205,11 +202,12 @@ function hasProcessedTargetEntryPoint(
|
|||
* So mark all formats in this entry-point as processed so that clients of ngcc can avoid
|
||||
* triggering ngcc for this entry-point in the future.
|
||||
*/
|
||||
function markNonAngularPackageAsProcessed(path: AbsoluteFsPath, propertiesToConsider: string[]) {
|
||||
const packageJsonPath = AbsoluteFsPath.from(resolve(path, 'package.json'));
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
function markNonAngularPackageAsProcessed(
|
||||
fs: FileSystem, path: AbsoluteFsPath, propertiesToConsider: string[]) {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(path, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
||||
propertiesToConsider.forEach(formatProperty => {
|
||||
if (packageJson[formatProperty])
|
||||
markAsProcessed(packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
|
||||
markAsProcessed(fs, packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {writeFileSync} from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
|
||||
|
||||
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
|
||||
|
@ -49,9 +47,9 @@ export function hasBeenProcessed(
|
|||
* @param format the property in the package.json of the format for which we are writing the marker.
|
||||
*/
|
||||
export function markAsProcessed(
|
||||
packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
|
||||
fs: FileSystem, packageJson: EntryPointPackageJson, packageJsonPath: AbsoluteFsPath,
|
||||
format: EntryPointJsonProperty) {
|
||||
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
|
||||
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
|
||||
writeFileSync(packageJsonPath, JSON.stringify(packageJson), 'utf8');
|
||||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 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 {dirname, resolve} from 'canonical-path';
|
||||
import {existsSync, lstatSync, readdirSync} from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
|
||||
/**
|
||||
* An entry point bundle contains one or two programs, e.g. `src` and `dts`,
|
||||
* that are compiled via TypeScript.
|
||||
|
@ -21,9 +22,9 @@ export interface BundleProgram {
|
|||
program: ts.Program;
|
||||
options: ts.CompilerOptions;
|
||||
host: ts.CompilerHost;
|
||||
path: string;
|
||||
path: AbsoluteFsPath;
|
||||
file: ts.SourceFile;
|
||||
r3SymbolsPath: string|null;
|
||||
r3SymbolsPath: AbsoluteFsPath|null;
|
||||
r3SymbolsFile: ts.SourceFile|null;
|
||||
}
|
||||
|
||||
|
@ -31,9 +32,10 @@ export interface BundleProgram {
|
|||
* Create a bundle program.
|
||||
*/
|
||||
export function makeBundleProgram(
|
||||
isCore: boolean, path: string, r3FileName: string, options: ts.CompilerOptions,
|
||||
host: ts.CompilerHost): BundleProgram {
|
||||
const r3SymbolsPath = isCore ? findR3SymbolsPath(dirname(path), r3FileName) : null;
|
||||
fs: FileSystem, isCore: boolean, path: AbsoluteFsPath, r3FileName: string,
|
||||
options: ts.CompilerOptions, host: ts.CompilerHost): BundleProgram {
|
||||
const r3SymbolsPath =
|
||||
isCore ? findR3SymbolsPath(fs, AbsoluteFsPath.dirname(path), r3FileName) : null;
|
||||
const rootPaths = r3SymbolsPath ? [path, r3SymbolsPath] : [path];
|
||||
const program = ts.createProgram(rootPaths, options, host);
|
||||
const file = program.getSourceFile(path) !;
|
||||
|
@ -45,26 +47,28 @@ export function makeBundleProgram(
|
|||
/**
|
||||
* Search the given directory hierarchy to find the path to the `r3_symbols` file.
|
||||
*/
|
||||
export function findR3SymbolsPath(directory: string, filename: string): string|null {
|
||||
const r3SymbolsFilePath = resolve(directory, filename);
|
||||
if (existsSync(r3SymbolsFilePath)) {
|
||||
export function findR3SymbolsPath(
|
||||
fs: FileSystem, directory: AbsoluteFsPath, filename: string): AbsoluteFsPath|null {
|
||||
const r3SymbolsFilePath = AbsoluteFsPath.resolve(directory, filename);
|
||||
if (fs.exists(r3SymbolsFilePath)) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
|
||||
const subDirectories =
|
||||
readdirSync(directory)
|
||||
fs.readdir(directory)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = lstatSync(resolve(directory, p));
|
||||
const stat = fs.lstat(AbsoluteFsPath.resolve(directory, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
});
|
||||
|
||||
for (const subDirectory of subDirectories) {
|
||||
const r3SymbolsFilePath = findR3SymbolsPath(resolve(directory, subDirectory, ), filename);
|
||||
const r3SymbolsFilePath =
|
||||
findR3SymbolsPath(fs, AbsoluteFsPath.resolve(directory, subDirectory), filename);
|
||||
if (r3SymbolsFilePath) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
|
|
|
@ -5,14 +5,10 @@
|
|||
* 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 path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
|
||||
|
||||
/**
|
||||
* The possible values for the format of an entry-point.
|
||||
*/
|
||||
|
@ -70,13 +66,14 @@ export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
|||
* @returns An entry-point if it is valid, `null` otherwise.
|
||||
*/
|
||||
export function getEntryPointInfo(
|
||||
logger: Logger, packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packageJsonPath = path.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
fs: FileSystem, logger: Logger, packagePath: AbsoluteFsPath,
|
||||
entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packageJsonPath = AbsoluteFsPath.resolve(entryPointPath, 'package.json');
|
||||
if (!fs.exists(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryPointPackageJson = loadEntryPointPackage(logger, packageJsonPath);
|
||||
const entryPointPackageJson = loadEntryPointPackage(fs, logger, packageJsonPath);
|
||||
if (!entryPointPackageJson) {
|
||||
return null;
|
||||
}
|
||||
|
@ -90,15 +87,15 @@ export function getEntryPointInfo(
|
|||
|
||||
// Also there must exist a `metadata.json` file next to the typings entry-point.
|
||||
const metadataPath =
|
||||
path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
||||
AbsoluteFsPath.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
||||
|
||||
const entryPointInfo: EntryPoint = {
|
||||
name: entryPointPackageJson.name,
|
||||
packageJson: entryPointPackageJson,
|
||||
package: packagePath,
|
||||
path: entryPointPath,
|
||||
typings: AbsoluteFsPath.from(path.resolve(entryPointPath, typings)),
|
||||
compiledByAngular: fs.existsSync(metadataPath),
|
||||
typings: AbsoluteFsPath.resolve(entryPointPath, typings),
|
||||
compiledByAngular: fs.exists(metadataPath),
|
||||
};
|
||||
|
||||
return entryPointInfo;
|
||||
|
@ -136,10 +133,10 @@ export function getEntryPointFormat(property: string): EntryPointFormat|undefine
|
|||
* @param packageJsonPath the absolute path to the package.json file.
|
||||
* @returns JSON from the package.json file if it is valid, `null` otherwise.
|
||||
*/
|
||||
function loadEntryPointPackage(logger: Logger, packageJsonPath: string): EntryPointPackageJson|
|
||||
null {
|
||||
function loadEntryPointPackage(
|
||||
fs: FileSystem, logger: Logger, packageJsonPath: AbsoluteFsPath): EntryPointPackageJson|null {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
return JSON.parse(fs.readFile(packageJsonPath));
|
||||
} catch (e) {
|
||||
// We may have run into a package.json with unexpected symbols
|
||||
logger.warn(`Failed to read entry point info from ${packageJsonPath} with error ${e}.`);
|
||||
|
|
|
@ -5,14 +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 {resolve} from 'canonical-path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {PathMappings} from '../utils';
|
||||
|
||||
import {BundleProgram, makeBundleProgram} from './bundle_program';
|
||||
import {EntryPointFormat, EntryPointJsonProperty} from './entry_point';
|
||||
import {NgccCompilerHost} from './ngcc_compiler_host';
|
||||
|
||||
/**
|
||||
* A bundle of files and paths (and TS programs) that correspond to a particular
|
||||
|
@ -38,25 +38,27 @@ export interface EntryPointBundle {
|
|||
* @param transformDts Whether to transform the typings along with this bundle.
|
||||
*/
|
||||
export function makeEntryPointBundle(
|
||||
entryPointPath: string, formatPath: string, typingsPath: string, isCore: boolean,
|
||||
formatProperty: EntryPointJsonProperty, format: EntryPointFormat, transformDts: boolean,
|
||||
pathMappings?: PathMappings): EntryPointBundle|null {
|
||||
fs: FileSystem, entryPointPath: string, formatPath: string, typingsPath: string,
|
||||
isCore: boolean, formatProperty: EntryPointJsonProperty, format: EntryPointFormat,
|
||||
transformDts: boolean, pathMappings?: PathMappings): EntryPointBundle|null {
|
||||
// Create the TS program and necessary helpers.
|
||||
const options: ts.CompilerOptions = {
|
||||
allowJs: true,
|
||||
maxNodeModuleJsDepth: Infinity,
|
||||
noLib: true,
|
||||
rootDir: entryPointPath, ...pathMappings
|
||||
};
|
||||
const host = ts.createCompilerHost(options);
|
||||
const host = new NgccCompilerHost(fs, options);
|
||||
const rootDirs = [AbsoluteFsPath.from(entryPointPath)];
|
||||
|
||||
// Create the bundle programs, as necessary.
|
||||
const src = makeBundleProgram(
|
||||
isCore, resolve(entryPointPath, formatPath), 'r3_symbols.js', options, host);
|
||||
const dts = transformDts ?
|
||||
makeBundleProgram(
|
||||
isCore, resolve(entryPointPath, typingsPath), 'r3_symbols.d.ts', options, host) :
|
||||
null;
|
||||
fs, isCore, AbsoluteFsPath.resolve(entryPointPath, formatPath), 'r3_symbols.js', options,
|
||||
host);
|
||||
const dts = transformDts ? makeBundleProgram(
|
||||
fs, isCore, AbsoluteFsPath.resolve(entryPointPath, typingsPath),
|
||||
'r3_symbols.d.ts', options, host) :
|
||||
null;
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
|
||||
return {format, formatProperty, rootDirs, isCore, isFlatCore, src, dts};
|
||||
|
|
|
@ -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 * as path from 'canonical-path';
|
||||
import * as fs from 'fs';
|
||||
import {join, resolve} from 'path';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {PathMappings} from '../utils';
|
||||
|
||||
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
||||
|
||||
|
||||
export class EntryPointFinder {
|
||||
constructor(private logger: Logger, private resolver: DependencyResolver) {}
|
||||
constructor(
|
||||
private fs: FileSystem, private logger: Logger, private resolver: DependencyResolver) {}
|
||||
/**
|
||||
* Search the given directory, and sub-directories, for Angular package entry points.
|
||||
* @param sourceDirectory An absolute path to the directory to search for entry points.
|
||||
|
@ -59,9 +55,9 @@ export class EntryPointFinder {
|
|||
AbsoluteFsPath[] {
|
||||
const basePaths = [sourceDirectory];
|
||||
if (pathMappings) {
|
||||
const baseUrl = AbsoluteFsPath.from(resolve(pathMappings.baseUrl));
|
||||
const baseUrl = AbsoluteFsPath.resolve(pathMappings.baseUrl);
|
||||
values(pathMappings.paths).forEach(paths => paths.forEach(path => {
|
||||
basePaths.push(AbsoluteFsPath.fromUnchecked(join(baseUrl, extractPathPrefix(path))));
|
||||
basePaths.push(AbsoluteFsPath.join(baseUrl, extractPathPrefix(path)));
|
||||
}));
|
||||
}
|
||||
basePaths.sort(); // Get the paths in order with the shorter ones first.
|
||||
|
@ -75,29 +71,29 @@ export class EntryPointFinder {
|
|||
*/
|
||||
private walkDirectoryForEntryPoints(sourceDirectory: AbsoluteFsPath): EntryPoint[] {
|
||||
const entryPoints: EntryPoint[] = [];
|
||||
fs.readdirSync(sourceDirectory)
|
||||
this.fs
|
||||
.readdir(sourceDirectory)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = fs.lstatSync(path.resolve(sourceDirectory, p));
|
||||
const stat = this.fs.lstat(AbsoluteFsPath.resolve(sourceDirectory, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
})
|
||||
.forEach(p => {
|
||||
// Either the directory is a potential package or a namespace containing packages (e.g
|
||||
// `@angular`).
|
||||
const packagePath = AbsoluteFsPath.from(path.join(sourceDirectory, p));
|
||||
const packagePath = AbsoluteFsPath.join(sourceDirectory, p);
|
||||
if (p.startsWith('@')) {
|
||||
entryPoints.push(...this.walkDirectoryForEntryPoints(packagePath));
|
||||
} else {
|
||||
entryPoints.push(...this.getEntryPointsForPackage(packagePath));
|
||||
|
||||
// Also check for any nested node_modules in this package
|
||||
const nestedNodeModulesPath =
|
||||
AbsoluteFsPath.from(path.resolve(packagePath, 'node_modules'));
|
||||
if (fs.existsSync(nestedNodeModulesPath)) {
|
||||
const nestedNodeModulesPath = AbsoluteFsPath.resolve(packagePath, 'node_modules');
|
||||
if (this.fs.exists(nestedNodeModulesPath)) {
|
||||
entryPoints.push(...this.walkDirectoryForEntryPoints(nestedNodeModulesPath));
|
||||
}
|
||||
}
|
||||
|
@ -114,14 +110,14 @@ export class EntryPointFinder {
|
|||
const entryPoints: EntryPoint[] = [];
|
||||
|
||||
// Try to get an entry point from the top level package directory
|
||||
const topLevelEntryPoint = getEntryPointInfo(this.logger, packagePath, packagePath);
|
||||
const topLevelEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, packagePath);
|
||||
if (topLevelEntryPoint !== null) {
|
||||
entryPoints.push(topLevelEntryPoint);
|
||||
}
|
||||
|
||||
// Now search all the directories of this package for possible entry points
|
||||
this.walkDirectory(packagePath, subdir => {
|
||||
const subEntryPoint = getEntryPointInfo(this.logger, packagePath, subdir);
|
||||
const subEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, subdir);
|
||||
if (subEntryPoint !== null) {
|
||||
entryPoints.push(subEntryPoint);
|
||||
}
|
||||
|
@ -137,19 +133,19 @@ export class EntryPointFinder {
|
|||
* @param fn the function to apply to each directory.
|
||||
*/
|
||||
private walkDirectory(dir: AbsoluteFsPath, fn: (dir: AbsoluteFsPath) => void) {
|
||||
return fs
|
||||
.readdirSync(dir)
|
||||
return this.fs
|
||||
.readdir(dir)
|
||||
// Not interested in hidden files
|
||||
.filter(p => !p.startsWith('.'))
|
||||
// Ignore node_modules
|
||||
.filter(p => p !== 'node_modules')
|
||||
// Only interested in directories (and only those that are not symlinks)
|
||||
.filter(p => {
|
||||
const stat = fs.lstatSync(path.resolve(dir, p));
|
||||
const stat = this.fs.lstat(AbsoluteFsPath.resolve(dir, p));
|
||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
||||
})
|
||||
.forEach(subDir => {
|
||||
const resolvedSubDir = AbsoluteFsPath.from(path.resolve(dir, subDir));
|
||||
const resolvedSubDir = AbsoluteFsPath.resolve(dir, subDir);
|
||||
fn(resolvedSubDir);
|
||||
this.walkDirectory(resolvedSubDir, fn);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @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 * as os from 'os';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
|
||||
export class NgccCompilerHost implements ts.CompilerHost {
|
||||
private _caseSensitive = this.fs.exists(AbsoluteFsPath.fromUnchecked(__filename.toUpperCase()));
|
||||
|
||||
constructor(private fs: FileSystem, private options: ts.CompilerOptions) {}
|
||||
|
||||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined {
|
||||
const text = this.readFile(fileName);
|
||||
return text !== undefined ? ts.createSourceFile(fileName, text, languageVersion) : undefined;
|
||||
}
|
||||
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return this.getDefaultLibLocation() + '/' + ts.getDefaultLibFileName(options);
|
||||
}
|
||||
|
||||
getDefaultLibLocation(): string {
|
||||
const nodeLibPath = AbsoluteFsPath.fromUnchecked(require.resolve('typescript'));
|
||||
return AbsoluteFsPath.join(nodeLibPath, '..');
|
||||
}
|
||||
|
||||
writeFile(fileName: string, data: string): void {
|
||||
this.fs.writeFile(AbsoluteFsPath.fromUnchecked(fileName), data);
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string { return this.fs.pwd(); }
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean { return this._caseSensitive; }
|
||||
|
||||
getNewLine(): string {
|
||||
switch (this.options.newLine) {
|
||||
case ts.NewLineKind.CarriageReturnLineFeed:
|
||||
return '\r\n';
|
||||
case ts.NewLineKind.LineFeed:
|
||||
return '\n';
|
||||
default:
|
||||
return os.EOL;
|
||||
}
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.fs.exists(AbsoluteFsPath.fromUnchecked(fileName));
|
||||
}
|
||||
|
||||
readFile(fileName: string): string|undefined {
|
||||
if (!this.fileExists(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
return this.fs.readFile(AbsoluteFsPath.fromUnchecked(fileName));
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../analy
|
|||
import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry';
|
||||
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {Esm2015ReflectionHost} from '../host/esm2015_host';
|
||||
import {Esm5ReflectionHost} from '../host/esm5_host';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
|
@ -46,7 +47,7 @@ import {EntryPointBundle} from './entry_point_bundle';
|
|||
* - Some formats may contain multiple "modules" in a single file.
|
||||
*/
|
||||
export class Transformer {
|
||||
constructor(private logger: Logger) {}
|
||||
constructor(private fs: FileSystem, private logger: Logger) {}
|
||||
|
||||
/**
|
||||
* Transform the source (and typings) files of a bundle.
|
||||
|
@ -85,9 +86,9 @@ export class Transformer {
|
|||
getRenderer(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle): Renderer {
|
||||
switch (bundle.format) {
|
||||
case 'esm2015':
|
||||
return new EsmRenderer(this.logger, host, isCore, bundle);
|
||||
return new EsmRenderer(this.fs, this.logger, host, isCore, bundle);
|
||||
case 'esm5':
|
||||
return new Esm5Renderer(this.logger, host, isCore, bundle);
|
||||
return new Esm5Renderer(this.fs, this.logger, host, isCore, bundle);
|
||||
default:
|
||||
throw new Error(`Renderer for "${bundle.format}" not yet implemented.`);
|
||||
}
|
||||
|
@ -102,8 +103,8 @@ export class Transformer {
|
|||
const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
const decorationAnalyzer = new DecorationAnalyzer(
|
||||
bundle.src.program, bundle.src.options, bundle.src.host, typeChecker, reflectionHost,
|
||||
referencesRegistry, bundle.rootDirs, isCore);
|
||||
this.fs, bundle.src.program, bundle.src.options, bundle.src.host, typeChecker,
|
||||
reflectionHost, referencesRegistry, bundle.rootDirs, isCore);
|
||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram();
|
||||
|
||||
const moduleWithProvidersAnalyzer =
|
||||
|
|
|
@ -5,18 +5,21 @@
|
|||
* 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 ts from 'typescript';
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {getIifeBody} from '../host/esm5_host';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {EsmRenderer} from './esm_renderer';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {EsmRenderer} from './esm_renderer';
|
||||
|
||||
export class Esm5Renderer extends EsmRenderer {
|
||||
constructor(logger: Logger, host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle) {
|
||||
super(logger, host, isCore, bundle);
|
||||
constructor(
|
||||
fs: FileSystem, logger: Logger, host: NgccReflectionHost, isCore: boolean,
|
||||
bundle: EntryPointBundle) {
|
||||
super(fs, logger, host, isCore, bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,20 +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 {dirname, relative} from 'canonical-path';
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {RedundantDecoratorMap, Renderer, stripExtension} from './renderer';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {PathSegment, AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {RedundantDecoratorMap, Renderer, stripExtension} from './renderer';
|
||||
|
||||
export class EsmRenderer extends Renderer {
|
||||
constructor(logger: Logger, host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle) {
|
||||
super(logger, host, isCore, bundle);
|
||||
constructor(
|
||||
fs: FileSystem, logger: Logger, host: NgccReflectionHost, isCore: boolean,
|
||||
bundle: EntryPointBundle) {
|
||||
super(fs, logger, host, isCore, bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +36,7 @@ export class EsmRenderer extends Renderer {
|
|||
output.appendLeft(insertionPoint, renderedImports);
|
||||
}
|
||||
|
||||
addExports(output: MagicString, entryPointBasePath: string, exports: ExportInfo[]): void {
|
||||
addExports(output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[]): void {
|
||||
exports.forEach(e => {
|
||||
let exportFrom = '';
|
||||
const isDtsFile = isDtsPath(entryPointBasePath);
|
||||
|
@ -41,7 +44,8 @@ export class EsmRenderer extends Renderer {
|
|||
|
||||
if (from) {
|
||||
const basePath = stripExtension(from);
|
||||
const relativePath = './' + relative(dirname(entryPointBasePath), basePath);
|
||||
const relativePath =
|
||||
'./' + PathSegment.relative(AbsoluteFsPath.dirname(entryPointBasePath), basePath);
|
||||
exportFrom = entryPointBasePath !== basePath ? ` from '${relativePath}'` : '';
|
||||
}
|
||||
|
||||
|
|
|
@ -6,25 +6,26 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
||||
import {SourceMapConverter, commentRegex, fromJSON, fromMapFileSource, fromObject, fromSource, generateMapFileComment, mapFileCommentRegex, removeComments, removeMapFileComments} from 'convert-source-map';
|
||||
import {readFileSync, statSync} from 'fs';
|
||||
import {SourceMapConverter, commentRegex, fromJSON, fromObject, fromSource, generateMapFileComment, mapFileCommentRegex, removeComments, removeMapFileComments} from 'convert-source-map';
|
||||
import MagicString from 'magic-string';
|
||||
import {basename, dirname, relative, resolve} from 'canonical-path';
|
||||
import {SourceMapConsumer, SourceMapGenerator, RawSourceMap} from 'source-map';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NoopImportRewriter, ImportRewriter, R3SymbolsImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
import {NoopImportRewriter, ImportRewriter, R3SymbolsImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER} from '../../../src/ngtsc/imports';
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {CompileResult} from '../../../src/ngtsc/transform';
|
||||
import {translateStatement, translateType, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
|
||||
|
||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
|
||||
|
||||
interface SourceMapInfo {
|
||||
source: string;
|
||||
|
@ -39,7 +40,7 @@ export interface FileInfo {
|
|||
/**
|
||||
* Path to where the file should be written.
|
||||
*/
|
||||
path: string;
|
||||
path: AbsoluteFsPath;
|
||||
/**
|
||||
* The contents of the file to be be written.
|
||||
*/
|
||||
|
@ -81,8 +82,8 @@ export const RedundantDecoratorMap = Map;
|
|||
*/
|
||||
export abstract class Renderer {
|
||||
constructor(
|
||||
protected logger: Logger, protected host: NgccReflectionHost, protected isCore: boolean,
|
||||
protected bundle: EntryPointBundle) {}
|
||||
protected fs: FileSystem, protected logger: Logger, protected host: NgccReflectionHost,
|
||||
protected isCore: boolean, protected bundle: EntryPointBundle) {}
|
||||
|
||||
renderProgram(
|
||||
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
||||
|
@ -189,7 +190,7 @@ export abstract class Renderer {
|
|||
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
|
||||
this.addImports(outputText, importManager.getAllImports(dtsFile.fileName), dtsFile);
|
||||
|
||||
this.addExports(outputText, dtsFile.fileName, renderInfo.privateExports);
|
||||
this.addExports(outputText, AbsoluteFsPath.fromSourceFile(dtsFile), renderInfo.privateExports);
|
||||
|
||||
|
||||
return this.renderSourceAndMap(dtsFile, input, outputText);
|
||||
|
@ -207,11 +208,12 @@ export abstract class Renderer {
|
|||
importManager: ImportManager): void {
|
||||
moduleWithProviders.forEach(info => {
|
||||
const ngModuleName = info.ngModule.node.name.text;
|
||||
const declarationFile = info.declaration.getSourceFile().fileName;
|
||||
const ngModuleFile = info.ngModule.node.getSourceFile().fileName;
|
||||
const declarationFile = AbsoluteFsPath.fromSourceFile(info.declaration.getSourceFile());
|
||||
const ngModuleFile = AbsoluteFsPath.fromSourceFile(info.ngModule.node.getSourceFile());
|
||||
const importPath = info.ngModule.viaModule ||
|
||||
(declarationFile !== ngModuleFile ?
|
||||
stripExtension(`./${relative(dirname(declarationFile), ngModuleFile)}`) :
|
||||
stripExtension(
|
||||
`./${PathSegment.relative(AbsoluteFsPath.dirname(declarationFile), ngModuleFile)}`) :
|
||||
null);
|
||||
const ngModule = getImportString(importManager, importPath, ngModuleName);
|
||||
|
||||
|
@ -252,7 +254,7 @@ export abstract class Renderer {
|
|||
output: MagicString, imports: {specifier: string, qualifier: string}[],
|
||||
sf: ts.SourceFile): void;
|
||||
protected abstract addExports(
|
||||
output: MagicString, entryPointBasePath: string, exports: ExportInfo[]): void;
|
||||
output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[]): void;
|
||||
protected abstract addDefinitions(
|
||||
output: MagicString, compiledClass: CompiledClass, definitions: string): void;
|
||||
protected abstract removeDecorators(
|
||||
|
@ -288,7 +290,7 @@ export abstract class Renderer {
|
|||
*/
|
||||
protected extractSourceMap(file: ts.SourceFile): SourceMapInfo {
|
||||
const inline = commentRegex.test(file.text);
|
||||
const external = mapFileCommentRegex.test(file.text);
|
||||
const external = mapFileCommentRegex.exec(file.text);
|
||||
|
||||
if (inline) {
|
||||
const inlineSourceMap = fromSource(file.text);
|
||||
|
@ -300,17 +302,22 @@ export abstract class Renderer {
|
|||
} else if (external) {
|
||||
let externalSourceMap: SourceMapConverter|null = null;
|
||||
try {
|
||||
externalSourceMap = fromMapFileSource(file.text, dirname(file.fileName));
|
||||
const fileName = external[1] || external[2];
|
||||
const filePath = AbsoluteFsPath.resolve(
|
||||
AbsoluteFsPath.dirname(AbsoluteFsPath.fromSourceFile(file)), fileName);
|
||||
const mappingFile = this.fs.readFile(filePath);
|
||||
externalSourceMap = fromJSON(mappingFile);
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
this.logger.warn(
|
||||
`The external map file specified in the source code comment "${e.path}" was not found on the file system.`);
|
||||
const mapPath = file.fileName + '.map';
|
||||
if (basename(e.path) !== basename(mapPath) && statSync(mapPath).isFile()) {
|
||||
const mapPath = AbsoluteFsPath.fromUnchecked(file.fileName + '.map');
|
||||
if (PathSegment.basename(e.path) !== PathSegment.basename(mapPath) &&
|
||||
this.fs.stat(mapPath).isFile()) {
|
||||
this.logger.warn(
|
||||
`Guessing the map file name from the source file name: "${basename(mapPath)}"`);
|
||||
`Guessing the map file name from the source file name: "${PathSegment.basename(mapPath)}"`);
|
||||
try {
|
||||
externalSourceMap = fromObject(JSON.parse(readFileSync(mapPath, 'utf8')));
|
||||
externalSourceMap = fromObject(JSON.parse(this.fs.readFile(mapPath)));
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
|
@ -333,9 +340,9 @@ export abstract class Renderer {
|
|||
*/
|
||||
protected renderSourceAndMap(
|
||||
sourceFile: ts.SourceFile, input: SourceMapInfo, output: MagicString): FileInfo[] {
|
||||
const outputPath = sourceFile.fileName;
|
||||
const outputMapPath = `${outputPath}.map`;
|
||||
const relativeSourcePath = basename(outputPath);
|
||||
const outputPath = AbsoluteFsPath.fromSourceFile(sourceFile);
|
||||
const outputMapPath = AbsoluteFsPath.fromUnchecked(`${outputPath}.map`);
|
||||
const relativeSourcePath = PathSegment.basename(outputPath);
|
||||
const relativeMapPath = `${relativeSourcePath}.map`;
|
||||
|
||||
const outputMap = output.generateMap({
|
||||
|
@ -502,8 +509,8 @@ export function renderDefinitions(
|
|||
return definitions;
|
||||
}
|
||||
|
||||
export function stripExtension(filePath: string): string {
|
||||
return filePath.replace(/\.(js|d\.ts)$/, '');
|
||||
export function stripExtension<T extends string>(filePath: T): T {
|
||||
return filePath.replace(/\.(js|d\.ts)$/, '') as T;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,15 +6,11 @@
|
|||
* 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 {dirname} from 'canonical-path';
|
||||
import {existsSync, writeFileSync} from 'fs';
|
||||
import {mkdir, mv} from 'shelljs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {EntryPoint} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileInfo} from '../rendering/renderer';
|
||||
|
||||
import {FileWriter} from './file_writer';
|
||||
|
||||
/**
|
||||
|
@ -22,19 +18,22 @@ import {FileWriter} from './file_writer';
|
|||
* a back-up of the original file with an extra `.bak` extension.
|
||||
*/
|
||||
export class InPlaceFileWriter implements FileWriter {
|
||||
constructor(protected fs: FileSystem) {}
|
||||
|
||||
writeBundle(_entryPoint: EntryPoint, _bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
|
||||
transformedFiles.forEach(file => this.writeFileAndBackup(file));
|
||||
}
|
||||
|
||||
protected writeFileAndBackup(file: FileInfo): void {
|
||||
mkdir('-p', dirname(file.path));
|
||||
const backPath = file.path + '.__ivy_ngcc_bak';
|
||||
if (existsSync(backPath)) {
|
||||
this.fs.ensureDir(AbsoluteFsPath.dirname(file.path));
|
||||
const backPath = AbsoluteFsPath.fromUnchecked(`${file.path}.__ivy_ngcc_bak`);
|
||||
if (this.fs.exists(backPath)) {
|
||||
throw new Error(
|
||||
`Tried to overwrite ${backPath} with an ngcc back up file, which is disallowed.`);
|
||||
}
|
||||
if (existsSync(file.path)) {
|
||||
mv(file.path, backPath);
|
||||
if (this.fs.exists(file.path)) {
|
||||
this.fs.moveFile(file.path, backPath);
|
||||
}
|
||||
writeFileSync(file.path, file.contents, 'utf8');
|
||||
this.fs.writeFile(file.path, file.contents);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,7 @@
|
|||
* 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 {dirname, join, relative} from 'canonical-path';
|
||||
import {writeFileSync} from 'fs';
|
||||
import {cp, mkdir} from 'shelljs';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
|
@ -32,7 +27,7 @@ const NGCC_DIRECTORY = '__ivy_ngcc__';
|
|||
export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
||||
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
|
||||
// The new folder is at the root of the overall package
|
||||
const ngccFolder = AbsoluteFsPath.fromUnchecked(join(entryPoint.package, NGCC_DIRECTORY));
|
||||
const ngccFolder = AbsoluteFsPath.join(entryPoint.package, NGCC_DIRECTORY);
|
||||
this.copyBundle(bundle, entryPoint.package, ngccFolder);
|
||||
transformedFiles.forEach(file => this.writeFile(file, entryPoint.package, ngccFolder));
|
||||
this.updatePackageJson(entryPoint, bundle.formatProperty, ngccFolder);
|
||||
|
@ -41,12 +36,13 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
|||
protected copyBundle(
|
||||
bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath) {
|
||||
bundle.src.program.getSourceFiles().forEach(sourceFile => {
|
||||
const relativePath = relative(packagePath, sourceFile.fileName);
|
||||
const relativePath =
|
||||
PathSegment.relative(packagePath, AbsoluteFsPath.fromSourceFile(sourceFile));
|
||||
const isOutsidePackage = relativePath.startsWith('..');
|
||||
if (!sourceFile.isDeclarationFile && !isOutsidePackage) {
|
||||
const newFilePath = join(ngccFolder, relativePath);
|
||||
mkdir('-p', dirname(newFilePath));
|
||||
cp(sourceFile.fileName, newFilePath);
|
||||
const newFilePath = AbsoluteFsPath.join(ngccFolder, relativePath);
|
||||
this.fs.ensureDir(AbsoluteFsPath.dirname(newFilePath));
|
||||
this.fs.copyFile(AbsoluteFsPath.fromSourceFile(sourceFile), newFilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -57,19 +53,24 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
|||
// This is either `.d.ts` or `.d.ts.map` file
|
||||
super.writeFileAndBackup(file);
|
||||
} else {
|
||||
const relativePath = relative(packagePath, file.path);
|
||||
const newFilePath = join(ngccFolder, relativePath);
|
||||
mkdir('-p', dirname(newFilePath));
|
||||
writeFileSync(newFilePath, file.contents, 'utf8');
|
||||
const relativePath = PathSegment.relative(packagePath, file.path);
|
||||
const newFilePath = AbsoluteFsPath.join(ngccFolder, relativePath);
|
||||
this.fs.ensureDir(AbsoluteFsPath.dirname(newFilePath));
|
||||
this.fs.writeFile(newFilePath, file.contents);
|
||||
}
|
||||
}
|
||||
|
||||
protected updatePackageJson(
|
||||
entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, ngccFolder: AbsoluteFsPath) {
|
||||
const formatPath = join(entryPoint.path, entryPoint.packageJson[formatProperty] !);
|
||||
const newFormatPath = join(ngccFolder, relative(entryPoint.package, formatPath));
|
||||
const formatPath =
|
||||
AbsoluteFsPath.join(entryPoint.path, entryPoint.packageJson[formatProperty] !);
|
||||
const newFormatPath =
|
||||
AbsoluteFsPath.join(ngccFolder, PathSegment.relative(entryPoint.package, formatPath));
|
||||
const newFormatProperty = formatProperty + '_ivy_ngcc';
|
||||
(entryPoint.packageJson as any)[newFormatProperty] = relative(entryPoint.path, newFormatPath);
|
||||
writeFileSync(join(entryPoint.path, 'package.json'), JSON.stringify(entryPoint.packageJson));
|
||||
(entryPoint.packageJson as any)[newFormatProperty] =
|
||||
PathSegment.relative(entryPoint.path, newFormatPath);
|
||||
this.fs.writeFile(
|
||||
AbsoluteFsPath.join(entryPoint.path, 'package.json'),
|
||||
JSON.stringify(entryPoint.packageJson));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ 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 {MockLogger} from '../helpers/mock_logger';
|
||||
import {makeTestBundleProgram} from '../helpers/utils';
|
||||
|
@ -136,8 +137,9 @@ describe('DecorationAnalyzer', () => {
|
|||
const reflectionHost =
|
||||
new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
const referencesRegistry = new NgccReferencesRegistry(reflectionHost);
|
||||
const fs = new NodeJSFileSystem();
|
||||
const analyzer = new DecorationAnalyzer(
|
||||
program, options, host, program.getTypeChecker(), reflectionHost, referencesRegistry,
|
||||
fs, program, options, host, program.getTypeChecker(), reflectionHost, referencesRegistry,
|
||||
[AbsoluteFsPath.fromUnchecked('/')], false);
|
||||
testHandler = createTestHandler();
|
||||
analyzer.handlers = [testHandler];
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
|
||||
describe('PrivateDeclarationsAnalyzer', () => {
|
||||
describe('analyzeProgram()', () => {
|
||||
|
||||
|
@ -147,11 +150,11 @@ describe('PrivateDeclarationsAnalyzer', () => {
|
|||
// not added to the ReferencesRegistry (i.e. they were not declared in an NgModule).
|
||||
expect(analyses.length).toEqual(2);
|
||||
expect(analyses).toEqual([
|
||||
{identifier: 'PrivateComponent1', from: '/src/b.js', dtsFrom: null, alias: null},
|
||||
{identifier: 'PrivateComponent1', from: _('/src/b.js'), dtsFrom: null, alias: null},
|
||||
{
|
||||
identifier: 'InternalComponent1',
|
||||
from: '/src/c.js',
|
||||
dtsFrom: '/typings/c.d.ts',
|
||||
from: _('/src/c.js'),
|
||||
dtsFrom: _('/typings/c.d.ts'),
|
||||
alias: null
|
||||
},
|
||||
]);
|
||||
|
@ -207,7 +210,7 @@ describe('PrivateDeclarationsAnalyzer', () => {
|
|||
const analyses = analyzer.analyzeProgram(program);
|
||||
expect(analyses).toEqual([{
|
||||
identifier: 'ComponentOne',
|
||||
from: '/src/a.js',
|
||||
from: _('/src/a.js'),
|
||||
dtsFrom: null,
|
||||
alias: 'aliasedComponentOne',
|
||||
}]);
|
||||
|
|
|
@ -5,8 +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 ts from 'typescript';
|
||||
|
||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
|
|
@ -9,6 +9,7 @@ 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 {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
|
@ -18,7 +19,8 @@ describe('DependencyResolver', () => {
|
|||
let host: EsmDependencyHost;
|
||||
let resolver: DependencyResolver;
|
||||
beforeEach(() => {
|
||||
host = new EsmDependencyHost(new ModuleResolver());
|
||||
const fs = new NodeJSFileSystem();
|
||||
host = new EsmDependencyHost(fs, new ModuleResolver(fs));
|
||||
resolver = new DependencyResolver(new MockLogger(), host);
|
||||
});
|
||||
describe('sortEntryPointsByDependency()', () => {
|
||||
|
|
|
@ -11,12 +11,16 @@ 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';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
|
||||
describe('DependencyHost', () => {
|
||||
let host: EsmDependencyHost;
|
||||
beforeEach(() => host = new EsmDependencyHost(new ModuleResolver()));
|
||||
beforeEach(() => {
|
||||
const fs = new NodeJSFileSystem();
|
||||
host = new EsmDependencyHost(fs, new ModuleResolver(fs));
|
||||
});
|
||||
|
||||
describe('getDependencies()', () => {
|
||||
beforeEach(createMockFileSystem);
|
||||
|
@ -94,13 +98,14 @@ describe('DependencyHost', () => {
|
|||
});
|
||||
|
||||
it('should support `paths` alias mappings when resolving modules', () => {
|
||||
host = new EsmDependencyHost(new ModuleResolver({
|
||||
baseUrl: '/dist',
|
||||
paths: {
|
||||
'@app/*': ['*'],
|
||||
'@lib/*/test': ['lib/*/test'],
|
||||
}
|
||||
}));
|
||||
const fs = new NodeJSFileSystem();
|
||||
host = new EsmDependencyHost(fs, new ModuleResolver(fs, {
|
||||
baseUrl: '/dist',
|
||||
paths: {
|
||||
'@app/*': ['*'],
|
||||
'@lib/*/test': ['lib/*/test'],
|
||||
}
|
||||
}));
|
||||
const {dependencies, missing, deepImports} = host.findDependencies(_('/path-alias/index.js'));
|
||||
expect(dependencies.size).toBe(4);
|
||||
expect(dependencies.has(_('/dist/components'))).toBe(true);
|
||||
|
|
|
@ -10,6 +10,7 @@ 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';
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
|
||||
|
@ -78,7 +79,8 @@ describe('ModuleResolver', () => {
|
|||
describe('resolveModule()', () => {
|
||||
describe('with relative paths', () => {
|
||||
it('should resolve sibling, child and aunt modules', () => {
|
||||
const resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
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')))
|
||||
|
@ -88,14 +90,16 @@ describe('ModuleResolver', () => {
|
|||
});
|
||||
|
||||
it('should return `null` if the resolved module relative module does not exist', () => {
|
||||
const resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
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 resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
expect(resolver.resolveModuleImport('package-1', _('/libs/local-package/index.js')))
|
||||
.toEqual(new ResolvedExternalModule(_('/libs/local-package/node_modules/package-1')));
|
||||
expect(
|
||||
|
@ -106,7 +110,8 @@ describe('ModuleResolver', () => {
|
|||
});
|
||||
|
||||
it('should resolve to the package.json of a higher node_modules package', () => {
|
||||
const resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
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')))
|
||||
|
@ -114,20 +119,23 @@ describe('ModuleResolver', () => {
|
|||
});
|
||||
|
||||
it('should return `null` if the package cannot be found', () => {
|
||||
const resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
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 resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
expect(resolver.resolveModuleImport('package-3', _('/libs/local-package/index.js')))
|
||||
.toBe(null);
|
||||
});
|
||||
|
||||
it('should identify deep imports into an external module', () => {
|
||||
const resolver = new ModuleResolver();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs);
|
||||
expect(
|
||||
resolver.resolveModuleImport('package-1/sub-folder', _('/libs/local-package/index.js')))
|
||||
.toEqual(
|
||||
|
@ -137,8 +145,9 @@ 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({baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
|
||||
new ModuleResolver(fs, {baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
|
||||
|
||||
expect(resolver.resolveModuleImport('package-4', _('/libs/local-package/index.js')))
|
||||
.toEqual(new ResolvedExternalModule(_('/dist/package-4')));
|
||||
|
@ -148,7 +157,8 @@ describe('ModuleResolver', () => {
|
|||
});
|
||||
|
||||
it('should select the best match by the length of prefix before the *', () => {
|
||||
const resolver = new ModuleResolver({
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs, {
|
||||
baseUrl: '/dist',
|
||||
paths: {
|
||||
'@lib/*': ['*'],
|
||||
|
@ -166,25 +176,28 @@ describe('ModuleResolver', () => {
|
|||
it('should follow the ordering of `paths` when matching mapped packages', () => {
|
||||
let resolver: ModuleResolver;
|
||||
|
||||
resolver = new ModuleResolver({baseUrl: '/dist', paths: {'*': ['*', 'sub-folder/*']}});
|
||||
const fs = new NodeJSFileSystem();
|
||||
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')));
|
||||
|
||||
resolver = new ModuleResolver({baseUrl: '/dist', paths: {'*': ['sub-folder/*', '*']}});
|
||||
resolver = new ModuleResolver(fs, {baseUrl: '/dist', paths: {'*': ['sub-folder/*', '*']}});
|
||||
expect(resolver.resolveModuleImport('package-4', _('/libs/local-package/index.js')))
|
||||
.toEqual(new ResolvedExternalModule(_('/dist/sub-folder/package-4')));
|
||||
});
|
||||
|
||||
it('should resolve packages when the path mappings have post-fixes', () => {
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver =
|
||||
new ModuleResolver({baseUrl: '/dist', paths: {'*': ['sub-folder/*/post-fix']}});
|
||||
new ModuleResolver(fs, {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({baseUrl: '/dist', paths: {'@shared/*': ['sub-folder/*']}});
|
||||
new ModuleResolver(fs, {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')))
|
||||
|
@ -193,15 +206,17 @@ describe('ModuleResolver', () => {
|
|||
|
||||
it('should resolve path as "relative" if the mapped path is inside the current package',
|
||||
() => {
|
||||
const resolver = new ModuleResolver({baseUrl: '/dist', paths: {'@shared/*': ['*']}});
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(fs, {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 resolver =
|
||||
new ModuleResolver({baseUrl: '/dist', paths: {'@shared/*/post-fix': ['*/post-fix']}});
|
||||
const fs = new NodeJSFileSystem();
|
||||
const resolver = new ModuleResolver(
|
||||
fs, {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')))
|
||||
|
|
|
@ -15,6 +15,7 @@ import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
|
|||
|
||||
export {getDeclaration} from '../../../src/ngtsc/testing/in_memory_typescript';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
/**
|
||||
*
|
||||
* @param format The format of the bundle.
|
||||
|
@ -28,11 +29,7 @@ export function makeTestEntryPointBundle(
|
|||
const src = makeTestBundleProgram(files);
|
||||
const dts = dtsFiles ? makeTestBundleProgram(dtsFiles) : null;
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
return {
|
||||
formatProperty,
|
||||
format,
|
||||
rootDirs: [AbsoluteFsPath.fromUnchecked('/')], src, dts, isCore, isFlatCore
|
||||
};
|
||||
return {formatProperty, format, rootDirs: [_('/')], src, dts, isCore, isFlatCore};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,10 +38,10 @@ export function makeTestEntryPointBundle(
|
|||
*/
|
||||
export function makeTestBundleProgram(files: {name: string, contents: string}[]): BundleProgram {
|
||||
const {program, options, host} = makeTestProgramInternal(...files);
|
||||
const path = files[0].name;
|
||||
const path = _(files[0].name);
|
||||
const file = program.getSourceFile(path) !;
|
||||
const r3SymbolsInfo = files.find(file => file.name.indexOf('r3_symbols') !== -1) || null;
|
||||
const r3SymbolsPath = r3SymbolsInfo && r3SymbolsInfo.name;
|
||||
const r3SymbolsPath = r3SymbolsInfo && _(r3SymbolsInfo.name);
|
||||
const r3SymbolsFile = r3SymbolsPath && program.getSourceFile(r3SymbolsPath) || null;
|
||||
return {program, options, host, path, file, r3SymbolsPath, r3SymbolsFile};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {ClassMemberKind, CtorParameter, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils';
|
||||
|
@ -1007,7 +1007,7 @@ describe('Esm2015ReflectionHost', () => {
|
|||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining({
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({
|
||||
name: 'arg1',
|
||||
decorators: [],
|
||||
}));
|
||||
|
@ -1021,7 +1021,7 @@ describe('Esm2015ReflectionHost', () => {
|
|||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining({
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({
|
||||
name: 'arg1',
|
||||
decorators: null,
|
||||
}));
|
||||
|
@ -1035,7 +1035,7 @@ describe('Esm2015ReflectionHost', () => {
|
|||
const parameters = host.getConstructorParameters(classNode) !;
|
||||
|
||||
expect(parameters.length).toBe(1);
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining({
|
||||
expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({
|
||||
name: 'arg1',
|
||||
decorators: null,
|
||||
}));
|
||||
|
@ -1115,11 +1115,11 @@ describe('Esm2015ReflectionHost', () => {
|
|||
const parameters = host.getConstructorParameters(classNode);
|
||||
|
||||
expect(parameters !.length).toBe(2);
|
||||
expect(parameters ![0]).toEqual(jasmine.objectContaining({
|
||||
expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({
|
||||
name: 'arg1',
|
||||
decorators: null,
|
||||
}));
|
||||
expect(parameters ![1]).toEqual(jasmine.objectContaining({
|
||||
expect(parameters ![1]).toEqual(jasmine.objectContaining<CtorParameter>({
|
||||
name: 'arg2',
|
||||
decorators: jasmine.any(Array) as any
|
||||
}));
|
||||
|
|
|
@ -12,6 +12,7 @@ import * as mockFs from 'mock-fs';
|
|||
import {join} from 'path';
|
||||
|
||||
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../../../test/runfile_helpers';
|
||||
import {NodeJSFileSystem} from '../../src/file_system/node_js_file_system';
|
||||
import {mainNgcc} from '../../src/main';
|
||||
import {markAsProcessed} from '../../src/packages/build_marker';
|
||||
import {EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point';
|
||||
|
@ -144,8 +145,10 @@ describe('ngcc main()', () => {
|
|||
const basePath = '/node_modules';
|
||||
const targetPackageJsonPath = _(join(basePath, packagePath, 'package.json'));
|
||||
const targetPackage = loadPackage(packagePath);
|
||||
markAsProcessed(targetPackage, targetPackageJsonPath, 'typings');
|
||||
properties.forEach(property => markAsProcessed(targetPackage, targetPackageJsonPath, property));
|
||||
const fs = new NodeJSFileSystem();
|
||||
markAsProcessed(fs, targetPackage, targetPackageJsonPath, 'typings');
|
||||
properties.forEach(
|
||||
property => markAsProcessed(fs, targetPackage, targetPackageJsonPath, property));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {readFileSync, writeFileSync} from 'fs';
|
||||
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 {EntryPoint} from '../../src/packages/entry_point';
|
||||
|
||||
function createMockFileSystem() {
|
||||
mockFs({
|
||||
|
@ -106,21 +106,24 @@ describe('Marker files', () => {
|
|||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
const fs = new NodeJSFileSystem();
|
||||
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'esm5');
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'esm5');
|
||||
pkg = JSON.parse(readFileSync(COMMON_PACKAGE_PATH, 'utf8'));
|
||||
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'));
|
||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||
markAsProcessed(pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, 'fesm2015');
|
||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toEqual('0.0.0-PLACEHOLDER');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ 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 {MockLogger} from '../helpers/mock_logger';
|
||||
|
@ -22,12 +23,13 @@ describe('findEntryPoints()', () => {
|
|||
let resolver: DependencyResolver;
|
||||
let finder: EntryPointFinder;
|
||||
beforeEach(() => {
|
||||
const fs = new NodeJSFileSystem();
|
||||
resolver =
|
||||
new DependencyResolver(new MockLogger(), new EsmDependencyHost(new ModuleResolver()));
|
||||
new DependencyResolver(new MockLogger(), new EsmDependencyHost(fs, new ModuleResolver(fs)));
|
||||
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
||||
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
||||
});
|
||||
finder = new EntryPointFinder(new MockLogger(), resolver);
|
||||
finder = new EntryPointFinder(fs, new MockLogger(), resolver);
|
||||
});
|
||||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
|
|
@ -6,24 +6,27 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/path';
|
||||
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 {getEntryPointInfo} from '../../src/packages/entry_point';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
|
||||
describe('getEntryPointInfo()', () => {
|
||||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
||||
const _ = AbsoluteFsPath.from;
|
||||
const SOME_PACKAGE = _('/some_package');
|
||||
|
||||
it('should return an object containing absolute paths to the formats of the specified entry-point',
|
||||
() => {
|
||||
const entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/valid_entry_point'));
|
||||
const fs = new NodeJSFileSystem();
|
||||
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,
|
||||
|
@ -35,21 +38,24 @@ describe('getEntryPointInfo()', () => {
|
|||
});
|
||||
|
||||
it('should return null if there is no package.json at the entry-point path', () => {
|
||||
const entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/missing_package_json'));
|
||||
const fs = new NodeJSFileSystem();
|
||||
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 entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/missing_typings'));
|
||||
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 entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
|
||||
const fs = new NodeJSFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
|
||||
expect(entryPoint).toEqual({
|
||||
name: 'some-package/missing_metadata',
|
||||
package: SOME_PACKAGE,
|
||||
|
@ -61,8 +67,9 @@ describe('getEntryPointInfo()', () => {
|
|||
});
|
||||
|
||||
it('should work if the typings field is named `types', () => {
|
||||
const fs = new NodeJSFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
new MockLogger(), SOME_PACKAGE, _('/some_package/types_rather_than_typings'));
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/types_rather_than_typings'));
|
||||
expect(entryPoint).toEqual({
|
||||
name: 'some-package/types_rather_than_typings',
|
||||
package: SOME_PACKAGE,
|
||||
|
@ -74,8 +81,9 @@ describe('getEntryPointInfo()', () => {
|
|||
});
|
||||
|
||||
it('should work with Angular Material style package.json', () => {
|
||||
const fs = new NodeJSFileSystem();
|
||||
const entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/material_style'));
|
||||
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/material_style'));
|
||||
expect(entryPoint).toEqual({
|
||||
name: 'some_package/material_style',
|
||||
package: SOME_PACKAGE,
|
||||
|
@ -87,8 +95,9 @@ describe('getEntryPointInfo()', () => {
|
|||
});
|
||||
|
||||
it('should return null if the package.json is not valid JSON', () => {
|
||||
const entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
|
||||
const fs = new NodeJSFileSystem();
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,31 +5,33 @@
|
|||
* 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 {dirname} from 'canonical-path';
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
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 {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
|
||||
function setup(file: {name: string, contents: string}) {
|
||||
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 decorationAnalyses =
|
||||
new DecorationAnalyzer(
|
||||
bundle.src.program, bundle.src.options, bundle.src.host, typeChecker, host,
|
||||
referencesRegistry, [AbsoluteFsPath.fromUnchecked('/')], false)
|
||||
.analyzeProgram();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const decorationAnalyses = new DecorationAnalyzer(
|
||||
fs, bundle.src.program, bundle.src.options, bundle.src.host,
|
||||
typeChecker, host, referencesRegistry, [_('/')], false)
|
||||
.analyzeProgram();
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||
const renderer = new EsmRenderer(logger, host, false, bundle);
|
||||
const renderer = new EsmRenderer(fs, logger, host, false, bundle);
|
||||
return {
|
||||
host,
|
||||
program: bundle.src.program,
|
||||
|
@ -136,11 +138,11 @@ import * as i1 from '@angular/common';`);
|
|||
it('should insert the given exports at the end of the source file', () => {
|
||||
const {renderer} = setup(PROGRAM);
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
renderer.addExports(output, PROGRAM.name.replace(/\.js$/, ''), [
|
||||
{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'},
|
||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
||||
{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'},
|
||||
]);
|
||||
expect(output.toString()).toContain(`
|
||||
// Some other content
|
||||
|
@ -153,11 +155,11 @@ export {TopLevelComponent};`);
|
|||
it('should not insert alias exports in js output', () => {
|
||||
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'},
|
||||
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'},
|
||||
]);
|
||||
const outputString = output.toString();
|
||||
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
||||
|
|
|
@ -5,31 +5,33 @@
|
|||
* 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 {dirname} from 'canonical-path';
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
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 {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
|
||||
function setup(file: {name: string, contents: string}) {
|
||||
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 decorationAnalyses =
|
||||
new DecorationAnalyzer(
|
||||
bundle.src.program, bundle.src.options, bundle.src.host, typeChecker, host,
|
||||
referencesRegistry, [AbsoluteFsPath.fromUnchecked('/')], false)
|
||||
.analyzeProgram();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const decorationAnalyses = new DecorationAnalyzer(
|
||||
fs, bundle.src.program, bundle.src.options, bundle.src.host,
|
||||
typeChecker, host, referencesRegistry, [_('/')], false)
|
||||
.analyzeProgram();
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||
const renderer = new Esm5Renderer(logger, host, false, bundle);
|
||||
const renderer = new Esm5Renderer(fs, logger, host, false, bundle);
|
||||
return {
|
||||
host,
|
||||
program: bundle.src.program,
|
||||
|
@ -173,11 +175,11 @@ import * as i1 from '@angular/common';`);
|
|||
it('should insert the given exports at the end of the source file', () => {
|
||||
const {renderer} = setup(PROGRAM);
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
renderer.addExports(output, PROGRAM.name.replace(/\.js$/, ''), [
|
||||
{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'},
|
||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
||||
{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'},
|
||||
]);
|
||||
expect(output.toString()).toContain(`
|
||||
export {A, B, C, NoIife, BadIife};
|
||||
|
@ -190,11 +192,11 @@ export {TopLevelComponent};`);
|
|||
it('should not insert alias exports in js output', () => {
|
||||
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'},
|
||||
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'},
|
||||
]);
|
||||
const outputString = output.toString();
|
||||
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as fs from 'fs';
|
|||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
import {fromObject, generateMapFileComment} from 'convert-source-map';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||
import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||
|
@ -20,11 +21,16 @@ import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
|
|||
import {makeTestEntryPointBundle} from '../helpers/utils';
|
||||
import {Logger} from '../../src/logging/logger';
|
||||
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;
|
||||
|
||||
class TestRenderer extends Renderer {
|
||||
constructor(
|
||||
logger: Logger, host: Esm2015ReflectionHost, isCore: boolean, bundle: EntryPointBundle) {
|
||||
super(logger, host, isCore, bundle);
|
||||
fs: FileSystem, logger: Logger, host: Esm2015ReflectionHost, isCore: boolean,
|
||||
bundle: EntryPointBundle) {
|
||||
super(fs, logger, host, isCore, bundle);
|
||||
}
|
||||
addImports(
|
||||
output: MagicString, imports: {specifier: string, qualifier: string}[], sf: ts.SourceFile) {
|
||||
|
@ -59,8 +65,9 @@ function createTestRenderer(
|
|||
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(
|
||||
bundle.src.program, bundle.src.options, bundle.src.host,
|
||||
fs, bundle.src.program, bundle.src.options, bundle.src.host,
|
||||
typeChecker, host, referencesRegistry, bundle.rootDirs, isCore)
|
||||
.analyzeProgram();
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||
|
@ -68,7 +75,7 @@ function createTestRenderer(
|
|||
new ModuleWithProvidersAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||
const privateDeclarationsAnalyses =
|
||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||
const renderer = new TestRenderer(logger, host, isCore, bundle);
|
||||
const renderer = new TestRenderer(fs, logger, host, isCore, bundle);
|
||||
spyOn(renderer, 'addImports').and.callThrough();
|
||||
spyOn(renderer, 'addDefinitions').and.callThrough();
|
||||
spyOn(renderer, 'removeDecorators').and.callThrough();
|
||||
|
@ -354,7 +361,7 @@ describe('Renderer', () => {
|
|||
|
||||
// Add a mock export to trigger export rendering
|
||||
privateDeclarationsAnalyses.push(
|
||||
{identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'});
|
||||
{identifier: 'ComponentB', from: _('/src/file.js'), dtsFrom: _('/typings/b.d.ts')});
|
||||
|
||||
const result = renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
|
|
|
@ -9,10 +9,14 @@
|
|||
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';
|
||||
|
||||
const _ = AbsoluteFsPath.fromUnchecked;
|
||||
|
||||
function createMockFileSystem() {
|
||||
mockFs({
|
||||
'/package/path': {
|
||||
|
@ -39,12 +43,13 @@ describe('InPlaceFileWriter', () => {
|
|||
afterEach(restoreRealFileSystem);
|
||||
|
||||
it('should write all the FileInfo to the disk', () => {
|
||||
const fileWriter = new InPlaceFileWriter();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const fileWriter = new InPlaceFileWriter(fs);
|
||||
fileWriter.writeBundle({} as EntryPoint, {} as EntryPointBundle, [
|
||||
{path: '/package/path/top-level.js', contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: '/package/path/folder-1/file-1.js', contents: 'MODIFIED FILE 1'},
|
||||
{path: '/package/path/folder-2/file-4.js', contents: 'MODIFIED FILE 4'},
|
||||
{path: '/package/path/folder-3/file-5.js', contents: 'NEW FILE 5'},
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{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');
|
||||
|
@ -55,12 +60,13 @@ describe('InPlaceFileWriter', () => {
|
|||
});
|
||||
|
||||
it('should create backups of all files that previously existed', () => {
|
||||
const fileWriter = new InPlaceFileWriter();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const fileWriter = new InPlaceFileWriter(fs);
|
||||
fileWriter.writeBundle({} as EntryPoint, {} as EntryPointBundle, [
|
||||
{path: '/package/path/top-level.js', contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: '/package/path/folder-1/file-1.js', contents: 'MODIFIED FILE 1'},
|
||||
{path: '/package/path/folder-2/file-4.js', contents: 'MODIFIED FILE 4'},
|
||||
{path: '/package/path/folder-3/file-5.js', contents: 'NEW FILE 5'},
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{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'))
|
||||
.toEqual('ORIGINAL TOP LEVEL');
|
||||
|
@ -74,12 +80,13 @@ describe('InPlaceFileWriter', () => {
|
|||
});
|
||||
|
||||
it('should error if the backup file already exists', () => {
|
||||
const fileWriter = new InPlaceFileWriter();
|
||||
const fs = new NodeJSFileSystem();
|
||||
const fileWriter = new InPlaceFileWriter(fs);
|
||||
expect(
|
||||
() => fileWriter.writeBundle(
|
||||
{} as EntryPoint, {} as EntryPointBundle,
|
||||
[
|
||||
{path: '/package/path/already-backed-up.js', contents: 'MODIFIED BACKED UP'},
|
||||
{path: _('/package/path/already-backed-up.js'), contents: 'MODIFIED BACKED UP'},
|
||||
]))
|
||||
.toThrowError(
|
||||
'Tried to overwrite /package/path/already-backed-up.js.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.');
|
||||
|
|
|
@ -10,6 +10,8 @@ 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';
|
||||
|
@ -81,6 +83,7 @@ describe('NewEntryPointFileWriter', () => {
|
|||
beforeEach(createMockFileSystem);
|
||||
afterEach(restoreRealFileSystem);
|
||||
|
||||
let fs: FileSystem;
|
||||
let fileWriter: FileWriter;
|
||||
let entryPoint: EntryPoint;
|
||||
let esm5bundle: EntryPointBundle;
|
||||
|
@ -88,17 +91,21 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
describe('writeBundle() [primary entry-point]', () => {
|
||||
beforeEach(() => {
|
||||
fileWriter = new NewEntryPointFileWriter();
|
||||
entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), _('/node_modules/test'), _('/node_modules/test')) !;
|
||||
esm5bundle = makeTestBundle(entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(entryPoint, 'es2015', 'esm2015');
|
||||
fs = new NodeJSFileSystem();
|
||||
fileWriter = new NewEntryPointFileWriter(fs);
|
||||
entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test')) !;
|
||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||
});
|
||||
|
||||
it('should write the modified files to a new folder', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/esm5.js', contents: 'export function FooTop() {} // MODIFIED'},
|
||||
{path: '/node_modules/test/esm5.js.map', contents: 'MODIFIED MAPPING DATA'},
|
||||
{
|
||||
path: _('/node_modules/test/esm5.js'),
|
||||
contents: 'export function FooTop() {} // MODIFIED'
|
||||
},
|
||||
{path: _('/node_modules/test/esm5.js.map'), contents: 'MODIFIED MAPPING DATA'},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/__ivy_ngcc__/esm5.js', 'utf8'))
|
||||
.toEqual('export function FooTop() {} // MODIFIED');
|
||||
|
@ -112,7 +119,10 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
it('should also copy unmodified files in the program', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{path: '/node_modules/test/es2015/foo.js', contents: 'export class FooTop {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/es2015/foo.js'),
|
||||
contents: 'export class FooTop {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/__ivy_ngcc__/es2015/foo.js', 'utf8'))
|
||||
.toEqual('export class FooTop {} // MODIFIED');
|
||||
|
@ -126,14 +136,20 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
it('should update the package.json properties', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/esm5.js', contents: 'export function FooTop() {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/esm5.js'),
|
||||
contents: 'export function FooTop() {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(loadPackageJson('/node_modules/test')).toEqual(jasmine.objectContaining({
|
||||
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
}));
|
||||
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{path: '/node_modules/test/es2015/foo.js', contents: 'export class FooTop {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/es2015/foo.js'),
|
||||
contents: 'export class FooTop {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(loadPackageJson('/node_modules/test')).toEqual(jasmine.objectContaining({
|
||||
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
|
@ -144,10 +160,10 @@ describe('NewEntryPointFileWriter', () => {
|
|||
it('should overwrite and backup typings files', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{
|
||||
path: '/node_modules/test/index.d.ts',
|
||||
contents: 'export declare class FooTop {} // MODIFIED'
|
||||
path: _('/node_modules/test/index.d.ts'),
|
||||
contents: 'export declare class FooTop {} // MODIFIED',
|
||||
},
|
||||
{path: '/node_modules/test/index.d.ts.map', contents: 'MODIFIED MAPPING DATA'},
|
||||
{path: _('/node_modules/test/index.d.ts.map'), contents: 'MODIFIED MAPPING DATA'},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/index.d.ts', 'utf8'))
|
||||
.toEqual('export declare class FooTop {} // MODIFIED');
|
||||
|
@ -165,16 +181,20 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
describe('writeBundle() [secondary entry-point]', () => {
|
||||
beforeEach(() => {
|
||||
fileWriter = new NewEntryPointFileWriter();
|
||||
entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), _('/node_modules/test'), _('/node_modules/test/a')) !;
|
||||
esm5bundle = makeTestBundle(entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(entryPoint, 'es2015', 'esm2015');
|
||||
fs = new NodeJSFileSystem();
|
||||
fileWriter = new NewEntryPointFileWriter(fs);
|
||||
entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/a')) !;
|
||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||
});
|
||||
|
||||
it('should write the modified file to a new folder', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/a/esm5.js', contents: 'export function FooA() {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/a/esm5.js'),
|
||||
contents: 'export function FooA() {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/__ivy_ngcc__/a/esm5.js', 'utf8'))
|
||||
.toEqual('export function FooA() {} // MODIFIED');
|
||||
|
@ -184,7 +204,10 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
it('should also copy unmodified files in the program', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{path: '/node_modules/test/a/es2015/foo.js', contents: 'export class FooA {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/a/es2015/foo.js'),
|
||||
contents: 'export class FooA {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/__ivy_ngcc__/a/es2015/foo.js', 'utf8'))
|
||||
.toEqual('export class FooA {} // MODIFIED');
|
||||
|
@ -198,14 +221,20 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
it('should update the package.json properties', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/a/esm5.js', contents: 'export function FooA() {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/a/esm5.js'),
|
||||
contents: 'export function FooA() {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(loadPackageJson('/node_modules/test/a')).toEqual(jasmine.objectContaining({
|
||||
module_ivy_ngcc: '../__ivy_ngcc__/a/esm5.js',
|
||||
}));
|
||||
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{path: '/node_modules/test/a/es2015/foo.js', contents: 'export class FooA {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/a/es2015/foo.js'),
|
||||
contents: 'export class FooA {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(loadPackageJson('/node_modules/test/a')).toEqual(jasmine.objectContaining({
|
||||
module_ivy_ngcc: '../__ivy_ngcc__/a/esm5.js',
|
||||
|
@ -216,7 +245,7 @@ describe('NewEntryPointFileWriter', () => {
|
|||
it('should overwrite and backup typings files', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{
|
||||
path: '/node_modules/test/a/index.d.ts',
|
||||
path: _('/node_modules/test/a/index.d.ts'),
|
||||
contents: 'export declare class FooA {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
|
@ -230,16 +259,20 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
describe('writeBundle() [entry-point (with files placed outside entry-point folder)]', () => {
|
||||
beforeEach(() => {
|
||||
fileWriter = new NewEntryPointFileWriter();
|
||||
entryPoint =
|
||||
getEntryPointInfo(new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !;
|
||||
esm5bundle = makeTestBundle(entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(entryPoint, 'es2015', 'esm2015');
|
||||
fs = new NodeJSFileSystem();
|
||||
fileWriter = new NewEntryPointFileWriter(fs);
|
||||
entryPoint = getEntryPointInfo(
|
||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !;
|
||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||
});
|
||||
|
||||
it('should write the modified file to a new folder', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/lib/esm5.js', contents: 'export function FooB() {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/lib/esm5.js'),
|
||||
contents: 'export function FooB() {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(readFileSync('/node_modules/test/__ivy_ngcc__/lib/esm5.js', 'utf8'))
|
||||
.toEqual('export function FooB() {} // MODIFIED');
|
||||
|
@ -250,7 +283,7 @@ describe('NewEntryPointFileWriter', () => {
|
|||
it('should also copy unmodified files in the program', () => {
|
||||
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'
|
||||
},
|
||||
]);
|
||||
|
@ -278,7 +311,7 @@ describe('NewEntryPointFileWriter', () => {
|
|||
it('should not copy files outside of the package', () => {
|
||||
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'
|
||||
},
|
||||
]);
|
||||
|
@ -288,7 +321,10 @@ describe('NewEntryPointFileWriter', () => {
|
|||
|
||||
it('should update the package.json properties', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm5bundle, [
|
||||
{path: '/node_modules/test/lib/esm5.js', contents: 'export function FooB() {} // MODIFIED'},
|
||||
{
|
||||
path: _('/node_modules/test/lib/esm5.js'),
|
||||
contents: 'export function FooB() {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
expect(loadPackageJson('/node_modules/test/b')).toEqual(jasmine.objectContaining({
|
||||
module_ivy_ngcc: '../__ivy_ngcc__/lib/esm5.js',
|
||||
|
@ -296,7 +332,7 @@ 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'
|
||||
},
|
||||
]);
|
||||
|
@ -309,7 +345,7 @@ describe('NewEntryPointFileWriter', () => {
|
|||
it('should overwrite and backup typings files', () => {
|
||||
fileWriter.writeBundle(entryPoint, esm2015bundle, [
|
||||
{
|
||||
path: '/node_modules/test/typings/index.d.ts',
|
||||
path: _('/node_modules/test/typings/index.d.ts'),
|
||||
contents: 'export declare class FooB {} // MODIFIED'
|
||||
},
|
||||
]);
|
||||
|
@ -323,9 +359,9 @@ describe('NewEntryPointFileWriter', () => {
|
|||
});
|
||||
|
||||
function makeTestBundle(
|
||||
entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty,
|
||||
fs: FileSystem, entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty,
|
||||
format: EntryPointFormat): EntryPointBundle {
|
||||
return makeEntryPointBundle(
|
||||
entryPoint.path, entryPoint.packageJson[formatProperty] !, entryPoint.typings, false,
|
||||
fs, entryPoint.path, entryPoint.packageJson[formatProperty] !, entryPoint.typings, false,
|
||||
formatProperty, format, true) !;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue