Compare commits
19 Commits
9.0.0-rc.0
...
5.0.0-rc.7
Author | SHA1 | Date | |
---|---|---|---|
5542517b9c | |||
fef3539608 | |||
f4d5729cb3 | |||
d343bf7885 | |||
9ce7f0e538 | |||
4a23df3909 | |||
14016c781f | |||
47caebfe86 | |||
5cfd9c6020 | |||
47bc6f105d | |||
40fa2593a9 | |||
680bcf7b8a | |||
ef08330341 | |||
6cc042e2ba | |||
9b26455740 | |||
18bce5987c | |||
f1108fea76 | |||
64b3e3e41a | |||
a82f863e24 |
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,3 +1,42 @@
|
||||
<a name="5.0.0-rc.7"></a>
|
||||
# [5.0.0-rc.7](https://github.com/angular/angular/compare/5.0.0-rc.6...5.0.0-rc.7) (2017-10-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** don’t store invalid state when using `listLazyRoutes` ([#19953](https://github.com/angular/angular/issues/19953)) ([4a23df3](https://github.com/angular/angular/commit/4a23df3))
|
||||
* **compiler:** make watch mode work on windows ([#19953](https://github.com/angular/angular/issues/19953)) ([f4d5729](https://github.com/angular/angular/commit/f4d5729)), closes [#19951](https://github.com/angular/angular/issues/19951)
|
||||
* **compiler:** recover from structural errors in watch mode ([#19953](https://github.com/angular/angular/issues/19953)) ([d343bf7](https://github.com/angular/angular/commit/d343bf7))
|
||||
* **compiler:** translate emit diagnostics with `noEmitOnError: true`. ([#19953](https://github.com/angular/angular/issues/19953)) ([9ce7f0e](https://github.com/angular/angular/commit/9ce7f0e)), closes [#19935](https://github.com/angular/angular/issues/19935)
|
||||
* **service-worker:** don't block initialization on registration ([#19936](https://github.com/angular/angular/issues/19936)) ([47caebf](https://github.com/angular/angular/commit/47caebf))
|
||||
* **service-worker:** fix improper call of Observable.merge ([#19962](https://github.com/angular/angular/issues/19962)) ([14016c7](https://github.com/angular/angular/commit/14016c7))
|
||||
* **service-worker:** listen for messages on the right event source ([#19954](https://github.com/angular/angular/issues/19954)) ([5cfd9c6](https://github.com/angular/angular/commit/5cfd9c6))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.6"></a>
|
||||
# [5.0.0-rc.6](https://github.com/angular/angular/compare/5.0.0-rc.5...5.0.0-rc.6) (2017-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** automatically set `emitDecoratorMetadata` when `"annotationsAs": "static fields”` ([#19927](https://github.com/angular/angular/issues/19927)) ([ef08330](https://github.com/angular/angular/commit/ef08330)), closes [#19916](https://github.com/angular/angular/issues/19916)
|
||||
* **compiler:** don’t type check templates with `skipTemplateCodegen` ([#19909](https://github.com/angular/angular/issues/19909)) ([18bce59](https://github.com/angular/angular/commit/18bce59))
|
||||
* **compiler-cli:** only use error collector when needed. ([#19912](https://github.com/angular/angular/issues/19912)) ([9b26455](https://github.com/angular/angular/commit/9b26455))
|
||||
* **compiler-cli:** produce correct paths for windows output ([#19915](https://github.com/angular/angular/issues/19915)) ([6cc042e](https://github.com/angular/angular/commit/6cc042e))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.5"></a>
|
||||
# [5.0.0-rc.5](https://github.com/angular/angular/compare/5.0.0-rc.4...5.0.0-rc.5) (2017-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** report all diagnostic error messages ([#19886](https://github.com/angular/angular/issues/19886)) ([a82f863](https://github.com/angular/angular/commit/a82f863))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.4"></a>
|
||||
# [5.0.0-rc.4](https://github.com/angular/angular/compare/5.0.0-rc.3...5.0.0-rc.4) (2017-10-24)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "5.0.0-rc.4",
|
||||
"version": "5.0.0-rc.7",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -24,7 +24,7 @@
|
||||
"dependencies": {
|
||||
"core-js": "^2.4.1",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
"rxjs": "^5.5.0",
|
||||
"rxjs": "^5.5.2",
|
||||
"tslib": "^1.7.1",
|
||||
"zone.js": "^0.8.12"
|
||||
},
|
||||
|
@ -38,10 +38,10 @@ export function translateDiagnostics(host: TypeCheckHost, untranslatedDiagnostic
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ts.push(diagnostic);
|
||||
}
|
||||
ts.push(diagnostic);
|
||||
});
|
||||
return {ts, ng};
|
||||
}
|
||||
|
@ -45,6 +45,12 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||
if (!transformDecorators && !transformTypesToClosure) {
|
||||
return undefined;
|
||||
}
|
||||
if (transformDecorators) {
|
||||
// This is needed as a workaround for https://github.com/angular/tsickle/issues/635
|
||||
// Otherwise tsickle might emit references to non imported values
|
||||
// as TypeScript elided the import.
|
||||
options.emitDecoratorMetadata = true;
|
||||
}
|
||||
const tsickleHost: tsickle.TsickleHost = {
|
||||
shouldSkipTsickleProcessing: (fileName) =>
|
||||
/\.d\.ts$/.test(fileName) || GENERATED_FILES.test(fileName),
|
||||
|
@ -89,7 +89,7 @@ export class NgTools_InternalApi_NG_2 {
|
||||
// as we only needed this to support Angular CLI 1.5.0 rc.*
|
||||
const ngProgram = createProgram({
|
||||
rootNames: options.program.getRootFileNames(),
|
||||
options: options.angularCompilerOptions,
|
||||
options: {...options.angularCompilerOptions, collectAllErrors: true},
|
||||
host: options.host
|
||||
});
|
||||
const lazyRoutes = ngProgram.listLazyRoutes(options.entryModule);
|
||||
|
@ -129,6 +129,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
return {close, ready: cb => readyPromise.then(cb), firstCompileResult};
|
||||
|
||||
function cacheEntry(fileName: string): CacheEntry {
|
||||
fileName = path.normalize(fileName);
|
||||
let entry = fileCache.get(fileName);
|
||||
if (!entry) {
|
||||
entry = {};
|
||||
@ -191,6 +192,10 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
};
|
||||
}
|
||||
ingoreFilesForWatch.clear();
|
||||
const oldProgram = cachedProgram;
|
||||
// We clear out the `cachedProgram` here as a
|
||||
// program can only be used as `oldProgram` 1x
|
||||
cachedProgram = undefined;
|
||||
const compileResult = performCompilation({
|
||||
rootNames: cachedOptions.rootNames,
|
||||
options: cachedOptions.options,
|
||||
@ -245,7 +250,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
if (event === FileChangeEvent.CreateDeleteDir) {
|
||||
fileCache.clear();
|
||||
} else {
|
||||
fileCache.delete(fileName);
|
||||
fileCache.delete(path.normalize(fileName));
|
||||
}
|
||||
|
||||
if (!ingoreFilesForWatch.has(path.normalize(fileName))) {
|
||||
|
@ -150,6 +150,9 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
||||
* in JIT mode. This is off by default.
|
||||
*/
|
||||
enableSummariesForJit?: boolean;
|
||||
|
||||
/** @internal */
|
||||
collectAllErrors?: boolean;
|
||||
}
|
||||
|
||||
export interface CompilerHost extends ts.CompilerHost {
|
||||
|
@ -426,6 +426,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
}
|
||||
|
||||
isSourceFile(filePath: string): boolean {
|
||||
// Don't generate any files nor typecheck them
|
||||
// if skipTemplateCodegen is set and fullTemplateTypeCheck is not yet set,
|
||||
// for backwards compatibility.
|
||||
if (this.options.skipTemplateCodegen && !this.options.fullTemplateTypeCheck) {
|
||||
return false;
|
||||
}
|
||||
// If we have a summary from a previous compilation,
|
||||
// treat the file never as a source file.
|
||||
if (this.librarySummaries.has(filePath)) {
|
||||
|
@ -181,11 +181,13 @@ function createVariableStatementForDeclarations(declarations: Declaration[]): ts
|
||||
/* modifiers */ undefined, ts.createVariableDeclarationList(varDecls, ts.NodeFlags.Const));
|
||||
}
|
||||
|
||||
export function getExpressionLoweringTransformFactory(requestsMap: RequestsMap):
|
||||
(context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||
export function getExpressionLoweringTransformFactory(
|
||||
requestsMap: RequestsMap, program: ts.Program): (context: ts.TransformationContext) =>
|
||||
(sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||
// Return the factory
|
||||
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile): ts.SourceFile => {
|
||||
const requests = requestsMap.getRequests(sourceFile);
|
||||
// We need to use the original SourceFile for reading metadata, and not the transformed one.
|
||||
const requests = requestsMap.getRequests(program.getSourceFile(sourceFile.fileName));
|
||||
if (requests && requests.size) {
|
||||
return transformSourceFile(sourceFile, requests, context);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, D
|
||||
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, isInRootDir, tsStructureIsReused} from './util';
|
||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused} from './util';
|
||||
|
||||
|
||||
|
||||
@ -149,11 +149,9 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
||||
ts.Diagnostic[] {
|
||||
if (sourceFile) {
|
||||
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
}
|
||||
const sourceFiles = sourceFile ? [sourceFile] : this.tsProgram.getSourceFiles();
|
||||
let diags: ts.Diagnostic[] = [];
|
||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
||||
sourceFiles.forEach(sf => {
|
||||
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||
}
|
||||
@ -177,15 +175,17 @@ class AngularCompilerProgram implements Program {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure already loaded');
|
||||
}
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
return this.compiler.loadFilesAsync(sourceFiles)
|
||||
.catch(this.catchAnalysisError.bind(this))
|
||||
.then(analyzedModules => {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
});
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
return this.compiler.loadFilesAsync(sourceFiles).then(analyzedModules => {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
});
|
||||
})
|
||||
.catch(e => this._createProgramOnError(e));
|
||||
}
|
||||
|
||||
listLazyRoutes(route?: string): LazyRoute[] {
|
||||
@ -300,6 +300,10 @@ class AngularCompilerProgram implements Program {
|
||||
}
|
||||
}
|
||||
this.emittedSourceFiles = emittedSourceFiles;
|
||||
// translate the diagnostics in the emitResult as well.
|
||||
const translatedEmitDiags = translateDiagnostics(this.hostAdapter, emitResult.diagnostics);
|
||||
emitResult.diagnostics = translatedEmitDiags.ts.concat(
|
||||
this.structuralDiagnostics.concat(translatedEmitDiags.ng).map(ngToTsDiagnostic));
|
||||
|
||||
if (!outSrcMapping.length) {
|
||||
// if no files were emitted by TypeScript, also don't emit .json files
|
||||
@ -386,7 +390,7 @@ class AngularCompilerProgram implements Program {
|
||||
customTransformers?: CustomTransformers): ts.CustomTransformers {
|
||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
if (!this.options.disableExpressionLowering) {
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache, this.tsProgram));
|
||||
}
|
||||
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
|
||||
if (customTransformers && customTransformers.beforeTs) {
|
||||
@ -400,14 +404,13 @@ class AngularCompilerProgram implements Program {
|
||||
if (this._analyzedModules) {
|
||||
return;
|
||||
}
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
let analyzedModules: NgAnalyzedModules|null;
|
||||
try {
|
||||
analyzedModules = this.compiler.loadFilesSync(sourceFiles);
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
const analyzedModules = this.compiler.loadFilesSync(sourceFiles);
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
} catch (e) {
|
||||
analyzedModules = this.catchAnalysisError(e);
|
||||
this._createProgramOnError(e);
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
}
|
||||
|
||||
private _createCompiler() {
|
||||
@ -422,14 +425,15 @@ class AngularCompilerProgram implements Program {
|
||||
this.oldProgramLibrarySummaries);
|
||||
const aotOptions = getAotCompilerOptions(this.options);
|
||||
this._structuralDiagnostics = [];
|
||||
const errorCollector = (err: any) => {
|
||||
this._structuralDiagnostics !.push({
|
||||
messageText: err.toString(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
};
|
||||
const errorCollector =
|
||||
(this.options.collectAllErrors || this.options.fullTemplateTypeCheck) ? (err: any) => {
|
||||
this._structuralDiagnostics !.push({
|
||||
messageText: err.toString(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
} : undefined;
|
||||
this._compiler = createAotCompiler(this._hostAdapter, aotOptions, errorCollector).compiler;
|
||||
}
|
||||
|
||||
@ -454,7 +458,7 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
let rootNames = this.rootNames;
|
||||
if (this.options.generateCodeForLibraries !== false) {
|
||||
// if we should generateCodeForLibraries, enver include
|
||||
// if we should generateCodeForLibraries, never include
|
||||
// generated files in the program as otherwise we will
|
||||
// ovewrite them and typescript will report the error
|
||||
// TS5055: Cannot write file ... because it would overwrite input file.
|
||||
@ -479,23 +483,21 @@ class AngularCompilerProgram implements Program {
|
||||
}
|
||||
|
||||
private _updateProgramWithTypeCheckStubs(
|
||||
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules|null, rootNames: string[]) {
|
||||
this._analyzedModules = analyzedModules || emptyModules;
|
||||
if (analyzedModules) {
|
||||
tmpProgram.getSourceFiles().forEach(sf => {
|
||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
||||
if (generate) {
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||
// for .ngfactory.ts files.
|
||||
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||
if (genFile) {
|
||||
this.hostAdapter.updateGeneratedFile(genFile);
|
||||
}
|
||||
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules, rootNames: string[]) {
|
||||
this._analyzedModules = analyzedModules;
|
||||
tmpProgram.getSourceFiles().forEach(sf => {
|
||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
||||
if (generate) {
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||
// for .ngfactory.ts files.
|
||||
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||
if (genFile) {
|
||||
this.hostAdapter.updateGeneratedFile(genFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
this._tsProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, tmpProgram);
|
||||
// Note: the new ts program should be completely reusable by TypeScript as:
|
||||
// - we cache all the files in the hostAdapter
|
||||
@ -506,27 +508,37 @@ class AngularCompilerProgram implements Program {
|
||||
}
|
||||
}
|
||||
|
||||
private catchAnalysisError(e: any): NgAnalyzedModules|null {
|
||||
private _createProgramOnError(e: any) {
|
||||
// Still fill the analyzedModules and the tsProgram
|
||||
// so that we don't cause other errors for users who e.g. want to emit the ngProgram.
|
||||
this._analyzedModules = emptyModules;
|
||||
this.oldTsProgram = undefined;
|
||||
this._hostAdapter.isSourceFile = () => false;
|
||||
this._tsProgram = ts.createProgram(this.rootNames, this.options, this.hostAdapter);
|
||||
if (isSyntaxError(e)) {
|
||||
const parserErrors = getParseErrors(e);
|
||||
if (parserErrors && parserErrors.length) {
|
||||
this._structuralDiagnostics =
|
||||
parserErrors.map<Diagnostic>(e => ({
|
||||
messageText: e.contextualMessage(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
span: e.span,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}));
|
||||
this._structuralDiagnostics = [
|
||||
...(this._structuralDiagnostics || []),
|
||||
...parserErrors.map<Diagnostic>(e => ({
|
||||
messageText: e.contextualMessage(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
span: e.span,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}))
|
||||
];
|
||||
} else {
|
||||
this._structuralDiagnostics = [{
|
||||
messageText: e.message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}];
|
||||
this._structuralDiagnostics = [
|
||||
...(this._structuralDiagnostics || []), {
|
||||
messageText: e.message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}
|
||||
];
|
||||
}
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -702,6 +714,10 @@ function getNgOptionDiagnostics(options: CompilerOptions): Diagnostic[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function normalizeSeparators(path: string): string {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that can adjust a path from source path to out path,
|
||||
* based on an existing mapping from source to out path.
|
||||
@ -723,18 +739,19 @@ export function createSrcToOutPathMapper(
|
||||
} = path): (srcFileName: string) => string {
|
||||
let srcToOutPath: (srcFileName: string) => string;
|
||||
if (outDir) {
|
||||
let path: {} = {}; // Ensure we error if we use `path` instead of `host`.
|
||||
if (sampleSrcFileName == null || sampleOutFileName == null) {
|
||||
throw new Error(`Can't calculate the rootDir without a sample srcFileName / outFileName. `);
|
||||
}
|
||||
const srcFileDir = host.dirname(sampleSrcFileName).replace(/\\/g, '/');
|
||||
const outFileDir = host.dirname(sampleOutFileName).replace(/\\/g, '/');
|
||||
const srcFileDir = normalizeSeparators(host.dirname(sampleSrcFileName));
|
||||
const outFileDir = normalizeSeparators(host.dirname(sampleOutFileName));
|
||||
if (srcFileDir === outFileDir) {
|
||||
return (srcFileName) => srcFileName;
|
||||
}
|
||||
// calculate the common suffix, stopping
|
||||
// at `outDir`.
|
||||
const srcDirParts = srcFileDir.split('/');
|
||||
const outDirParts = path.relative(outDir, outFileDir).split('/');
|
||||
const outDirParts = normalizeSeparators(host.relative(outDir, outFileDir)).split('/');
|
||||
let i = 0;
|
||||
while (i < Math.min(srcDirParts.length, outDirParts.length) &&
|
||||
srcDirParts[srcDirParts.length - 1 - i] === outDirParts[outDirParts.length - 1 - i])
|
||||
@ -750,7 +767,7 @@ export function createSrcToOutPathMapper(
|
||||
export function i18nExtract(
|
||||
formatName: string | null, outFile: string | null, host: ts.CompilerHost,
|
||||
options: CompilerOptions, bundle: MessageBundle): string[] {
|
||||
formatName = formatName || 'null';
|
||||
formatName = formatName || 'xlf';
|
||||
// Checks the format and returns the extension
|
||||
const ext = i18nGetExtension(formatName);
|
||||
const content = i18nSerialize(bundle, formatName, options);
|
||||
@ -784,7 +801,7 @@ export function i18nSerialize(
|
||||
}
|
||||
|
||||
export function i18nGetExtension(formatName: string): string {
|
||||
const format = (formatName || 'xlf').toLowerCase();
|
||||
const format = formatName.toLowerCase();
|
||||
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
|
@ -51,3 +51,29 @@ function pathStartsWithPrefix(prefix: string, fullPath: string): string|null {
|
||||
const rel = path.relative(prefix, fullPath);
|
||||
return rel.startsWith('..') ? null : rel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ng.Diagnostic into a ts.Diagnostic.
|
||||
* This looses some information, and also uses an incomplete object as `file`.
|
||||
*
|
||||
* I.e. only use this where the API allows only a ts.Diagnostic.
|
||||
*/
|
||||
export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic {
|
||||
let file: ts.SourceFile|undefined;
|
||||
let start: number|undefined;
|
||||
let length: number|undefined;
|
||||
if (ng.span) {
|
||||
// Note: We can't use a real ts.SourceFile,
|
||||
// but we can at least mirror the properties `fileName` and `text`, which
|
||||
// are mostly used for error reporting.
|
||||
file = { fileName: ng.span.start.file.url, text: ng.span.start.file.content } as ts.SourceFile;
|
||||
start = ng.span.start.offset;
|
||||
length = ng.span.end.offset - start;
|
||||
}
|
||||
return {
|
||||
file,
|
||||
messageText: ng.messageText,
|
||||
category: ng.category,
|
||||
code: ng.code, start, length,
|
||||
};
|
||||
}
|
||||
|
@ -502,29 +502,69 @@ describe('ngc transformer command-line', () => {
|
||||
it('should add metadata as decorators', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "decorators"
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('aclass.ts', `export class AClass {}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AClass} from './aclass';
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
fn(p: any) {}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`);
|
||||
@NgModule({declarations: []})
|
||||
export class MyModule {
|
||||
constructor(importedClass: AClass) {}
|
||||
}
|
||||
`);
|
||||
|
||||
const exitCode = main(['-p', basePath], errorSpy);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('MyComp = __decorate([');
|
||||
expect(mymoduleSource).toContain('MyModule = __decorate([');
|
||||
expect(mymoduleSource).toContain(`import { AClass } from './aclass';`);
|
||||
expect(mymoduleSource).toContain(`__metadata("design:paramtypes", [AClass])`);
|
||||
});
|
||||
|
||||
it('should add metadata as static fields', () => {
|
||||
// Note: Don't specify emitDecoratorMetadata here on purpose,
|
||||
// as regression test for https://github.com/angular/angular/issues/19916.
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "static fields"
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('aclass.ts', `export class AClass {}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AClass} from './aclass';
|
||||
|
||||
@NgModule({declarations: []})
|
||||
export class MyModule {
|
||||
constructor(importedClass: AClass) {}
|
||||
}
|
||||
`);
|
||||
|
||||
const exitCode = main(['-p', basePath], errorSpy);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).not.toContain('__decorate');
|
||||
expect(mymoduleSource).toContain('args: [{ declarations: [] },] }');
|
||||
expect(mymoduleSource).not.toContain(`__metadata`);
|
||||
expect(mymoduleSource).toContain(`import { AClass } from './aclass';`);
|
||||
expect(mymoduleSource).toContain(`{ type: AClass, }`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1391,5 +1431,73 @@ describe('ngc transformer command-line', () => {
|
||||
main(['-p', path.join(basePath, 'src/tsconfig.json')], message => messages.push(message));
|
||||
expect(exitCode).toBe(0, 'Compile failed unexpectedly.\n ' + messages.join('\n '));
|
||||
});
|
||||
|
||||
it('should emit all structural errors', () => {
|
||||
write('src/tsconfig.json', `{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"files": ["test-module.ts"]
|
||||
}`);
|
||||
write('src/lib/indirect2.ts', `
|
||||
declare var f: any;
|
||||
export const t2 = f\`<p>hello</p>\`;
|
||||
`);
|
||||
write('src/lib/indirect1.ts', `
|
||||
import {t2} from './indirect2';
|
||||
export const t1 = t2 + ' ';
|
||||
`);
|
||||
write('src/lib/test.component.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
import {t1} from './indirect1';
|
||||
|
||||
@Component({
|
||||
template: t1
|
||||
})
|
||||
export class TestComponent {}
|
||||
`);
|
||||
write('src/test-module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {TestComponent} from './lib/test.component';
|
||||
|
||||
@NgModule({declarations: [TestComponent]})
|
||||
export class TestModule {}
|
||||
`);
|
||||
const messages: string[] = [];
|
||||
const exitCode =
|
||||
main(['-p', path.join(basePath, 'src/tsconfig.json')], message => messages.push(message));
|
||||
expect(exitCode).toBe(1, 'Compile was expected to fail');
|
||||
expect(messages[0]).toContain(['Tagged template expressions are not supported in metadata']);
|
||||
});
|
||||
|
||||
it('should allow using 2 classes with the same name in declarations with noEmitOnError=true',
|
||||
() => {
|
||||
write('src/tsconfig.json', `{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"noEmitOnError": true
|
||||
},
|
||||
"files": ["test-module.ts"]
|
||||
}`);
|
||||
function writeComp(fileName: string) {
|
||||
write(fileName, `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp', template: ''})
|
||||
export class TestComponent {}
|
||||
`);
|
||||
}
|
||||
writeComp('src/comp1.ts');
|
||||
writeComp('src/comp2.ts');
|
||||
write('src/test-module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {TestComponent as Comp1} from './comp1';
|
||||
import {TestComponent as Comp2} from './comp2';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Comp1, Comp2],
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(main(['-p', path.join(basePath, 'src/tsconfig.json')])).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -105,6 +105,47 @@ describe('perform watch', () => {
|
||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5);
|
||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5);
|
||||
});
|
||||
|
||||
it('should recover from static analysis errors', () => {
|
||||
const config = createConfig();
|
||||
const host = new MockWatchHost(config);
|
||||
|
||||
const okFileContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class MyModule {}
|
||||
`;
|
||||
const errorFileContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => (1===1 ? null as any : null as any))
|
||||
export class MyModule {}
|
||||
`;
|
||||
const indexTsPath = path.resolve(testSupport.basePath, 'src', 'index.ts');
|
||||
|
||||
testSupport.write(indexTsPath, okFileContent);
|
||||
|
||||
performWatchCompilation(host);
|
||||
expectNoDiagnostics(config.options, host.diagnostics);
|
||||
|
||||
// Do it multiple times as the watch mode switches internal modes.
|
||||
// E.g. from regular compile to using summaries, ...
|
||||
for (let i = 0; i < 3; i++) {
|
||||
host.diagnostics = [];
|
||||
testSupport.write(indexTsPath, okFileContent);
|
||||
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
|
||||
expectNoDiagnostics(config.options, host.diagnostics);
|
||||
|
||||
host.diagnostics = [];
|
||||
testSupport.write(indexTsPath, errorFileContent);
|
||||
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
|
||||
|
||||
const errDiags = host.diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
|
||||
expect(errDiags.length).toBe(1);
|
||||
expect(errDiags[0].messageText).toContain('Function calls are not supported.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function createModuleAndCompSource(prefix: string, template: string = prefix + 'template') {
|
||||
@ -122,7 +163,8 @@ function createModuleAndCompSource(prefix: string, template: string = prefix + '
|
||||
}
|
||||
|
||||
class MockWatchHost {
|
||||
timeoutListeners: Array<(() => void)|null> = [];
|
||||
nextTimeoutListenerId = 1;
|
||||
timeoutListeners: {[id: string]: (() => void)} = {};
|
||||
fileChangeListeners: Array<((event: FileChangeEvent, fileName: string) => void)|null> = [];
|
||||
diagnostics: ng.Diagnostics = [];
|
||||
constructor(public config: ng.ParsedConfiguration) {}
|
||||
@ -141,16 +183,16 @@ class MockWatchHost {
|
||||
close: () => this.fileChangeListeners[id] = null,
|
||||
};
|
||||
}
|
||||
setTimeout(callback: () => void, ms: number): any {
|
||||
const id = this.timeoutListeners.length;
|
||||
this.timeoutListeners.push(callback);
|
||||
setTimeout(callback: () => void): any {
|
||||
const id = this.nextTimeoutListenerId++;
|
||||
this.timeoutListeners[id] = callback;
|
||||
return id;
|
||||
}
|
||||
clearTimeout(timeoutId: any): void { this.timeoutListeners[timeoutId] = null; }
|
||||
clearTimeout(timeoutId: any): void { delete this.timeoutListeners[timeoutId]; }
|
||||
flushTimeouts() {
|
||||
this.timeoutListeners.forEach(cb => {
|
||||
if (cb) cb();
|
||||
});
|
||||
const listeners = this.timeoutListeners;
|
||||
this.timeoutListeners = {};
|
||||
Object.keys(listeners).forEach(id => listeners[id]());
|
||||
}
|
||||
triggerFileChange(event: FileChangeEvent, fileName: string) {
|
||||
this.fileChangeListeners.forEach(listener => {
|
||||
|
@ -64,10 +64,10 @@ export function setup(): TestSupport {
|
||||
function write(fileName: string, content: string) {
|
||||
const dir = path.dirname(fileName);
|
||||
if (dir != '.') {
|
||||
const newDir = path.join(basePath, dir);
|
||||
const newDir = path.resolve(basePath, dir);
|
||||
if (!fs.existsSync(newDir)) fs.mkdirSync(newDir);
|
||||
}
|
||||
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
|
||||
fs.writeFileSync(path.resolve(basePath, fileName), content, {encoding: 'utf-8'});
|
||||
}
|
||||
|
||||
function writeFiles(...mockDirs: {[fileName: string]: string}[]) {
|
||||
|
@ -181,13 +181,15 @@ function convert(annotatedSource: string) {
|
||||
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
||||
const moduleSourceFile = program.getSourceFile(fileName);
|
||||
const transformers: ts.CustomTransformers = {
|
||||
before: [getExpressionLoweringTransformFactory({
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||
return requests;
|
||||
} else {return new Map();}
|
||||
}
|
||||
})]
|
||||
before: [getExpressionLoweringTransformFactory(
|
||||
{
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||
return requests;
|
||||
} else {return new Map();}
|
||||
}
|
||||
},
|
||||
program)]
|
||||
};
|
||||
let result: string = '';
|
||||
const emitResult = program.emit(
|
||||
|
@ -11,7 +11,8 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost, LazyRoute} from '../../src/transformers/api';
|
||||
import {formatDiagnostics} from '../../src/perform_compile';
|
||||
import {CompilerHost, EmitFlags, LazyRoute} from '../../src/transformers/api';
|
||||
import {createSrcToOutPathMapper} from '../../src/transformers/program';
|
||||
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
|
||||
import {TestSupport, expectNoDiagnosticsInProgram, setup} from '../test_support';
|
||||
@ -309,11 +310,35 @@ describe('ng program', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should typecheck templates even if skipTemplateCodegen is set', () => {
|
||||
it('should not typecheck templates if skipTemplateCodegen is set but fullTemplateTypeCheck is not',
|
||||
() => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => {if (1==1) return null as any;})
|
||||
export class SomeClassWithInvalidMetadata {}
|
||||
`,
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({skipTemplateCodegen: true});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
const emitResult = program.emit({emitFlags: EmitFlags.All});
|
||||
expect(emitResult.diagnostics.length).toBe(0);
|
||||
|
||||
testSupport.shouldExist('built/src/main.metadata.json');
|
||||
});
|
||||
|
||||
it('should typecheck templates if skipTemplateCodegen and fullTemplateTypeCheck is set', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': createModuleAndCompSource('main', `{{nonExistent}}`),
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({skipTemplateCodegen: true});
|
||||
const options = testSupport.createCompilerOptions({
|
||||
skipTemplateCodegen: true,
|
||||
fullTemplateTypeCheck: true,
|
||||
});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
@ -554,8 +579,8 @@ describe('ng program', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function createProgram(rootNames: string[]) {
|
||||
const options = testSupport.createCompilerOptions();
|
||||
function createProgram(rootNames: string[], overrideOptions: ng.CompilerOptions = {}) {
|
||||
const options = testSupport.createCompilerOptions(overrideOptions);
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host});
|
||||
@ -593,6 +618,34 @@ describe('ng program', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should emit correctly after listing lazyRoutes', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot([{loadChildren: './lazy/lazy#LazyModule'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/lazy/lazy.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule {}
|
||||
`,
|
||||
});
|
||||
const {program, options} = createProgram(['src/main.ts', 'src/lazy/lazy.ts']);
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
program.listLazyRoutes();
|
||||
program.emit();
|
||||
|
||||
const lazyNgFactory =
|
||||
fs.readFileSync(path.resolve(testSupport.basePath, 'built/src/lazy/lazy.ngfactory.js'));
|
||||
expect(lazyNgFactory).toContain('import * as i1 from "./lazy";');
|
||||
});
|
||||
|
||||
it('should list lazyRoutes given an entryRoute recursively', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, options} = createProgram(['src/main.ts']);
|
||||
@ -797,7 +850,7 @@ describe('ng program', () => {
|
||||
export class ChildModule {}
|
||||
`,
|
||||
});
|
||||
const program = createProgram(['src/main.ts']).program;
|
||||
const program = createProgram(['src/main.ts'], {collectAllErrors: true}).program;
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
@ -806,4 +859,96 @@ describe('ng program', () => {
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report errors for ts and ng errors on emit with noEmitOnError=true', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
// Ts error
|
||||
let x: string = 1;
|
||||
|
||||
// Ng error
|
||||
@Component({selector: 'comp', templateUrl: './main.html'})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`,
|
||||
'src/main.html': '{{nonExistent}}'
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({noEmitOnError: true});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program1 = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
const errorDiags =
|
||||
program1.emit().diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
|
||||
expect(formatDiagnostics(errorDiags))
|
||||
.toContain(`src/main.ts(5,13): error TS2322: Type '1' is not assignable to type 'string'.`);
|
||||
expect(formatDiagnostics(errorDiags))
|
||||
.toContain(
|
||||
`src/main.html(1,1): error TS100: Property 'nonExistent' does not exist on type 'MyComp'.`);
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
const fileWithStructuralError = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => (1===1 ? null as any : null as any))
|
||||
export class MyModule {}
|
||||
`;
|
||||
const fileWithGoodContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class MyModule {}
|
||||
`;
|
||||
|
||||
it('should not throw on structural errors but collect them', () => {
|
||||
testSupport.write('src/index.ts', fileWithStructuralError);
|
||||
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
|
||||
|
||||
const structuralErrors = program.getNgStructuralDiagnostics();
|
||||
expect(structuralErrors.length).toBe(1);
|
||||
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
|
||||
});
|
||||
|
||||
it('should not throw on structural errors but collect them (loadNgStructureAsync)', (done) => {
|
||||
testSupport.write('src/index.ts', fileWithStructuralError);
|
||||
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
|
||||
program.loadNgStructureAsync().then(() => {
|
||||
const structuralErrors = program.getNgStructuralDiagnostics();
|
||||
expect(structuralErrors.length).toBe(1);
|
||||
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to use a program with structural errors as oldProgram', () => {
|
||||
testSupport.write('src/index.ts', fileWithStructuralError);
|
||||
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program1 = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
|
||||
expect(program1.getNgStructuralDiagnostics().length).toBe(1);
|
||||
|
||||
testSupport.write('src/index.ts', fileWithGoodContent);
|
||||
const program2 = ng.createProgram({
|
||||
rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')],
|
||||
options,
|
||||
host,
|
||||
oldProgram: program1
|
||||
});
|
||||
expectNoDiagnosticsInProgram(options, program2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -193,6 +193,7 @@ export class AotCompiler {
|
||||
|
||||
private _createNgFactoryStub(
|
||||
outputCtx: OutputContext, file: NgAnalyzedFile, emitFlags: StubEmitFlags) {
|
||||
let componentId = 0;
|
||||
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
||||
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
||||
// so we don't change the .ngfactory file too much when adding the typecheck block.
|
||||
@ -230,12 +231,14 @@ export class AotCompiler {
|
||||
if (!compMeta.isComponent) {
|
||||
return;
|
||||
}
|
||||
componentId++;
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, ngModuleMeta, this._metadataResolver.getHostComponentMetadata(compMeta),
|
||||
[compMeta.type], externalReferenceVars);
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, ngModuleMeta, compMeta, ngModuleMeta.transitiveModule.directives,
|
||||
outputCtx, `${compMeta.type.reference.name}_Host_${componentId}`, ngModuleMeta,
|
||||
this._metadataResolver.getHostComponentMetadata(compMeta), [compMeta.type],
|
||||
externalReferenceVars);
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, `${compMeta.type.reference.name}_${componentId}`, ngModuleMeta, compMeta,
|
||||
ngModuleMeta.transitiveModule.directives, externalReferenceVars);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -246,12 +249,13 @@ export class AotCompiler {
|
||||
}
|
||||
|
||||
private _createTypeCheckBlock(
|
||||
ctx: OutputContext, moduleMeta: CompileNgModuleMetadata, compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileIdentifierMetadata[], externalReferenceVars: Map<any, string>) {
|
||||
ctx: OutputContext, componentId: string, moduleMeta: CompileNgModuleMetadata,
|
||||
compMeta: CompileDirectiveMetadata, directives: CompileIdentifierMetadata[],
|
||||
externalReferenceVars: Map<any, string>) {
|
||||
const {template: parsedTemplate, pipes: usedPipes} =
|
||||
this._parseTemplate(compMeta, moduleMeta, directives);
|
||||
ctx.statements.push(...this._typeCheckCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
||||
componentId, compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
||||
}
|
||||
|
||||
emitMessageBundle(analyzeResult: NgAnalyzedModules, locale: string|null): MessageBundle {
|
||||
|
@ -54,7 +54,7 @@ export function createAotUrlResolver(host: {
|
||||
*/
|
||||
export function createAotCompiler(
|
||||
compilerHost: AotCompilerHost, options: AotCompilerOptions,
|
||||
errorCollector: (error: any, type?: any) =>
|
||||
errorCollector?: (error: any, type?: any) =>
|
||||
void): {compiler: AotCompiler, reflector: StaticReflector} {
|
||||
let translations: string = options.translations || '';
|
||||
|
||||
|
@ -80,8 +80,10 @@ export class StaticReflector implements CompileReflector {
|
||||
const refSymbol =
|
||||
this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !, containingFile);
|
||||
const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
||||
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
||||
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
||||
if (!containingFile) {
|
||||
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
||||
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
||||
}
|
||||
return declarationSymbol;
|
||||
}
|
||||
|
||||
@ -750,7 +752,7 @@ class PopulatedScope extends BindingScope {
|
||||
}
|
||||
|
||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
||||
const result = new Error(message);
|
||||
const result = syntaxError(message);
|
||||
(result as any).fileName = fileName;
|
||||
(result as any).line = line;
|
||||
(result as any).column = column;
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {AotCompilerOptions} from '../aot/compiler_options';
|
||||
import {StaticReflector} from '../aot/static_reflector';
|
||||
import {StaticSymbol} from '../aot/static_symbol';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeSummary, viewClassName} from '../compile_metadata';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata';
|
||||
import {BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
@ -33,7 +33,8 @@ export class TypeCheckCompiler {
|
||||
* and also violate the point above.
|
||||
*/
|
||||
compileComponent(
|
||||
component: CompileDirectiveMetadata, template: TemplateAst[], usedPipes: CompilePipeSummary[],
|
||||
componentId: string, component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
usedPipes: CompilePipeSummary[],
|
||||
externalReferenceVars: Map<StaticSymbol, string>): o.Statement[] {
|
||||
const pipes = new Map<string, StaticSymbol>();
|
||||
usedPipes.forEach(p => pipes.set(p.name, p.type.reference));
|
||||
@ -48,7 +49,7 @@ export class TypeCheckCompiler {
|
||||
const visitor = viewBuilderFactory(null);
|
||||
visitor.visitAll([], template);
|
||||
|
||||
return visitor.build();
|
||||
return visitor.build(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,8 +104,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
templateVisitAll(this, astNodes);
|
||||
}
|
||||
|
||||
build(targetStatements: o.Statement[] = []): o.Statement[] {
|
||||
this.children.forEach((child) => child.build(targetStatements));
|
||||
build(componentId: string, targetStatements: o.Statement[] = []): o.Statement[] {
|
||||
this.children.forEach((child) => child.build(componentId, targetStatements));
|
||||
const viewStmts: o.Statement[] =
|
||||
[o.variable(DYNAMIC_VAR_NAME).set(o.NULL_EXPR).toDeclStmt(o.DYNAMIC_TYPE)];
|
||||
let bindingCount = 0;
|
||||
@ -128,7 +129,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
|
||||
});
|
||||
|
||||
const viewName = `_View_${this.component.name}_${this.embeddedViewIndex}`;
|
||||
const viewName = `_View_${componentId}_${this.embeddedViewIndex}`;
|
||||
const viewFactory = new o.DeclareFunctionStmt(viewName, [], viewStmts);
|
||||
targetStatements.push(viewFactory);
|
||||
return targetStatements;
|
||||
|
@ -1062,6 +1062,22 @@ describe('StaticReflector', () => {
|
||||
.useValue)
|
||||
.toEqual({path: 'foo', data: {e: 1}});
|
||||
});
|
||||
|
||||
describe('resolveExternalReference', () => {
|
||||
it('should register modules names in the StaticSymbolResolver if no containingFile is given',
|
||||
() => {
|
||||
init({
|
||||
'/tmp/root.ts': ``,
|
||||
'/tmp/a.ts': `export const x = 1;`,
|
||||
});
|
||||
let symbol =
|
||||
reflector.resolveExternalReference({moduleName: './a', name: 'x'}, '/tmp/root.ts');
|
||||
expect(symbolResolver.getKnownModuleName(symbol.filePath)).toBeFalsy();
|
||||
|
||||
symbol = reflector.resolveExternalReference({moduleName: 'a', name: 'x'});
|
||||
expect(symbolResolver.getKnownModuleName(symbol.filePath)).toBe('a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||
|
@ -106,8 +106,7 @@ export class NgswCommChannel {
|
||||
this.registration = <Observable<ServiceWorkerRegistration>>(
|
||||
op_switchMap.call(this.worker, () => serviceWorker.getRegistration()));
|
||||
|
||||
const rawEvents = <Observable<MessageEvent>>(op_switchMap.call(
|
||||
this.registration, (reg: ServiceWorkerRegistration) => obs_fromEvent(reg, 'message')));
|
||||
const rawEvents = obs_fromEvent(serviceWorker, 'message');
|
||||
|
||||
const rawEventPayload =
|
||||
<Observable<Object>>(op_map.call(rawEvents, (event: MessageEvent) => event.data));
|
||||
|
@ -30,8 +30,10 @@ export function ngswAppInitializer(
|
||||
op_filter.call(app.isStable, (stable: boolean) => !!stable) as Observable<boolean>;
|
||||
const isStable = op_take.call(onStable, 1) as Observable<boolean>;
|
||||
const whenStable = op_toPromise.call(isStable) as Promise<boolean>;
|
||||
return whenStable.then(() => navigator.serviceWorker.register(script, options))
|
||||
.then(() => undefined) as Promise<void>;
|
||||
|
||||
// Don't return the Promise, as that will block the application until the SW is registered, and
|
||||
// cause a crash if the SW registration fails.
|
||||
whenStable.then(() => navigator.serviceWorker.register(script, options));
|
||||
};
|
||||
return initializer;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ export class SwPush {
|
||||
|
||||
const workerDrivenSubscriptions = <Observable<PushSubscription|null>>(op_switchMap.call(
|
||||
this.pushManager, (pm: PushManager) => pm.getSubscription().then(sub => { return sub; })));
|
||||
this.subscription = obs_merge.call(workerDrivenSubscriptions, this.subscriptionChanges);
|
||||
this.subscription = obs_merge(workerDrivenSubscriptions, this.subscriptionChanges);
|
||||
}
|
||||
|
||||
requestSubscription(options: {serverPublicKey: string}): Promise<PushSubscription> {
|
||||
|
@ -44,11 +44,9 @@ export function main() {
|
||||
});
|
||||
describe('SwPush', () => {
|
||||
let push: SwPush;
|
||||
let reg: MockServiceWorkerRegistration;
|
||||
beforeEach((done: DoneFn) => {
|
||||
beforeEach(() => {
|
||||
push = new SwPush(comm);
|
||||
mock.setupSw();
|
||||
mock.mockRegistration.then(r => reg = r).then(() => done());
|
||||
});
|
||||
it('receives push messages', (done: DoneFn) => {
|
||||
push.messages.subscribe(msg => {
|
||||
@ -57,7 +55,7 @@ export function main() {
|
||||
});
|
||||
done();
|
||||
});
|
||||
reg.sendMessage({
|
||||
mock.sendMessage({
|
||||
type: 'PUSH',
|
||||
data: {
|
||||
message: 'this was a push message',
|
||||
@ -76,11 +74,9 @@ export function main() {
|
||||
});
|
||||
describe('SwUpdate', () => {
|
||||
let update: SwUpdate;
|
||||
let reg: MockServiceWorkerRegistration;
|
||||
beforeEach((done: DoneFn) => {
|
||||
beforeEach(() => {
|
||||
update = new SwUpdate(comm);
|
||||
mock.setupSw();
|
||||
mock.mockRegistration.then(r => reg = r).then(() => done());
|
||||
});
|
||||
it('processes update availability notifications when sent', (done: DoneFn) => {
|
||||
update.available.subscribe(event => {
|
||||
@ -89,7 +85,7 @@ export function main() {
|
||||
expect(event.type).toEqual('UPDATE_AVAILABLE');
|
||||
done();
|
||||
});
|
||||
reg.sendMessage({
|
||||
mock.sendMessage({
|
||||
type: 'UPDATE_AVAILABLE',
|
||||
current: {
|
||||
version: 'A',
|
||||
@ -106,7 +102,7 @@ export function main() {
|
||||
expect(event.type).toEqual('UPDATE_ACTIVATED');
|
||||
done();
|
||||
});
|
||||
reg.sendMessage({
|
||||
mock.sendMessage({
|
||||
type: 'UPDATE_ACTIVATED',
|
||||
previous: {
|
||||
version: 'A',
|
||||
@ -119,7 +115,7 @@ export function main() {
|
||||
it('activates updates when requested', (done: DoneFn) => {
|
||||
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
||||
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
||||
reg.sendMessage({
|
||||
mock.sendMessage({
|
||||
type: 'STATUS',
|
||||
nonce: msg.statusNonce,
|
||||
status: true,
|
||||
@ -130,7 +126,7 @@ export function main() {
|
||||
it('reports activation failure when requested', (done: DoneFn) => {
|
||||
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
||||
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
||||
reg.sendMessage({
|
||||
mock.sendMessage({
|
||||
type: 'STATUS',
|
||||
nonce: msg.statusNonce,
|
||||
status: false,
|
||||
|
@ -85,7 +85,7 @@ export function main() {
|
||||
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
||||
|
||||
scope.clients.add('default');
|
||||
scope.clients.getMock('default') !.queue.subscribe(msg => { reg.sendMessage(msg); });
|
||||
scope.clients.getMock('default') !.queue.subscribe(msg => { mock.sendMessage(msg); });
|
||||
|
||||
mock.messages.subscribe(msg => { scope.handleMessage(msg, 'default'); });
|
||||
|
||||
|
@ -10,17 +10,26 @@ import {Subject} from 'rxjs/Subject';
|
||||
|
||||
export class MockServiceWorkerContainer {
|
||||
private onControllerChange: Function[] = [];
|
||||
private onMessage: Function[] = [];
|
||||
private registration: MockServiceWorkerRegistration|null = null;
|
||||
controller: MockServiceWorker|null = null;
|
||||
|
||||
messages = new Subject();
|
||||
|
||||
addEventListener(event: 'controllerchange', handler: Function) {
|
||||
this.onControllerChange.push(handler);
|
||||
addEventListener(event: 'controllerchange'|'message', handler: Function) {
|
||||
if (event === 'controllerchange') {
|
||||
this.onControllerChange.push(handler);
|
||||
} else if (event === 'message') {
|
||||
this.onMessage.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListener(event: 'controllerchange', handler: Function) {
|
||||
this.onControllerChange = this.onControllerChange.filter(h => h !== handler);
|
||||
if (event === 'controllerchange') {
|
||||
this.onControllerChange = this.onControllerChange.filter(h => h !== handler);
|
||||
} else if (event === 'message') {
|
||||
this.onMessage = this.onMessage.filter(h => h !== handler);
|
||||
}
|
||||
}
|
||||
|
||||
async register(url: string): Promise<void> { return; }
|
||||
@ -36,6 +45,12 @@ export class MockServiceWorkerContainer {
|
||||
get mockRegistration(): Promise<MockServiceWorkerRegistration> {
|
||||
return Promise.resolve(this.registration !);
|
||||
}
|
||||
|
||||
sendMessage(value: Object): void {
|
||||
this.onMessage.forEach(onMessage => onMessage({
|
||||
data: value,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export class MockServiceWorker {
|
||||
@ -44,21 +59,4 @@ export class MockServiceWorker {
|
||||
postMessage(value: Object) { this.mock.messages.next(value); }
|
||||
}
|
||||
|
||||
export class MockServiceWorkerRegistration {
|
||||
private onMessage: Function[] = [];
|
||||
messages: Object[] = [];
|
||||
|
||||
constructor() {}
|
||||
|
||||
addEventListener(event: 'message', handler: Function) { this.onMessage.push(handler); }
|
||||
|
||||
removeEventListener(event: 'message', handler: Function) {
|
||||
this.onMessage = this.onMessage.filter(h => h !== handler);
|
||||
}
|
||||
|
||||
sendMessage(value: Object): void {
|
||||
this.onMessage.forEach(onMessage => onMessage({
|
||||
data: value,
|
||||
}));
|
||||
}
|
||||
}
|
||||
export class MockServiceWorkerRegistration {}
|
||||
|
@ -6194,9 +6194,9 @@ rx-lite@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||
|
||||
rxjs@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.0.tgz#26d8f3866eb700e247e0728a147c3d628993d812"
|
||||
rxjs@^5.5.2:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3"
|
||||
dependencies:
|
||||
symbol-observable "^1.0.1"
|
||||
|
||||
|
Reference in New Issue
Block a user