Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"@types/jasmine": "^2.8.8",
"@types/jasminewd2": "^2.0.6",
"@types/minimist": "^1.2.0",
"@types/mock-fs": "^3.6.30",
"@types/node": "^10.9.4",
"@types/selenium-webdriver": "3.0.7",
"@types/shelljs": "^0.7.8",
Expand Down Expand Up @@ -90,7 +89,6 @@
"magic-string": "^0.25.0",
"materialize-css": "1.0.0",
"minimist": "1.2.0",
"mock-fs": "^4.10.1",
"node-uuid": "1.4.8",
"nodejs-websocket": "^1.7.2",
"protractor": "^5.4.2",
Expand Down
18 changes: 13 additions & 5 deletions packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import * as ts from 'typescript';

import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
import {AbsoluteFsPath, FileSystem, LogicalFileSystem, absoluteFrom, dirname, resolve} from '../../../src/ngtsc/file_system';
import {FileSystem, LogicalFileSystem, absoluteFrom, dirname, resolve} from '../../../src/ngtsc/file_system';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {ClassDeclaration, ClassSymbol, Decorator} from '../../../src/ngtsc/reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
import {NgccReflectionHost} from '../host/ngcc_host';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {isDefined} from '../utils';
import {isWithinPackage} from './util';

export interface AnalyzedFile {
sourceFile: ts.SourceFile;
Expand Down Expand Up @@ -66,6 +68,13 @@ class NgccResourceLoader implements ResourceLoader {
* This Analyzer will analyze the files that have decorated classes that need to be transformed.
*/
export class DecorationAnalyzer {
private program = this.bundle.src.program;
private options = this.bundle.src.options;
private host = this.bundle.src.host;
private typeChecker = this.bundle.src.program.getTypeChecker();
private rootDirs = this.bundle.rootDirs;
private packagePath = this.bundle.entryPoint.package;
private isCore = this.bundle.isCore;
resourceManager = new NgccResourceLoader(this.fs);
metaRegistry = new LocalMetadataRegistry();
dtsMetaReader = new DtsMetadataReader(this.typeChecker, this.reflectionHost);
Expand Down Expand Up @@ -112,10 +121,8 @@ export class DecorationAnalyzer {
];

constructor(
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) {}
private fs: FileSystem, private bundle: EntryPointBundle,
private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry) {}

/**
* Analyze a program to find all the decorated files should be transformed.
Expand All @@ -125,6 +132,7 @@ export class DecorationAnalyzer {
analyzeProgram(): DecorationAnalyses {
const decorationAnalyses = new DecorationAnalyses();
const analysedFiles = this.program.getSourceFiles()
.filter(sourceFile => isWithinPackage(this.packagePath, sourceFile))
.map(sourceFile => this.analyzeFile(sourceFile))
.filter(isDefined);
analysedFiles.forEach(analysedFile => this.resolveFile(analysedFile));
Expand Down
18 changes: 11 additions & 7 deletions packages/compiler-cli/ngcc/src/analysis/switch_marker_analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
import {isWithinPackage} from './util';

export interface SwitchMarkerAnalysis {
sourceFile: ts.SourceFile;
Expand All @@ -21,7 +23,7 @@ export const SwitchMarkerAnalyses = Map;
* that will be replaced.
*/
export class SwitchMarkerAnalyzer {
constructor(private host: NgccReflectionHost) {}
constructor(private host: NgccReflectionHost, private packagePath: AbsoluteFsPath) {}
/**
* Analyze the files in the program to identify declarations that contain R3
* switch markers.
Expand All @@ -32,12 +34,14 @@ export class SwitchMarkerAnalyzer {
*/
analyzeProgram(program: ts.Program): SwitchMarkerAnalyses {
const analyzedFiles = new SwitchMarkerAnalyses();
program.getSourceFiles().forEach(sourceFile => {
const declarations = this.host.getSwitchableDeclarations(sourceFile);
if (declarations.length) {
analyzedFiles.set(sourceFile, {sourceFile, declarations});
}
});
program.getSourceFiles()
.filter(sourceFile => isWithinPackage(this.packagePath, sourceFile))
.forEach(sourceFile => {
const declarations = this.host.getSwitchableDeclarations(sourceFile);
if (declarations.length) {
analyzedFiles.set(sourceFile, {sourceFile, declarations});
}
});
return analyzedFiles;
}
}
13 changes: 13 additions & 0 deletions packages/compiler-cli/ngcc/src/analysis/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @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 ts from 'typescript';
import {AbsoluteFsPath, absoluteFromSourceFile, relative} from '../../../src/ngtsc/file_system';

export function isWithinPackage(packagePath: AbsoluteFsPath, sourceFile: ts.SourceFile): boolean {
return !relative(packagePath, absoluteFromSourceFile(sourceFile)).startsWith('..');
}
38 changes: 29 additions & 9 deletions packages/compiler-cli/ngcc/src/host/esm2015_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1084,16 +1084,28 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N

/**
* Get the parameter type and decorators for the constructor of a class,
* where the information is stored on a static method of the class.
* where the information is stored on a static property of the class.
*
* Note that in ESM2015, the method is defined by an arrow function that returns an array of
* decorator and type information.
* Note that in ESM2015, the property is defined an array, or by an arrow function that returns an
* array, of decorator and type information.
*
* For example,
*
* ```
* SomeDirective.ctorParameters = () => [
* { type: ViewContainerRef, },
* { type: TemplateRef, },
* { type: undefined, decorators: [{ type: Inject, args: [INJECTED_TOKEN,] },] },
* {type: ViewContainerRef},
* {type: TemplateRef},
* {type: undefined, decorators: [{ type: Inject, args: [INJECTED_TOKEN]}]},
* ];
* ```
*
* or
*
* ```
* SomeDirective.ctorParameters = [
* {type: ViewContainerRef},
* {type: TemplateRef},
* {type: undefined, decorators: [{type: Inject, args: [INJECTED_TOKEN]}]},
* ];
* ```
*
Expand All @@ -1102,9 +1114,12 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
*/
protected getParamInfoFromStaticProperty(paramDecoratorsProperty: ts.Symbol): ParamInfo[]|null {
const paramDecorators = getPropertyValueFromSymbol(paramDecoratorsProperty);
if (paramDecorators && ts.isArrowFunction(paramDecorators)) {
if (ts.isArrayLiteralExpression(paramDecorators.body)) {
const elements = paramDecorators.body.elements;
if (paramDecorators) {
// The decorators array may be wrapped in an arrow function. If so unwrap it.
const container =
ts.isArrowFunction(paramDecorators) ? paramDecorators.body : paramDecorators;
if (ts.isArrayLiteralExpression(container)) {
const elements = container.elements;
return elements
.map(
element =>
Expand All @@ -1119,6 +1134,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
.filter(decorator => this.isFromCore(decorator));
return {typeExpression, decorators};
});
} else if (paramDecorators !== undefined) {
this.logger.warn(
'Invalid constructor parameter decorator in ' +
paramDecorators.getSourceFile().fileName + ':\n',
paramDecorators.getText());
}
}
return null;
Expand Down
8 changes: 7 additions & 1 deletion packages/compiler-cli/ngcc/src/host/esm5_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
*/
protected getParamInfoFromStaticProperty(paramDecoratorsProperty: ts.Symbol): ParamInfo[]|null {
const paramDecorators = getPropertyValueFromSymbol(paramDecoratorsProperty);
// The decorators array may be wrapped in a function. If so unwrap it.
const returnStatement = getReturnStatement(paramDecorators);
const expression = returnStatement && returnStatement.expression;
const expression = returnStatement ? returnStatement.expression : paramDecorators;
if (expression && ts.isArrayLiteralExpression(expression)) {
const elements = expression.elements;
return elements.map(reflectArrayElement).map(paramInfo => {
Expand All @@ -366,6 +367,11 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
const decorators = decoratorInfo && this.reflectDecorators(decoratorInfo);
return {typeExpression, decorators};
});
} else if (paramDecorators !== undefined) {
this.logger.warn(
'Invalid constructor parameter decorator in ' + paramDecorators.getSourceFile().fileName +
':\n',
paramDecorators.getText());
}
return null;
}
Expand Down
46 changes: 28 additions & 18 deletions packages/compiler-cli/ngcc/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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 {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, resolve} from '../../src/ngtsc/file_system';
import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system';
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
import {DependencyResolver} from './dependencies/dependency_resolver';
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
Expand All @@ -14,6 +14,7 @@ import {UmdDependencyHost} from './dependencies/umd_dependency_host';
import {ConsoleLogger, LogLevel} from './logging/console_logger';
import {Logger} from './logging/logger';
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
import {NgccConfiguration} from './packages/configuration';
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
import {makeEntryPointBundle} from './packages/entry_point_bundle';
import {EntryPointFinder} from './packages/entry_point_finder';
Expand Down Expand Up @@ -60,6 +61,10 @@ export interface NgccOptions {
* These are used to resolve paths to locally built Angular libraries.
*/
pathMappings?: PathMappings;
/**
* Provide a file-system service that will be used by ngcc for all file interactions.
*/
fileSystem?: FileSystem;
}

const SUPPORTED_FORMATS: EntryPointFormat[] = ['esm5', 'esm2015', 'umd', 'commonjs'];
Expand All @@ -76,27 +81,28 @@ export function mainNgcc(
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
compileAllFormats = true, createNewEntryPointFormats = false,
logger = new ConsoleLogger(LogLevel.info), pathMappings}: NgccOptions): void {
const fs = getFileSystem();
const transformer = new Transformer(fs, logger);
const moduleResolver = new ModuleResolver(fs, pathMappings);
const esmDependencyHost = new EsmDependencyHost(fs, moduleResolver);
const umdDependencyHost = new UmdDependencyHost(fs, moduleResolver);
const commonJsDependencyHost = new CommonJsDependencyHost(fs, moduleResolver);
const resolver = new DependencyResolver(fs, logger, {
const fileSystem = getFileSystem();
const transformer = new Transformer(fileSystem, logger);
const moduleResolver = new ModuleResolver(fileSystem, pathMappings);
const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver);
const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver);
const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver);
const resolver = new DependencyResolver(fileSystem, logger, {
esm5: esmDependencyHost,
esm2015: esmDependencyHost,
umd: umdDependencyHost,
commonjs: commonJsDependencyHost
});
const finder = new EntryPointFinder(fs, logger, resolver);
const fileWriter = getFileWriter(fs, createNewEntryPointFormats);
const config = new NgccConfiguration(fileSystem, dirname(absoluteFrom(basePath)));
const finder = new EntryPointFinder(fileSystem, config, logger, resolver);
const fileWriter = getFileWriter(fileSystem, createNewEntryPointFormats);

const absoluteTargetEntryPointPath =
targetEntryPointPath ? resolve(basePath, targetEntryPointPath) : undefined;

if (absoluteTargetEntryPointPath &&
hasProcessedTargetEntryPoint(
fs, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
fileSystem, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
logger.debug('The target entry-point has already been processed');
return;
}
Expand All @@ -112,7 +118,8 @@ export function mainNgcc(
});

if (absoluteTargetEntryPointPath && entryPoints.length === 0) {
markNonAngularPackageAsProcessed(fs, absoluteTargetEntryPointPath, propertiesToConsider);
markNonAngularPackageAsProcessed(
fileSystem, absoluteTargetEntryPointPath, propertiesToConsider);
return;
}

Expand All @@ -122,14 +129,14 @@ export function mainNgcc(

const compiledFormats = new Set<string>();
const entryPointPackageJson = entryPoint.packageJson;
const entryPointPackageJsonPath = fs.resolve(entryPoint.path, 'package.json');
const entryPointPackageJsonPath = fileSystem.resolve(entryPoint.path, 'package.json');

const hasProcessedDts = hasBeenProcessed(entryPointPackageJson, 'typings');

for (let i = 0; i < propertiesToConsider.length; i++) {
const property = propertiesToConsider[i] as EntryPointJsonProperty;
const formatPath = entryPointPackageJson[property];
const format = getEntryPointFormat(fs, entryPoint, property);
const format = getEntryPointFormat(fileSystem, entryPoint, property);

// No format then this property is not supposed to be compiled.
if (!formatPath || !format || SUPPORTED_FORMATS.indexOf(format) === -1) continue;
Expand All @@ -147,8 +154,7 @@ 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(
fs, entryPoint.path, formatPath, entryPoint.typings, isCore, property, format,
processDts, pathMappings);
fileSystem, entryPoint, formatPath, isCore, property, format, processDts, pathMappings);
if (bundle) {
logger.info(`Compiling ${entryPoint.name} : ${property} as ${format}`);
const transformedFiles = transformer.transform(bundle);
Expand All @@ -165,9 +171,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(fs, entryPointPackageJson, entryPointPackageJsonPath, property);
markAsProcessed(fileSystem, entryPointPackageJson, entryPointPackageJsonPath, property);
if (processDts) {
markAsProcessed(fs, entryPointPackageJson, entryPointPackageJsonPath, 'typings');
markAsProcessed(fileSystem, entryPointPackageJson, entryPointPackageJsonPath, 'typings');
}
}
}
Expand All @@ -187,6 +193,10 @@ function hasProcessedTargetEntryPoint(
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
compileAllFormats: boolean) {
const packageJsonPath = resolve(targetPath, 'package.json');
// It might be that this target is configured in which case its package.json might not exist.
if (!fs.exists(packageJsonPath)) {
return false;
}
const packageJson = JSON.parse(fs.readFile(packageJsonPath));

for (const property of propertiesToConsider) {
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler-cli/ngcc/src/packages/build_marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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 {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
import {AbsoluteFsPath, FileSystem, dirname} from '../../../src/ngtsc/file_system';
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';

export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
Expand Down Expand Up @@ -49,5 +49,8 @@ export function markAsProcessed(
format: EntryPointJsonProperty) {
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
// Just in case this package.json was synthesized due to a custom configuration
// we will ensure that the path to the containing folder exists before we write the file.
fs.ensureDir(dirname(packageJsonPath));
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
}
Loading