feat(compiler-cli): new compiler api and command-line using TypeScript transformers

This commit is contained in:
Chuck Jazdzewski
2017-06-09 14:50:57 -07:00
committed by Matias Niemelä
parent 43c187b624
commit 3097083277
31 changed files with 1990 additions and 115 deletions

View File

@ -63,8 +63,17 @@ export class AotCompiler {
emitAllStubs(analyzeResult: NgAnalyzedModules): GeneratedFile[] {
const {files} = analyzeResult;
const sourceModules =
files.map(file => this._compileStubFile(file.srcUrl, file.directives, file.ngModules));
const sourceModules = files.map(
file =>
this._compileStubFile(file.srcUrl, file.directives, file.pipes, file.ngModules, false));
return flatten(sourceModules);
}
emitPartialStubs(analyzeResult: NgAnalyzedModules): GeneratedFile[] {
const {files} = analyzeResult;
const sourceModules = files.map(
file =>
this._compileStubFile(file.srcUrl, file.directives, file.pipes, file.ngModules, true));
return flatten(sourceModules);
}
@ -78,13 +87,22 @@ export class AotCompiler {
}
private _compileStubFile(
srcFileUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]): GeneratedFile[] {
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], partial: boolean): GeneratedFile[] {
// partial is true when we only need the files we are certain will produce a factory and/or
// summary.
// This is the normal case for `ngc` but if we assume libraryies are generating their own
// factories
// then we might need a factory for a file that re-exports a module or factory which we cannot
// know
// ahead of time so we need a stub generate for all non-.d.ts files. The .d.ts files do not need
// to
// be excluded here because they are excluded when the modules are analyzed. If a factory ends
// up
// not being needed, the factory file is not written in writeFile callback.
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
const generatedFiles: GeneratedFile[] = [];
const jitSummaryStmts: o.Statement[] = [];
const ngFactoryStms: o.Statement[] = [];
const ngFactoryOutputCtx = this._createOutputContext(ngfactoryFilePath(srcFileUrl, true));
const jitSummaryOutputCtx = this._createOutputContext(summaryForJitFileName(srcFileUrl, true));
@ -93,29 +111,50 @@ export class AotCompiler {
this._ngModuleCompiler.createStub(ngFactoryOutputCtx, ngModuleReference);
createForJitStub(jitSummaryOutputCtx, ngModuleReference);
});
// Note: we are creating stub ngfactory/ngsummary for all source files,
// as the real calculation requires almost the same logic as producing the real content for
// them.
// Our pipeline will filter out empty ones at the end.
generatedFiles.push(this._codegenSourceModule(srcFileUrl, ngFactoryOutputCtx));
generatedFiles.push(this._codegenSourceModule(srcFileUrl, jitSummaryOutputCtx));
let partialJitStubRequired = false;
let partialFactoryStubRequired = false;
// create stubs for external stylesheets (always empty, as users should not import anything from
// the generated code)
directives.forEach((dirType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
partialJitStubRequired = true;
if (!compMeta.isComponent) {
return;
}
// Note: compMeta is a component and therefore template is non null.
compMeta.template !.externalStylesheets.forEach((stylesheetMeta) => {
generatedFiles.push(this._codegenSourceModule(
stylesheetMeta.moduleUrl !,
this._createOutputContext(_stylesModuleUrl(
stylesheetMeta.moduleUrl !, this._styleCompiler.needsStyleShim(compMeta),
fileSuffix))));
const styleContext = this._createOutputContext(_stylesModuleUrl(
stylesheetMeta.moduleUrl !, this._styleCompiler.needsStyleShim(compMeta), fileSuffix));
_createTypeReferenceStub(styleContext, Identifiers.ComponentFactory);
generatedFiles.push(this._codegenSourceModule(stylesheetMeta.moduleUrl !, styleContext));
});
partialFactoryStubRequired = true;
});
// If we need all the stubs to be generated then insert an arbitrary reference into the stub
if ((partialFactoryStubRequired || !partial) && ngFactoryOutputCtx.statements.length <= 0) {
_createTypeReferenceStub(ngFactoryOutputCtx, Identifiers.ComponentFactory);
}
if ((partialJitStubRequired || !partial || (pipes && pipes.length > 0)) &&
jitSummaryOutputCtx.statements.length <= 0) {
_createTypeReferenceStub(jitSummaryOutputCtx, Identifiers.ComponentFactory);
}
// Note: we are creating stub ngfactory/ngsummary for all source files,
// as the real calculation requires almost the same logic as producing the real content for
// them. Our pipeline will filter out empty ones at the end. Because of this filter, however,
// stub references to the reference type needs to be generated even if the user cannot
// refer to type from the `.d.ts` file to prevent the file being elided from the emit.
generatedFiles.push(this._codegenSourceModule(srcFileUrl, ngFactoryOutputCtx));
if (this._enableSummariesForJit) {
generatedFiles.push(this._codegenSourceModule(srcFileUrl, jitSummaryOutputCtx));
}
return generatedFiles;
}
@ -349,6 +388,10 @@ export class AotCompiler {
}
}
function _createTypeReferenceStub(outputCtx: OutputContext, reference: o.ExternalReference) {
outputCtx.statements.push(o.importExpr(reference).toStmt());
}
function _resolveStyleStatements(
symbolResolver: StaticSymbolResolver, compileResult: CompiledStylesheet, needsShim: boolean,
fileSuffix: string): void {

View File

@ -100,7 +100,6 @@ function createSummaryForJitFunction(
]));
}
class ToJsonSerializer extends ValueTransformer {
// Note: This only contains symbols without members.
symbols: StaticSymbol[] = [];

View File

@ -128,10 +128,6 @@ export function hostViewClassName(compType: any): string {
return `HostView_${identifierName({reference: compType})}`;
}
export function dirWrapperClassName(dirType: any) {
return `Wrapper_${identifierName({reference: dirType})}`;
}
export function componentFactoryName(compType: any): string {
return `${identifierName({reference: compType})}NgFactory`;
}

View File

@ -69,5 +69,5 @@ export * from './selector';
export * from './style_compiler';
export * from './template_parser/template_parser';
export {ViewCompiler} from './view_compiler/view_compiler';
export {isSyntaxError, syntaxError} from './util';
export {getParseErrors, isSyntaxError, syntaxError} from './util';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -23,8 +23,7 @@ import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsync, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';
import {SyncAsync, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');

View File

@ -118,12 +118,14 @@ export class ParseError {
public span: ParseSourceSpan, public msg: string,
public level: ParseErrorLevel = ParseErrorLevel.ERROR) {}
toString(): string {
contextualMessage(): string {
const ctx = this.span.start.getContext(100, 3);
const contextStr =
ctx ? ` ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` : '';
return ctx ? ` ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` : '';
}
toString(): string {
const details = this.span.details ? `, ${this.span.details}` : '';
return `${this.msg}${contextStr}: ${this.span.start}${details}`;
return `${this.msg}${this.contextualMessage()}: ${this.span.start}${details}`;
}
}

View File

@ -129,7 +129,7 @@ export class TemplateParser {
if (errors.length > 0) {
const errorString = errors.join('\n');
throw syntaxError(`Template parse errors:\n${errorString}`);
throw syntaxError(`Template parse errors:\n${errorString}`, errors);
}
return {template: result.templateAst !, pipes: result.usedPipes !};

View File

@ -9,6 +9,7 @@
import {ɵisPromise as isPromise} from '@angular/core';
import * as o from './output/output_ast';
import {ParseError} from './parse_util';
export const MODULE_SUFFIX = '';
@ -96,21 +97,26 @@ export const SyncAsync = {
all: <T>(syncAsyncValues: SyncAsync<T>[]): SyncAsync<T[]> => {
return syncAsyncValues.some(isPromise) ? Promise.all(syncAsyncValues) : syncAsyncValues as T[];
}
};
export function syntaxError(msg: string, parseErrors?: ParseError[]): Error {
const error = Error(msg);
(error as any)[ERROR_SYNTAX_ERROR] = true;
if (parseErrors) (error as any)[ERROR_PARSE_ERRORS] = parseErrors;
return error;
}
export function syntaxError(msg: string):
Error {
const error = Error(msg);
(error as any)[ERROR_SYNTAX_ERROR] = true;
return error;
}
const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
const ERROR_PARSE_ERRORS = 'ngParseErrors';
export function isSyntaxError(error: Error): boolean {
return (error as any)[ERROR_SYNTAX_ERROR];
}
export function getParseErrors(error: Error): ParseError[] {
return (error as any)[ERROR_PARSE_ERRORS] || [];
}
export function escapeRegExp(s: string): string {
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}