Compare commits
19 Commits
ngcontaine
...
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>
|
<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)
|
# [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",
|
"name": "angular-srcs",
|
||||||
"version": "5.0.0-rc.4",
|
"version": "5.0.0-rc.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"branchPattern": "2.0.*",
|
"branchPattern": "2.0.*",
|
||||||
"description": "Angular - a web framework for modern web apps",
|
"description": "Angular - a web framework for modern web apps",
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"reflect-metadata": "^0.1.3",
|
"reflect-metadata": "^0.1.3",
|
||||||
"rxjs": "^5.5.0",
|
"rxjs": "^5.5.2",
|
||||||
"tslib": "^1.7.1",
|
"tslib": "^1.7.1",
|
||||||
"zone.js": "^0.8.12"
|
"zone.js": "^0.8.12"
|
||||||
},
|
},
|
||||||
|
@ -38,10 +38,10 @@ export function translateDiagnostics(host: TypeCheckHost, untranslatedDiagnostic
|
|||||||
source: SOURCE,
|
source: SOURCE,
|
||||||
code: DEFAULT_ERROR_CODE
|
code: DEFAULT_ERROR_CODE
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ts.push(diagnostic);
|
||||||
}
|
}
|
||||||
ts.push(diagnostic);
|
|
||||||
});
|
});
|
||||||
return {ts, ng};
|
return {ts, ng};
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,12 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
|||||||
if (!transformDecorators && !transformTypesToClosure) {
|
if (!transformDecorators && !transformTypesToClosure) {
|
||||||
return undefined;
|
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 = {
|
const tsickleHost: tsickle.TsickleHost = {
|
||||||
shouldSkipTsickleProcessing: (fileName) =>
|
shouldSkipTsickleProcessing: (fileName) =>
|
||||||
/\.d\.ts$/.test(fileName) || GENERATED_FILES.test(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.*
|
// as we only needed this to support Angular CLI 1.5.0 rc.*
|
||||||
const ngProgram = createProgram({
|
const ngProgram = createProgram({
|
||||||
rootNames: options.program.getRootFileNames(),
|
rootNames: options.program.getRootFileNames(),
|
||||||
options: options.angularCompilerOptions,
|
options: {...options.angularCompilerOptions, collectAllErrors: true},
|
||||||
host: options.host
|
host: options.host
|
||||||
});
|
});
|
||||||
const lazyRoutes = ngProgram.listLazyRoutes(options.entryModule);
|
const lazyRoutes = ngProgram.listLazyRoutes(options.entryModule);
|
||||||
|
@ -129,6 +129,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
|||||||
return {close, ready: cb => readyPromise.then(cb), firstCompileResult};
|
return {close, ready: cb => readyPromise.then(cb), firstCompileResult};
|
||||||
|
|
||||||
function cacheEntry(fileName: string): CacheEntry {
|
function cacheEntry(fileName: string): CacheEntry {
|
||||||
|
fileName = path.normalize(fileName);
|
||||||
let entry = fileCache.get(fileName);
|
let entry = fileCache.get(fileName);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
entry = {};
|
entry = {};
|
||||||
@ -191,6 +192,10 @@ export function performWatchCompilation(host: PerformWatchHost):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
ingoreFilesForWatch.clear();
|
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({
|
const compileResult = performCompilation({
|
||||||
rootNames: cachedOptions.rootNames,
|
rootNames: cachedOptions.rootNames,
|
||||||
options: cachedOptions.options,
|
options: cachedOptions.options,
|
||||||
@ -245,7 +250,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
|||||||
if (event === FileChangeEvent.CreateDeleteDir) {
|
if (event === FileChangeEvent.CreateDeleteDir) {
|
||||||
fileCache.clear();
|
fileCache.clear();
|
||||||
} else {
|
} else {
|
||||||
fileCache.delete(fileName);
|
fileCache.delete(path.normalize(fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ingoreFilesForWatch.has(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.
|
* in JIT mode. This is off by default.
|
||||||
*/
|
*/
|
||||||
enableSummariesForJit?: boolean;
|
enableSummariesForJit?: boolean;
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
collectAllErrors?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompilerHost extends ts.CompilerHost {
|
export interface CompilerHost extends ts.CompilerHost {
|
||||||
|
@ -426,6 +426,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSourceFile(filePath: string): boolean {
|
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,
|
// If we have a summary from a previous compilation,
|
||||||
// treat the file never as a source file.
|
// treat the file never as a source file.
|
||||||
if (this.librarySummaries.has(filePath)) {
|
if (this.librarySummaries.has(filePath)) {
|
||||||
|
@ -181,11 +181,13 @@ function createVariableStatementForDeclarations(declarations: Declaration[]): ts
|
|||||||
/* modifiers */ undefined, ts.createVariableDeclarationList(varDecls, ts.NodeFlags.Const));
|
/* modifiers */ undefined, ts.createVariableDeclarationList(varDecls, ts.NodeFlags.Const));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExpressionLoweringTransformFactory(requestsMap: RequestsMap):
|
export function getExpressionLoweringTransformFactory(
|
||||||
(context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
requestsMap: RequestsMap, program: ts.Program): (context: ts.TransformationContext) =>
|
||||||
|
(sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||||
// Return the factory
|
// Return the factory
|
||||||
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile): ts.SourceFile => {
|
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) {
|
if (requests && requests.size) {
|
||||||
return transformSourceFile(sourceFile, requests, context);
|
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 {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
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):
|
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
||||||
ts.Diagnostic[] {
|
ts.Diagnostic[] {
|
||||||
if (sourceFile) {
|
const sourceFiles = sourceFile ? [sourceFile] : this.tsProgram.getSourceFiles();
|
||||||
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
|
||||||
}
|
|
||||||
let diags: ts.Diagnostic[] = [];
|
let diags: ts.Diagnostic[] = [];
|
||||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
sourceFiles.forEach(sf => {
|
||||||
if (!GENERATED_FILES.test(sf.fileName)) {
|
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||||
}
|
}
|
||||||
@ -177,15 +175,17 @@ class AngularCompilerProgram implements Program {
|
|||||||
if (this._analyzedModules) {
|
if (this._analyzedModules) {
|
||||||
throw new Error('Angular structure already loaded');
|
throw new Error('Angular structure already loaded');
|
||||||
}
|
}
|
||||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
return Promise.resolve()
|
||||||
return this.compiler.loadFilesAsync(sourceFiles)
|
.then(() => {
|
||||||
.catch(this.catchAnalysisError.bind(this))
|
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||||
.then(analyzedModules => {
|
return this.compiler.loadFilesAsync(sourceFiles).then(analyzedModules => {
|
||||||
if (this._analyzedModules) {
|
if (this._analyzedModules) {
|
||||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||||
}
|
}
|
||||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(e => this._createProgramOnError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
listLazyRoutes(route?: string): LazyRoute[] {
|
listLazyRoutes(route?: string): LazyRoute[] {
|
||||||
@ -300,6 +300,10 @@ class AngularCompilerProgram implements Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.emittedSourceFiles = emittedSourceFiles;
|
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 (!outSrcMapping.length) {
|
||||||
// if no files were emitted by TypeScript, also don't emit .json files
|
// 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 {
|
customTransformers?: CustomTransformers): ts.CustomTransformers {
|
||||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||||
if (!this.options.disableExpressionLowering) {
|
if (!this.options.disableExpressionLowering) {
|
||||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache, this.tsProgram));
|
||||||
}
|
}
|
||||||
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
|
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
|
||||||
if (customTransformers && customTransformers.beforeTs) {
|
if (customTransformers && customTransformers.beforeTs) {
|
||||||
@ -400,14 +404,13 @@ class AngularCompilerProgram implements Program {
|
|||||||
if (this._analyzedModules) {
|
if (this._analyzedModules) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
|
||||||
let analyzedModules: NgAnalyzedModules|null;
|
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
analyzedModules = this.catchAnalysisError(e);
|
this._createProgramOnError(e);
|
||||||
}
|
}
|
||||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createCompiler() {
|
private _createCompiler() {
|
||||||
@ -422,14 +425,15 @@ class AngularCompilerProgram implements Program {
|
|||||||
this.oldProgramLibrarySummaries);
|
this.oldProgramLibrarySummaries);
|
||||||
const aotOptions = getAotCompilerOptions(this.options);
|
const aotOptions = getAotCompilerOptions(this.options);
|
||||||
this._structuralDiagnostics = [];
|
this._structuralDiagnostics = [];
|
||||||
const errorCollector = (err: any) => {
|
const errorCollector =
|
||||||
this._structuralDiagnostics !.push({
|
(this.options.collectAllErrors || this.options.fullTemplateTypeCheck) ? (err: any) => {
|
||||||
messageText: err.toString(),
|
this._structuralDiagnostics !.push({
|
||||||
category: ts.DiagnosticCategory.Error,
|
messageText: err.toString(),
|
||||||
source: SOURCE,
|
category: ts.DiagnosticCategory.Error,
|
||||||
code: DEFAULT_ERROR_CODE
|
source: SOURCE,
|
||||||
});
|
code: DEFAULT_ERROR_CODE
|
||||||
};
|
});
|
||||||
|
} : undefined;
|
||||||
this._compiler = createAotCompiler(this._hostAdapter, aotOptions, errorCollector).compiler;
|
this._compiler = createAotCompiler(this._hostAdapter, aotOptions, errorCollector).compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +458,7 @@ class AngularCompilerProgram implements Program {
|
|||||||
|
|
||||||
let rootNames = this.rootNames;
|
let rootNames = this.rootNames;
|
||||||
if (this.options.generateCodeForLibraries !== false) {
|
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
|
// generated files in the program as otherwise we will
|
||||||
// ovewrite them and typescript will report the error
|
// ovewrite them and typescript will report the error
|
||||||
// TS5055: Cannot write file ... because it would overwrite input file.
|
// TS5055: Cannot write file ... because it would overwrite input file.
|
||||||
@ -479,23 +483,21 @@ class AngularCompilerProgram implements Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateProgramWithTypeCheckStubs(
|
private _updateProgramWithTypeCheckStubs(
|
||||||
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules|null, rootNames: string[]) {
|
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules, rootNames: string[]) {
|
||||||
this._analyzedModules = analyzedModules || emptyModules;
|
this._analyzedModules = analyzedModules;
|
||||||
if (analyzedModules) {
|
tmpProgram.getSourceFiles().forEach(sf => {
|
||||||
tmpProgram.getSourceFiles().forEach(sf => {
|
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
||||||
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
if (generate) {
|
||||||
if (generate) {
|
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
// for .ngfactory.ts files.
|
||||||
// for .ngfactory.ts files.
|
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||||
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
if (genFile) {
|
||||||
if (genFile) {
|
this.hostAdapter.updateGeneratedFile(genFile);
|
||||||
this.hostAdapter.updateGeneratedFile(genFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
this._tsProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, tmpProgram);
|
this._tsProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, tmpProgram);
|
||||||
// Note: the new ts program should be completely reusable by TypeScript as:
|
// Note: the new ts program should be completely reusable by TypeScript as:
|
||||||
// - we cache all the files in the hostAdapter
|
// - 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)) {
|
if (isSyntaxError(e)) {
|
||||||
const parserErrors = getParseErrors(e);
|
const parserErrors = getParseErrors(e);
|
||||||
if (parserErrors && parserErrors.length) {
|
if (parserErrors && parserErrors.length) {
|
||||||
this._structuralDiagnostics =
|
this._structuralDiagnostics = [
|
||||||
parserErrors.map<Diagnostic>(e => ({
|
...(this._structuralDiagnostics || []),
|
||||||
messageText: e.contextualMessage(),
|
...parserErrors.map<Diagnostic>(e => ({
|
||||||
category: ts.DiagnosticCategory.Error,
|
messageText: e.contextualMessage(),
|
||||||
span: e.span,
|
category: ts.DiagnosticCategory.Error,
|
||||||
source: SOURCE,
|
span: e.span,
|
||||||
code: DEFAULT_ERROR_CODE
|
source: SOURCE,
|
||||||
}));
|
code: DEFAULT_ERROR_CODE
|
||||||
|
}))
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
this._structuralDiagnostics = [{
|
this._structuralDiagnostics = [
|
||||||
messageText: e.message,
|
...(this._structuralDiagnostics || []), {
|
||||||
category: ts.DiagnosticCategory.Error,
|
messageText: e.message,
|
||||||
source: SOURCE,
|
category: ts.DiagnosticCategory.Error,
|
||||||
code: DEFAULT_ERROR_CODE
|
source: SOURCE,
|
||||||
}];
|
code: DEFAULT_ERROR_CODE
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -702,6 +714,10 @@ function getNgOptionDiagnostics(options: CompilerOptions): Diagnostic[] {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeSeparators(path: string): string {
|
||||||
|
return path.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that can adjust a path from source path to out path,
|
* Returns a function that can adjust a path from source path to out path,
|
||||||
* based on an existing mapping from source to out path.
|
* based on an existing mapping from source to out path.
|
||||||
@ -723,18 +739,19 @@ export function createSrcToOutPathMapper(
|
|||||||
} = path): (srcFileName: string) => string {
|
} = path): (srcFileName: string) => string {
|
||||||
let srcToOutPath: (srcFileName: string) => string;
|
let srcToOutPath: (srcFileName: string) => string;
|
||||||
if (outDir) {
|
if (outDir) {
|
||||||
|
let path: {} = {}; // Ensure we error if we use `path` instead of `host`.
|
||||||
if (sampleSrcFileName == null || sampleOutFileName == null) {
|
if (sampleSrcFileName == null || sampleOutFileName == null) {
|
||||||
throw new Error(`Can't calculate the rootDir without a sample srcFileName / outFileName. `);
|
throw new Error(`Can't calculate the rootDir without a sample srcFileName / outFileName. `);
|
||||||
}
|
}
|
||||||
const srcFileDir = host.dirname(sampleSrcFileName).replace(/\\/g, '/');
|
const srcFileDir = normalizeSeparators(host.dirname(sampleSrcFileName));
|
||||||
const outFileDir = host.dirname(sampleOutFileName).replace(/\\/g, '/');
|
const outFileDir = normalizeSeparators(host.dirname(sampleOutFileName));
|
||||||
if (srcFileDir === outFileDir) {
|
if (srcFileDir === outFileDir) {
|
||||||
return (srcFileName) => srcFileName;
|
return (srcFileName) => srcFileName;
|
||||||
}
|
}
|
||||||
// calculate the common suffix, stopping
|
// calculate the common suffix, stopping
|
||||||
// at `outDir`.
|
// at `outDir`.
|
||||||
const srcDirParts = srcFileDir.split('/');
|
const srcDirParts = srcFileDir.split('/');
|
||||||
const outDirParts = path.relative(outDir, outFileDir).split('/');
|
const outDirParts = normalizeSeparators(host.relative(outDir, outFileDir)).split('/');
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < Math.min(srcDirParts.length, outDirParts.length) &&
|
while (i < Math.min(srcDirParts.length, outDirParts.length) &&
|
||||||
srcDirParts[srcDirParts.length - 1 - i] === outDirParts[outDirParts.length - 1 - i])
|
srcDirParts[srcDirParts.length - 1 - i] === outDirParts[outDirParts.length - 1 - i])
|
||||||
@ -750,7 +767,7 @@ export function createSrcToOutPathMapper(
|
|||||||
export function i18nExtract(
|
export function i18nExtract(
|
||||||
formatName: string | null, outFile: string | null, host: ts.CompilerHost,
|
formatName: string | null, outFile: string | null, host: ts.CompilerHost,
|
||||||
options: CompilerOptions, bundle: MessageBundle): string[] {
|
options: CompilerOptions, bundle: MessageBundle): string[] {
|
||||||
formatName = formatName || 'null';
|
formatName = formatName || 'xlf';
|
||||||
// Checks the format and returns the extension
|
// Checks the format and returns the extension
|
||||||
const ext = i18nGetExtension(formatName);
|
const ext = i18nGetExtension(formatName);
|
||||||
const content = i18nSerialize(bundle, formatName, options);
|
const content = i18nSerialize(bundle, formatName, options);
|
||||||
@ -784,7 +801,7 @@ export function i18nSerialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function i18nGetExtension(formatName: string): string {
|
export function i18nGetExtension(formatName: string): string {
|
||||||
const format = (formatName || 'xlf').toLowerCase();
|
const format = formatName.toLowerCase();
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'xmb':
|
case 'xmb':
|
||||||
|
@ -51,3 +51,29 @@ function pathStartsWithPrefix(prefix: string, fullPath: string): string|null {
|
|||||||
const rel = path.relative(prefix, fullPath);
|
const rel = path.relative(prefix, fullPath);
|
||||||
return rel.startsWith('..') ? null : rel;
|
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', () => {
|
it('should add metadata as decorators', () => {
|
||||||
writeConfig(`{
|
writeConfig(`{
|
||||||
"extends": "./tsconfig-base.json",
|
"extends": "./tsconfig-base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"emitDecoratorMetadata": true
|
||||||
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotationsAs": "decorators"
|
"annotationsAs": "decorators"
|
||||||
},
|
},
|
||||||
"files": ["mymodule.ts"]
|
"files": ["mymodule.ts"]
|
||||||
}`);
|
}`);
|
||||||
|
write('aclass.ts', `export class AClass {}`);
|
||||||
write('mymodule.ts', `
|
write('mymodule.ts', `
|
||||||
import {NgModule, Component} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
|
import {AClass} from './aclass';
|
||||||
|
|
||||||
@Component({template: ''})
|
@NgModule({declarations: []})
|
||||||
export class MyComp {
|
export class MyModule {
|
||||||
fn(p: any) {}
|
constructor(importedClass: AClass) {}
|
||||||
}
|
}
|
||||||
|
`);
|
||||||
@NgModule({declarations: [MyComp]})
|
|
||||||
export class MyModule {}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const exitCode = main(['-p', basePath], errorSpy);
|
const exitCode = main(['-p', basePath], errorSpy);
|
||||||
expect(exitCode).toEqual(0);
|
expect(exitCode).toEqual(0);
|
||||||
|
|
||||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
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));
|
main(['-p', path.join(basePath, 'src/tsconfig.json')], message => messages.push(message));
|
||||||
expect(exitCode).toBe(0, 'Compile failed unexpectedly.\n ' + messages.join('\n '));
|
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(mainTsPath, ts.ScriptTarget.ES5);
|
||||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, 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') {
|
function createModuleAndCompSource(prefix: string, template: string = prefix + 'template') {
|
||||||
@ -122,7 +163,8 @@ function createModuleAndCompSource(prefix: string, template: string = prefix + '
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MockWatchHost {
|
class MockWatchHost {
|
||||||
timeoutListeners: Array<(() => void)|null> = [];
|
nextTimeoutListenerId = 1;
|
||||||
|
timeoutListeners: {[id: string]: (() => void)} = {};
|
||||||
fileChangeListeners: Array<((event: FileChangeEvent, fileName: string) => void)|null> = [];
|
fileChangeListeners: Array<((event: FileChangeEvent, fileName: string) => void)|null> = [];
|
||||||
diagnostics: ng.Diagnostics = [];
|
diagnostics: ng.Diagnostics = [];
|
||||||
constructor(public config: ng.ParsedConfiguration) {}
|
constructor(public config: ng.ParsedConfiguration) {}
|
||||||
@ -141,16 +183,16 @@ class MockWatchHost {
|
|||||||
close: () => this.fileChangeListeners[id] = null,
|
close: () => this.fileChangeListeners[id] = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
setTimeout(callback: () => void, ms: number): any {
|
setTimeout(callback: () => void): any {
|
||||||
const id = this.timeoutListeners.length;
|
const id = this.nextTimeoutListenerId++;
|
||||||
this.timeoutListeners.push(callback);
|
this.timeoutListeners[id] = callback;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
clearTimeout(timeoutId: any): void { this.timeoutListeners[timeoutId] = null; }
|
clearTimeout(timeoutId: any): void { delete this.timeoutListeners[timeoutId]; }
|
||||||
flushTimeouts() {
|
flushTimeouts() {
|
||||||
this.timeoutListeners.forEach(cb => {
|
const listeners = this.timeoutListeners;
|
||||||
if (cb) cb();
|
this.timeoutListeners = {};
|
||||||
});
|
Object.keys(listeners).forEach(id => listeners[id]());
|
||||||
}
|
}
|
||||||
triggerFileChange(event: FileChangeEvent, fileName: string) {
|
triggerFileChange(event: FileChangeEvent, fileName: string) {
|
||||||
this.fileChangeListeners.forEach(listener => {
|
this.fileChangeListeners.forEach(listener => {
|
||||||
|
@ -64,10 +64,10 @@ export function setup(): TestSupport {
|
|||||||
function write(fileName: string, content: string) {
|
function write(fileName: string, content: string) {
|
||||||
const dir = path.dirname(fileName);
|
const dir = path.dirname(fileName);
|
||||||
if (dir != '.') {
|
if (dir != '.') {
|
||||||
const newDir = path.join(basePath, dir);
|
const newDir = path.resolve(basePath, dir);
|
||||||
if (!fs.existsSync(newDir)) fs.mkdirSync(newDir);
|
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}[]) {
|
function writeFiles(...mockDirs: {[fileName: string]: string}[]) {
|
||||||
|
@ -181,13 +181,15 @@ function convert(annotatedSource: string) {
|
|||||||
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
||||||
const moduleSourceFile = program.getSourceFile(fileName);
|
const moduleSourceFile = program.getSourceFile(fileName);
|
||||||
const transformers: ts.CustomTransformers = {
|
const transformers: ts.CustomTransformers = {
|
||||||
before: [getExpressionLoweringTransformFactory({
|
before: [getExpressionLoweringTransformFactory(
|
||||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
{
|
||||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||||
return requests;
|
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||||
} else {return new Map();}
|
return requests;
|
||||||
}
|
} else {return new Map();}
|
||||||
})]
|
}
|
||||||
|
},
|
||||||
|
program)]
|
||||||
};
|
};
|
||||||
let result: string = '';
|
let result: string = '';
|
||||||
const emitResult = program.emit(
|
const emitResult = program.emit(
|
||||||
|
@ -11,7 +11,8 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
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 {createSrcToOutPathMapper} from '../../src/transformers/program';
|
||||||
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
|
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
|
||||||
import {TestSupport, expectNoDiagnosticsInProgram, setup} from '../test_support';
|
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({
|
testSupport.writeFiles({
|
||||||
'src/main.ts': createModuleAndCompSource('main', `{{nonExistent}}`),
|
'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 host = ng.createCompilerHost({options});
|
||||||
const program = ng.createProgram(
|
const program = ng.createProgram(
|
||||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||||
@ -554,8 +579,8 @@ describe('ng program', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProgram(rootNames: string[]) {
|
function createProgram(rootNames: string[], overrideOptions: ng.CompilerOptions = {}) {
|
||||||
const options = testSupport.createCompilerOptions();
|
const options = testSupport.createCompilerOptions(overrideOptions);
|
||||||
const host = ng.createCompilerHost({options});
|
const host = ng.createCompilerHost({options});
|
||||||
const program = ng.createProgram(
|
const program = ng.createProgram(
|
||||||
{rootNames: rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host});
|
{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', () => {
|
it('should list lazyRoutes given an entryRoute recursively', () => {
|
||||||
writeSomeRoutes();
|
writeSomeRoutes();
|
||||||
const {program, options} = createProgram(['src/main.ts']);
|
const {program, options} = createProgram(['src/main.ts']);
|
||||||
@ -797,7 +850,7 @@ describe('ng program', () => {
|
|||||||
export class ChildModule {}
|
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([{
|
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([{
|
||||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||||
referencedModule:
|
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(
|
private _createNgFactoryStub(
|
||||||
outputCtx: OutputContext, file: NgAnalyzedFile, emitFlags: StubEmitFlags) {
|
outputCtx: OutputContext, file: NgAnalyzedFile, emitFlags: StubEmitFlags) {
|
||||||
|
let componentId = 0;
|
||||||
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
||||||
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
// 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.
|
// 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) {
|
if (!compMeta.isComponent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
componentId++;
|
||||||
this._createTypeCheckBlock(
|
this._createTypeCheckBlock(
|
||||||
outputCtx, ngModuleMeta, this._metadataResolver.getHostComponentMetadata(compMeta),
|
outputCtx, `${compMeta.type.reference.name}_Host_${componentId}`, ngModuleMeta,
|
||||||
[compMeta.type], externalReferenceVars);
|
this._metadataResolver.getHostComponentMetadata(compMeta), [compMeta.type],
|
||||||
this._createTypeCheckBlock(
|
|
||||||
outputCtx, ngModuleMeta, compMeta, ngModuleMeta.transitiveModule.directives,
|
|
||||||
externalReferenceVars);
|
externalReferenceVars);
|
||||||
|
this._createTypeCheckBlock(
|
||||||
|
outputCtx, `${compMeta.type.reference.name}_${componentId}`, ngModuleMeta, compMeta,
|
||||||
|
ngModuleMeta.transitiveModule.directives, externalReferenceVars);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -246,12 +249,13 @@ export class AotCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createTypeCheckBlock(
|
private _createTypeCheckBlock(
|
||||||
ctx: OutputContext, moduleMeta: CompileNgModuleMetadata, compMeta: CompileDirectiveMetadata,
|
ctx: OutputContext, componentId: string, moduleMeta: CompileNgModuleMetadata,
|
||||||
directives: CompileIdentifierMetadata[], externalReferenceVars: Map<any, string>) {
|
compMeta: CompileDirectiveMetadata, directives: CompileIdentifierMetadata[],
|
||||||
|
externalReferenceVars: Map<any, string>) {
|
||||||
const {template: parsedTemplate, pipes: usedPipes} =
|
const {template: parsedTemplate, pipes: usedPipes} =
|
||||||
this._parseTemplate(compMeta, moduleMeta, directives);
|
this._parseTemplate(compMeta, moduleMeta, directives);
|
||||||
ctx.statements.push(...this._typeCheckCompiler.compileComponent(
|
ctx.statements.push(...this._typeCheckCompiler.compileComponent(
|
||||||
compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
componentId, compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
||||||
}
|
}
|
||||||
|
|
||||||
emitMessageBundle(analyzeResult: NgAnalyzedModules, locale: string|null): MessageBundle {
|
emitMessageBundle(analyzeResult: NgAnalyzedModules, locale: string|null): MessageBundle {
|
||||||
|
@ -54,7 +54,7 @@ export function createAotUrlResolver(host: {
|
|||||||
*/
|
*/
|
||||||
export function createAotCompiler(
|
export function createAotCompiler(
|
||||||
compilerHost: AotCompilerHost, options: AotCompilerOptions,
|
compilerHost: AotCompilerHost, options: AotCompilerOptions,
|
||||||
errorCollector: (error: any, type?: any) =>
|
errorCollector?: (error: any, type?: any) =>
|
||||||
void): {compiler: AotCompiler, reflector: StaticReflector} {
|
void): {compiler: AotCompiler, reflector: StaticReflector} {
|
||||||
let translations: string = options.translations || '';
|
let translations: string = options.translations || '';
|
||||||
|
|
||||||
|
@ -80,8 +80,10 @@ export class StaticReflector implements CompileReflector {
|
|||||||
const refSymbol =
|
const refSymbol =
|
||||||
this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !, containingFile);
|
this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !, containingFile);
|
||||||
const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
||||||
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
if (!containingFile) {
|
||||||
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
||||||
|
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
||||||
|
}
|
||||||
return declarationSymbol;
|
return declarationSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,7 +752,7 @@ class PopulatedScope extends BindingScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
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).fileName = fileName;
|
||||||
(result as any).line = line;
|
(result as any).line = line;
|
||||||
(result as any).column = column;
|
(result as any).column = column;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {AotCompilerOptions} from '../aot/compiler_options';
|
import {AotCompilerOptions} from '../aot/compiler_options';
|
||||||
import {StaticReflector} from '../aot/static_reflector';
|
import {StaticReflector} from '../aot/static_reflector';
|
||||||
import {StaticSymbol} from '../aot/static_symbol';
|
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 {BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||||
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
||||||
import {Identifiers} from '../identifiers';
|
import {Identifiers} from '../identifiers';
|
||||||
@ -33,7 +33,8 @@ export class TypeCheckCompiler {
|
|||||||
* and also violate the point above.
|
* and also violate the point above.
|
||||||
*/
|
*/
|
||||||
compileComponent(
|
compileComponent(
|
||||||
component: CompileDirectiveMetadata, template: TemplateAst[], usedPipes: CompilePipeSummary[],
|
componentId: string, component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
usedPipes: CompilePipeSummary[],
|
||||||
externalReferenceVars: Map<StaticSymbol, string>): o.Statement[] {
|
externalReferenceVars: Map<StaticSymbol, string>): o.Statement[] {
|
||||||
const pipes = new Map<string, StaticSymbol>();
|
const pipes = new Map<string, StaticSymbol>();
|
||||||
usedPipes.forEach(p => pipes.set(p.name, p.type.reference));
|
usedPipes.forEach(p => pipes.set(p.name, p.type.reference));
|
||||||
@ -48,7 +49,7 @@ export class TypeCheckCompiler {
|
|||||||
const visitor = viewBuilderFactory(null);
|
const visitor = viewBuilderFactory(null);
|
||||||
visitor.visitAll([], template);
|
visitor.visitAll([], template);
|
||||||
|
|
||||||
return visitor.build();
|
return visitor.build(componentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +104,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
|||||||
templateVisitAll(this, astNodes);
|
templateVisitAll(this, astNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
build(targetStatements: o.Statement[] = []): o.Statement[] {
|
build(componentId: string, targetStatements: o.Statement[] = []): o.Statement[] {
|
||||||
this.children.forEach((child) => child.build(targetStatements));
|
this.children.forEach((child) => child.build(componentId, targetStatements));
|
||||||
const viewStmts: o.Statement[] =
|
const viewStmts: o.Statement[] =
|
||||||
[o.variable(DYNAMIC_VAR_NAME).set(o.NULL_EXPR).toDeclStmt(o.DYNAMIC_TYPE)];
|
[o.variable(DYNAMIC_VAR_NAME).set(o.NULL_EXPR).toDeclStmt(o.DYNAMIC_TYPE)];
|
||||||
let bindingCount = 0;
|
let bindingCount = 0;
|
||||||
@ -128,7 +129,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
|||||||
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
|
(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);
|
const viewFactory = new o.DeclareFunctionStmt(viewName, [], viewStmts);
|
||||||
targetStatements.push(viewFactory);
|
targetStatements.push(viewFactory);
|
||||||
return targetStatements;
|
return targetStatements;
|
||||||
|
@ -1062,6 +1062,22 @@ describe('StaticReflector', () => {
|
|||||||
.useValue)
|
.useValue)
|
||||||
.toEqual({path: 'foo', data: {e: 1}});
|
.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} = {
|
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||||
|
@ -106,8 +106,7 @@ export class NgswCommChannel {
|
|||||||
this.registration = <Observable<ServiceWorkerRegistration>>(
|
this.registration = <Observable<ServiceWorkerRegistration>>(
|
||||||
op_switchMap.call(this.worker, () => serviceWorker.getRegistration()));
|
op_switchMap.call(this.worker, () => serviceWorker.getRegistration()));
|
||||||
|
|
||||||
const rawEvents = <Observable<MessageEvent>>(op_switchMap.call(
|
const rawEvents = obs_fromEvent(serviceWorker, 'message');
|
||||||
this.registration, (reg: ServiceWorkerRegistration) => obs_fromEvent(reg, 'message')));
|
|
||||||
|
|
||||||
const rawEventPayload =
|
const rawEventPayload =
|
||||||
<Observable<Object>>(op_map.call(rawEvents, (event: MessageEvent) => event.data));
|
<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>;
|
op_filter.call(app.isStable, (stable: boolean) => !!stable) as Observable<boolean>;
|
||||||
const isStable = op_take.call(onStable, 1) as Observable<boolean>;
|
const isStable = op_take.call(onStable, 1) as Observable<boolean>;
|
||||||
const whenStable = op_toPromise.call(isStable) as Promise<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;
|
return initializer;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export class SwPush {
|
|||||||
|
|
||||||
const workerDrivenSubscriptions = <Observable<PushSubscription|null>>(op_switchMap.call(
|
const workerDrivenSubscriptions = <Observable<PushSubscription|null>>(op_switchMap.call(
|
||||||
this.pushManager, (pm: PushManager) => pm.getSubscription().then(sub => { return sub; })));
|
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> {
|
requestSubscription(options: {serverPublicKey: string}): Promise<PushSubscription> {
|
||||||
|
@ -44,11 +44,9 @@ export function main() {
|
|||||||
});
|
});
|
||||||
describe('SwPush', () => {
|
describe('SwPush', () => {
|
||||||
let push: SwPush;
|
let push: SwPush;
|
||||||
let reg: MockServiceWorkerRegistration;
|
beforeEach(() => {
|
||||||
beforeEach((done: DoneFn) => {
|
|
||||||
push = new SwPush(comm);
|
push = new SwPush(comm);
|
||||||
mock.setupSw();
|
mock.setupSw();
|
||||||
mock.mockRegistration.then(r => reg = r).then(() => done());
|
|
||||||
});
|
});
|
||||||
it('receives push messages', (done: DoneFn) => {
|
it('receives push messages', (done: DoneFn) => {
|
||||||
push.messages.subscribe(msg => {
|
push.messages.subscribe(msg => {
|
||||||
@ -57,7 +55,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
reg.sendMessage({
|
mock.sendMessage({
|
||||||
type: 'PUSH',
|
type: 'PUSH',
|
||||||
data: {
|
data: {
|
||||||
message: 'this was a push message',
|
message: 'this was a push message',
|
||||||
@ -76,11 +74,9 @@ export function main() {
|
|||||||
});
|
});
|
||||||
describe('SwUpdate', () => {
|
describe('SwUpdate', () => {
|
||||||
let update: SwUpdate;
|
let update: SwUpdate;
|
||||||
let reg: MockServiceWorkerRegistration;
|
beforeEach(() => {
|
||||||
beforeEach((done: DoneFn) => {
|
|
||||||
update = new SwUpdate(comm);
|
update = new SwUpdate(comm);
|
||||||
mock.setupSw();
|
mock.setupSw();
|
||||||
mock.mockRegistration.then(r => reg = r).then(() => done());
|
|
||||||
});
|
});
|
||||||
it('processes update availability notifications when sent', (done: DoneFn) => {
|
it('processes update availability notifications when sent', (done: DoneFn) => {
|
||||||
update.available.subscribe(event => {
|
update.available.subscribe(event => {
|
||||||
@ -89,7 +85,7 @@ export function main() {
|
|||||||
expect(event.type).toEqual('UPDATE_AVAILABLE');
|
expect(event.type).toEqual('UPDATE_AVAILABLE');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
reg.sendMessage({
|
mock.sendMessage({
|
||||||
type: 'UPDATE_AVAILABLE',
|
type: 'UPDATE_AVAILABLE',
|
||||||
current: {
|
current: {
|
||||||
version: 'A',
|
version: 'A',
|
||||||
@ -106,7 +102,7 @@ export function main() {
|
|||||||
expect(event.type).toEqual('UPDATE_ACTIVATED');
|
expect(event.type).toEqual('UPDATE_ACTIVATED');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
reg.sendMessage({
|
mock.sendMessage({
|
||||||
type: 'UPDATE_ACTIVATED',
|
type: 'UPDATE_ACTIVATED',
|
||||||
previous: {
|
previous: {
|
||||||
version: 'A',
|
version: 'A',
|
||||||
@ -119,7 +115,7 @@ export function main() {
|
|||||||
it('activates updates when requested', (done: DoneFn) => {
|
it('activates updates when requested', (done: DoneFn) => {
|
||||||
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
||||||
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
||||||
reg.sendMessage({
|
mock.sendMessage({
|
||||||
type: 'STATUS',
|
type: 'STATUS',
|
||||||
nonce: msg.statusNonce,
|
nonce: msg.statusNonce,
|
||||||
status: true,
|
status: true,
|
||||||
@ -130,7 +126,7 @@ export function main() {
|
|||||||
it('reports activation failure when requested', (done: DoneFn) => {
|
it('reports activation failure when requested', (done: DoneFn) => {
|
||||||
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
mock.messages.subscribe((msg: {action: string, statusNonce: number}) => {
|
||||||
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
expect(msg.action).toEqual('ACTIVATE_UPDATE');
|
||||||
reg.sendMessage({
|
mock.sendMessage({
|
||||||
type: 'STATUS',
|
type: 'STATUS',
|
||||||
nonce: msg.statusNonce,
|
nonce: msg.statusNonce,
|
||||||
status: false,
|
status: false,
|
||||||
|
@ -85,7 +85,7 @@ export function main() {
|
|||||||
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
||||||
|
|
||||||
scope.clients.add('default');
|
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'); });
|
mock.messages.subscribe(msg => { scope.handleMessage(msg, 'default'); });
|
||||||
|
|
||||||
|
@ -10,17 +10,26 @@ import {Subject} from 'rxjs/Subject';
|
|||||||
|
|
||||||
export class MockServiceWorkerContainer {
|
export class MockServiceWorkerContainer {
|
||||||
private onControllerChange: Function[] = [];
|
private onControllerChange: Function[] = [];
|
||||||
|
private onMessage: Function[] = [];
|
||||||
private registration: MockServiceWorkerRegistration|null = null;
|
private registration: MockServiceWorkerRegistration|null = null;
|
||||||
controller: MockServiceWorker|null = null;
|
controller: MockServiceWorker|null = null;
|
||||||
|
|
||||||
messages = new Subject();
|
messages = new Subject();
|
||||||
|
|
||||||
addEventListener(event: 'controllerchange', handler: Function) {
|
addEventListener(event: 'controllerchange'|'message', handler: Function) {
|
||||||
this.onControllerChange.push(handler);
|
if (event === 'controllerchange') {
|
||||||
|
this.onControllerChange.push(handler);
|
||||||
|
} else if (event === 'message') {
|
||||||
|
this.onMessage.push(handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEventListener(event: 'controllerchange', handler: Function) {
|
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; }
|
async register(url: string): Promise<void> { return; }
|
||||||
@ -36,6 +45,12 @@ export class MockServiceWorkerContainer {
|
|||||||
get mockRegistration(): Promise<MockServiceWorkerRegistration> {
|
get mockRegistration(): Promise<MockServiceWorkerRegistration> {
|
||||||
return Promise.resolve(this.registration !);
|
return Promise.resolve(this.registration !);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMessage(value: Object): void {
|
||||||
|
this.onMessage.forEach(onMessage => onMessage({
|
||||||
|
data: value,
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockServiceWorker {
|
export class MockServiceWorker {
|
||||||
@ -44,21 +59,4 @@ export class MockServiceWorker {
|
|||||||
postMessage(value: Object) { this.mock.messages.next(value); }
|
postMessage(value: Object) { this.mock.messages.next(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockServiceWorkerRegistration {
|
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,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6194,9 +6194,9 @@ rx-lite@^3.1.2:
|
|||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||||
|
|
||||||
rxjs@^5.5.0:
|
rxjs@^5.5.2:
|
||||||
version "5.5.0"
|
version "5.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.0.tgz#26d8f3866eb700e247e0728a147c3d628993d812"
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3"
|
||||||
dependencies:
|
dependencies:
|
||||||
symbol-observable "^1.0.1"
|
symbol-observable "^1.0.1"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user