fix(compiler): look for flat module resources using declaration module path (#15367)
`ngc` would look for flat module resources relative to the flat module index. `ngc` now looks for flat module resources relative to the `.d.ts` file that declarates the component. Fixes #15221 PR Close #15367
This commit is contained in:
parent
7354949763
commit
90d2518d9a
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './src/flat.component';
|
||||||
|
export * from './src/flat.module';
|
|
@ -0,0 +1 @@
|
||||||
|
<div>flat module component</div>
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'flat-comp',
|
||||||
|
templateUrl: 'flat.component.html',
|
||||||
|
})
|
||||||
|
export class FlatComponent {
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
import {FlatComponent} from './flat.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
FlatComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
FlatComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FlatModule {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
// For TypeScript 1.8, we have to lay out generated files
|
||||||
|
// in the same source directory with your code.
|
||||||
|
"genDir": "ng",
|
||||||
|
"flatModuleId": "flat_module",
|
||||||
|
"flatModuleOutFile": "index.js",
|
||||||
|
"skipTemplateCodegen": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "",
|
||||||
|
"declaration": true,
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"outDir": "../node_modules/flat_module",
|
||||||
|
// Prevent scanning up the directory tree for types
|
||||||
|
"typeRoots": ["node_modules/@types"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"files": ["public-api.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'use-flat-module',
|
||||||
|
template: '<flat-comp></flat-comp>',
|
||||||
|
})
|
||||||
|
export class ComponentUsingFlatModule {
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import {ApplicationRef, NgModule} from '@angular/core';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import {ServerModule} from '@angular/platform-server';
|
import {ServerModule} from '@angular/platform-server';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
import {FlatModule} from 'flat_module';
|
||||||
// Note: don't refer to third_party_src as we want to test that
|
// Note: don't refer to third_party_src as we want to test that
|
||||||
// we can compile components from node_modules!
|
// we can compile components from node_modules!
|
||||||
import {ThirdpartyModule} from 'third_party/module';
|
import {ThirdpartyModule} from 'third_party/module';
|
||||||
|
@ -18,6 +19,7 @@ import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
||||||
import {AnimateCmp} from './animate';
|
import {AnimateCmp} from './animate';
|
||||||
import {BasicComp} from './basic';
|
import {BasicComp} from './basic';
|
||||||
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
||||||
|
import {ComponentUsingFlatModule} from './comp_using_flat_module';
|
||||||
import {CUSTOM} from './custom_token';
|
import {CUSTOM} from './custom_token';
|
||||||
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||||
import {BindingErrorComp} from './errors';
|
import {BindingErrorComp} from './errors';
|
||||||
|
@ -48,6 +50,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||||
ProjectingComp,
|
ProjectingComp,
|
||||||
SomeDirectiveInRootModule,
|
SomeDirectiveInRootModule,
|
||||||
SomePipeInRootModule,
|
SomePipeInRootModule,
|
||||||
|
ComponentUsingFlatModule,
|
||||||
ComponentUsingThirdParty,
|
ComponentUsingThirdParty,
|
||||||
BindingErrorComp,
|
BindingErrorComp,
|
||||||
],
|
],
|
||||||
|
@ -58,6 +61,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||||
ModuleUsingCustomElements,
|
ModuleUsingCustomElements,
|
||||||
SomeLibModule.withProviders(),
|
SomeLibModule.withProviders(),
|
||||||
ThirdpartyModule,
|
ThirdpartyModule,
|
||||||
|
FlatModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SomeService,
|
SomeService,
|
||||||
|
@ -73,6 +77,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||||
CompWithReferences,
|
CompWithReferences,
|
||||||
ProjectingComp,
|
ProjectingComp,
|
||||||
ComponentUsingThirdParty,
|
ComponentUsingThirdParty,
|
||||||
|
ComponentUsingFlatModule,
|
||||||
BindingErrorComp,
|
BindingErrorComp,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import './init';
|
import './init';
|
||||||
|
|
||||||
import {ComponentUsingThirdParty} from '../src/comp_using_3rdp';
|
import {ComponentUsingThirdParty} from '../src/comp_using_3rdp';
|
||||||
|
import {ComponentUsingFlatModule} from '../src/comp_using_flat_module';
|
||||||
import {MainModule} from '../src/module';
|
import {MainModule} from '../src/module';
|
||||||
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
||||||
|
|
||||||
|
@ -43,6 +44,15 @@ describe('NgModule', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('flat modules', () => {
|
||||||
|
it('should support flat module entryComponents components', () => {
|
||||||
|
// https://github.com/angular/angular/issues/15221
|
||||||
|
const fixture = createComponent(ComponentUsingFlatModule);
|
||||||
|
const bundleComp = fixture.nativeElement.children;
|
||||||
|
expect(bundleComp[0].children[0].children[0].data).toEqual('flat module component');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('third-party modules', () => {
|
describe('third-party modules', () => {
|
||||||
// https://github.com/angular/angular/issues/11889
|
// https://github.com/angular/angular/issues/11889
|
||||||
it('should support third party entryComponents components', () => {
|
it('should support third party entryComponents components', () => {
|
||||||
|
|
|
@ -42,6 +42,7 @@ export class StaticAndDynamicReflectionCapabilities {
|
||||||
setter(name: string): ɵSetterFn { return this.dynamicDelegate.setter(name); }
|
setter(name: string): ɵSetterFn { return this.dynamicDelegate.setter(name); }
|
||||||
method(name: string): ɵMethodFn { return this.dynamicDelegate.method(name); }
|
method(name: string): ɵMethodFn { return this.dynamicDelegate.method(name); }
|
||||||
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
||||||
|
resourceUri(type: any): string { return this.staticDelegate.resourceUri(type); }
|
||||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any) {
|
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any) {
|
||||||
return this.staticDelegate.resolveIdentifier(name, moduleUrl, members);
|
return this.staticDelegate.resolveIdentifier(name, moduleUrl, members);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,11 @@ export class StaticReflector implements ɵReflectorReader {
|
||||||
return staticSymbol ? staticSymbol.filePath : null;
|
return staticSymbol ? staticSymbol.filePath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceUri(typeOrFunc: StaticSymbol): string {
|
||||||
|
const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
|
||||||
|
return this.symbolResolver.getResourcePath(staticSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, members: string[]): StaticSymbol {
|
resolveIdentifier(name: string, moduleUrl: string, members: string[]): StaticSymbol {
|
||||||
const importSymbol = this.getStaticSymbol(moduleUrl, name);
|
const importSymbol = this.getStaticSymbol(moduleUrl, name);
|
||||||
const rootSymbol = this.findDeclaration(moduleUrl, name);
|
const rootSymbol = this.findDeclaration(moduleUrl, name);
|
||||||
|
|
|
@ -58,6 +58,7 @@ export class StaticSymbolResolver {
|
||||||
private resolvedFilePaths = new Set<string>();
|
private resolvedFilePaths = new Set<string>();
|
||||||
// Note: this will only contain StaticSymbols without members!
|
// Note: this will only contain StaticSymbols without members!
|
||||||
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
||||||
|
private symbolResourcePaths = new Map<StaticSymbol, string>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
||||||
|
@ -108,6 +109,15 @@ export class StaticSymbolResolver {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getResourcePath produces the path to the original location of the symbol and should
|
||||||
|
* be used to determine the relative location of resource references recorded in
|
||||||
|
* symbol metadata.
|
||||||
|
*/
|
||||||
|
getResourcePath(staticSymbol: StaticSymbol): string {
|
||||||
|
return this.symbolResourcePaths.get(staticSymbol) || staticSymbol.filePath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getTypeArity returns the number of generic type parameters the given symbol
|
* getTypeArity returns the number of generic type parameters the given symbol
|
||||||
* has. If the symbol is not a type the result is null.
|
* has. If the symbol is not a type the result is null.
|
||||||
|
@ -200,18 +210,35 @@ export class StaticSymbolResolver {
|
||||||
// handle direct declarations of the symbol
|
// handle direct declarations of the symbol
|
||||||
const topLevelSymbolNames =
|
const topLevelSymbolNames =
|
||||||
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
||||||
|
const origins: {[index: string]: string} = metadata['origins'] || {};
|
||||||
Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
||||||
const symbolMeta = metadata['metadata'][metadataKey];
|
const symbolMeta = metadata['metadata'][metadataKey];
|
||||||
const name = unescapeIdentifier(metadataKey);
|
const name = unescapeIdentifier(metadataKey);
|
||||||
const canonicalSymbol = this.getStaticSymbol(filePath, name);
|
|
||||||
|
const symbol = this.getStaticSymbol(filePath, name);
|
||||||
|
let importSymbol: StaticSymbol|undefined = undefined;
|
||||||
if (metadata['importAs']) {
|
if (metadata['importAs']) {
|
||||||
// Index bundle indexes should use the importAs module name instead of a reference
|
// Index bundle indexes should use the importAs module name instead of a reference
|
||||||
// to the .d.ts file directly.
|
// to the .d.ts file directly.
|
||||||
const importSymbol = this.getStaticSymbol(metadata['importAs'], name);
|
importSymbol = this.getStaticSymbol(metadata['importAs'], name);
|
||||||
this.recordImportAs(canonicalSymbol, importSymbol);
|
this.recordImportAs(symbol, importSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
const origin = origins[metadataKey];
|
||||||
|
if (origin) {
|
||||||
|
// If the symbol is from a bundled index, use the declaration location of the
|
||||||
|
// symbol so relative references (such as './my.html') will be calculated
|
||||||
|
// correctly.
|
||||||
|
const originFilePath = this.resolveModule(origin, filePath);
|
||||||
|
if (!originFilePath) {
|
||||||
|
this.reportError(
|
||||||
|
new Error(`Couldn't resolve original symbol for ${origin} from ${filePath}`), null);
|
||||||
|
} else {
|
||||||
|
this.symbolResourcePaths.set(symbol, originFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resolvedSymbols.push(
|
resolvedSymbols.push(
|
||||||
this.createResolvedSymbol(canonicalSymbol, topLevelSymbolNames, symbolMeta));
|
this.createResolvedSymbol(symbol, filePath, topLevelSymbolNames, symbolMeta));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +284,7 @@ export class StaticSymbolResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private createResolvedSymbol(
|
private createResolvedSymbol(
|
||||||
sourceSymbol: StaticSymbol, topLevelSymbolNames: Set<string>,
|
sourceSymbol: StaticSymbol, topLevelPath: string, topLevelSymbolNames: Set<string>,
|
||||||
metadata: any): ResolvedStaticSymbol {
|
metadata: any): ResolvedStaticSymbol {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
@ -291,7 +318,7 @@ export class StaticSymbolResolver {
|
||||||
return {__symbolic: 'reference', name: name};
|
return {__symbolic: 'reference', name: name};
|
||||||
} else {
|
} else {
|
||||||
if (topLevelSymbolNames.has(name)) {
|
if (topLevelSymbolNames.has(name)) {
|
||||||
return self.getStaticSymbol(sourceSymbol.filePath, name);
|
return self.getStaticSymbol(topLevelPath, name);
|
||||||
}
|
}
|
||||||
// ambient value
|
// ambient value
|
||||||
null;
|
null;
|
||||||
|
|
|
@ -1060,7 +1060,7 @@ function isValidType(value: any): boolean {
|
||||||
export function componentModuleUrl(
|
export function componentModuleUrl(
|
||||||
reflector: ɵReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
reflector: ɵReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
||||||
if (type instanceof StaticSymbol) {
|
if (type instanceof StaticSymbol) {
|
||||||
return type.filePath;
|
return reflector.resourceUri(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleId = cmpMetadata.moduleId;
|
const moduleId = cmpMetadata.moduleId;
|
||||||
|
|
|
@ -415,6 +415,49 @@ describe('compiler (bundled Angular)', () => {
|
||||||
.toBeDefined();
|
.toBeDefined();
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Bundled libary', () => {
|
||||||
|
let host: MockCompilerHost;
|
||||||
|
let aotHost: MockAotCompilerHost;
|
||||||
|
let libraryFiles: Map<string, string>;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
// Emit the library bundle
|
||||||
|
const emittingHost =
|
||||||
|
new EmittingCompilerHost(['/bolder/index.ts'], {emitMetadata: false, mockData: LIBRARY});
|
||||||
|
|
||||||
|
// Create the metadata bundled
|
||||||
|
const indexModule = '/bolder/public-api';
|
||||||
|
const bundler =
|
||||||
|
new MetadataBundler(indexModule, 'bolder', new MockMetadataBundlerHost(emittingHost));
|
||||||
|
const bundle = bundler.getMetadataBundle();
|
||||||
|
const metadata = JSON.stringify(bundle.metadata, null, ' ');
|
||||||
|
const bundleIndexSource = privateEntriesToIndex('./public-api', bundle.privates);
|
||||||
|
emittingHost.override('/bolder/index.ts', bundleIndexSource);
|
||||||
|
emittingHost.addWrittenFile('/bolder/index.metadata.json', metadata);
|
||||||
|
|
||||||
|
// Emit the sources
|
||||||
|
const emittingProgram = ts.createProgram(['/bolder/index.ts'], settings, emittingHost);
|
||||||
|
emittingProgram.emit();
|
||||||
|
libraryFiles = emittingHost.written;
|
||||||
|
|
||||||
|
// Copy the .html file
|
||||||
|
const htmlFileName = '/bolder/src/bolder.component.html';
|
||||||
|
libraryFiles.set(htmlFileName, emittingHost.readFile(htmlFileName));
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = new MockCompilerHost(
|
||||||
|
LIBRARY_USING_APP_MODULE, LIBRARY_USING_APP, angularFiles, [libraryFiles]);
|
||||||
|
aotHost = new MockAotCompilerHost(host);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should compile',
|
||||||
|
async(() => compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics)));
|
||||||
|
|
||||||
|
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||||
|
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function expectNoDiagnostics(program: ts.Program) {
|
function expectNoDiagnostics(program: ts.Program) {
|
||||||
|
@ -547,6 +590,72 @@ const FILES: MockData = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LIBRARY: MockData = {
|
||||||
|
bolder: {
|
||||||
|
'public-api.ts': `
|
||||||
|
export * from './src/bolder.component';
|
||||||
|
export * from './src/bolder.module';
|
||||||
|
`,
|
||||||
|
src: {
|
||||||
|
'bolder.component.ts': `
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'bolder',
|
||||||
|
templateUrl: './bolder.component.html'
|
||||||
|
})
|
||||||
|
export class BolderComponent {
|
||||||
|
@Input() data: string;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'bolder.component.html': `
|
||||||
|
<b>{{data}}</b>
|
||||||
|
`,
|
||||||
|
'bolder.module.ts': `
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {BolderComponent} from './bolder.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [BolderComponent],
|
||||||
|
exports: [BolderComponent]
|
||||||
|
})
|
||||||
|
export class BolderModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LIBRARY_USING_APP_MODULE = ['/lib-user/app/app.module.ts'];
|
||||||
|
const LIBRARY_USING_APP: MockData = {
|
||||||
|
'lib-user': {
|
||||||
|
app: {
|
||||||
|
'app.component.ts': `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<h1>Hello <bolder [data]="name"></bolder></h1>'
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
name = 'Angular';
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'app.module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BolderModule } from 'bolder';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [ AppComponent ],
|
||||||
|
bootstrap: [ AppComponent ],
|
||||||
|
imports: [ BolderModule ]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function expectPromiseToThrow(p: Promise<any>, msg: RegExp) {
|
function expectPromiseToThrow(p: Promise<any>, msg: RegExp) {
|
||||||
p.then(
|
p.then(
|
||||||
() => { throw new Error('Expected to throw'); }, (e) => { expect(e.message).toMatch(msg); });
|
() => { throw new Error('Expected to throw'); }, (e) => { expect(e.message).toMatch(msg); });
|
||||||
|
|
|
@ -41,7 +41,10 @@ export const settings: ts.CompilerOptions = {
|
||||||
types: []
|
types: []
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface EmitterOptions { emitMetadata: boolean; }
|
export interface EmitterOptions {
|
||||||
|
emitMetadata: boolean;
|
||||||
|
mockData?: MockData;
|
||||||
|
}
|
||||||
|
|
||||||
export class EmittingCompilerHost implements ts.CompilerHost {
|
export class EmittingCompilerHost implements ts.CompilerHost {
|
||||||
private angularSourcePath: string|undefined;
|
private angularSourcePath: string|undefined;
|
||||||
|
@ -100,12 +103,14 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||||
|
|
||||||
// ts.ModuleResolutionHost
|
// ts.ModuleResolutionHost
|
||||||
fileExists(fileName: string): boolean {
|
fileExists(fileName: string): boolean {
|
||||||
return this.addedFiles.has(fileName) || fs.existsSync(fileName);
|
return this.addedFiles.has(fileName) || open(fileName, this.options.mockData) != null ||
|
||||||
|
fs.existsSync(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
readFile(fileName: string): string {
|
readFile(fileName: string): string {
|
||||||
const result = this.addedFiles.get(fileName);
|
const result = this.addedFiles.get(fileName) || open(fileName, this.options.mockData);
|
||||||
if (result) return result;
|
if (result) return result;
|
||||||
|
|
||||||
let basename = path.basename(fileName);
|
let basename = path.basename(fileName);
|
||||||
if (/^lib.*\.d\.ts$/.test(basename)) {
|
if (/^lib.*\.d\.ts$/.test(basename)) {
|
||||||
let libPath = ts.getDefaultLibFilePath(settings);
|
let libPath = ts.getDefaultLibFilePath(settings);
|
||||||
|
@ -115,12 +120,17 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryExists(directoryName: string): boolean {
|
directoryExists(directoryName: string): boolean {
|
||||||
return fs.existsSync(directoryName) && fs.statSync(directoryName).isDirectory();
|
return directoryExists(directoryName, this.options.mockData) ||
|
||||||
|
(fs.existsSync(directoryName) && fs.statSync(directoryName).isDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentDirectory(): string { return this.root; }
|
getCurrentDirectory(): string { return this.root; }
|
||||||
|
|
||||||
getDirectories(dir: string): string[] {
|
getDirectories(dir: string): string[] {
|
||||||
|
const result = open(dir, this.options.mockData);
|
||||||
|
if (result && typeof result !== 'string') {
|
||||||
|
return Object.keys(result);
|
||||||
|
}
|
||||||
return fs.readdirSync(dir).filter(p => {
|
return fs.readdirSync(dir).filter(p => {
|
||||||
const name = path.join(dir, p);
|
const name = path.join(dir, p);
|
||||||
const stat = fs.statSync(name);
|
const stat = fs.statSync(name);
|
||||||
|
@ -160,6 +170,8 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||||
getNewLine(): string { return '\n'; }
|
getNewLine(): string { return '\n'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MOCK_NODEMODULES_PREFIX = '/node_modules/';
|
||||||
|
|
||||||
export class MockCompilerHost implements ts.CompilerHost {
|
export class MockCompilerHost implements ts.CompilerHost {
|
||||||
scriptNames: string[];
|
scriptNames: string[];
|
||||||
|
|
||||||
|
@ -171,7 +183,9 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||||
private assumeExists = new Set<string>();
|
private assumeExists = new Set<string>();
|
||||||
private traces: string[] = [];
|
private traces: string[] = [];
|
||||||
|
|
||||||
constructor(scriptNames: string[], private data: MockData, private angular: Map<string, string>) {
|
constructor(
|
||||||
|
scriptNames: string[], private data: MockData, private angular: Map<string, string>,
|
||||||
|
private libraries?: Map<string, string>[]) {
|
||||||
this.scriptNames = scriptNames.slice(0);
|
this.scriptNames = scriptNames.slice(0);
|
||||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||||
let angularIndex = moduleFilename.indexOf('@angular');
|
let angularIndex = moduleFilename.indexOf('@angular');
|
||||||
|
@ -219,13 +233,21 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||||
const effectiveName = this.getEffectiveName(fileName);
|
const effectiveName = this.getEffectiveName(fileName);
|
||||||
if (effectiveName == fileName) {
|
if (effectiveName == fileName) {
|
||||||
let result = open(fileName, this.data) != null;
|
let result = open(fileName, this.data) != null;
|
||||||
|
if (!result && fileName.startsWith(MOCK_NODEMODULES_PREFIX)) {
|
||||||
|
const libraryPath = fileName.substr(MOCK_NODEMODULES_PREFIX.length - 1);
|
||||||
|
for (const library of this.libraries) {
|
||||||
|
if (library.has(libraryPath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
if (fileName.match(rxjs)) {
|
if (fileName.match(rxjs)) {
|
||||||
let result = fs.existsSync(effectiveName);
|
let result = fs.existsSync(effectiveName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
let result = this.angular.has(effectiveName);
|
const result = this.angular.has(effectiveName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,9 +314,16 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||||
return fs.readFileSync(path.join(path.dirname(libPath), basename), 'utf8');
|
return fs.readFileSync(path.join(path.dirname(libPath), basename), 'utf8');
|
||||||
} else {
|
} else {
|
||||||
let effectiveName = this.getEffectiveName(fileName);
|
let effectiveName = this.getEffectiveName(fileName);
|
||||||
if (effectiveName === fileName)
|
if (effectiveName === fileName) {
|
||||||
return open(fileName, this.data);
|
const result = open(fileName, this.data);
|
||||||
else {
|
if (!result && fileName.startsWith(MOCK_NODEMODULES_PREFIX)) {
|
||||||
|
const libraryPath = fileName.substr(MOCK_NODEMODULES_PREFIX.length - 1);
|
||||||
|
for (const library of this.libraries) {
|
||||||
|
if (library.has(libraryPath)) return library.get(libraryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
if (fileName.match(rxjs)) {
|
if (fileName.match(rxjs)) {
|
||||||
if (fs.existsSync(fileName)) {
|
if (fs.existsSync(fileName)) {
|
||||||
return fs.readFileSync(fileName, 'utf8');
|
return fs.readFileSync(fileName, 'utf8');
|
||||||
|
@ -391,7 +420,11 @@ export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResource(path: string): Promise<string> {
|
loadResource(path: string): Promise<string> {
|
||||||
return Promise.resolve(this.tsHost.readFile(path));
|
if (this.tsHost.fileExists(path)) {
|
||||||
|
return Promise.resolve(this.tsHost.readFile(path));
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error(`Resource ${path} not found.`))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +440,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
function find(fileName: string, data: MockData): MockData|undefined {
|
function find(fileName: string, data: MockData): MockData|undefined {
|
||||||
|
if (!data) return undefined;
|
||||||
let names = fileName.split('/');
|
let names = fileName.split('/');
|
||||||
if (names.length && !names[0].length) names.shift();
|
if (names.length && !names[0].length) names.shift();
|
||||||
let current = data;
|
let current = data;
|
||||||
|
|
|
@ -20,6 +20,7 @@ export interface PlatformReflectionCapabilities {
|
||||||
setter(name: string): SetterFn;
|
setter(name: string): SetterFn;
|
||||||
method(name: string): MethodFn;
|
method(name: string): MethodFn;
|
||||||
importUri(type: Type<any>): string;
|
importUri(type: Type<any>): string;
|
||||||
|
resourceUri(type: Type<any>): string;
|
||||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
||||||
resolveEnum(enumIdentifier: any, name: string): any;
|
resolveEnum(enumIdentifier: any, name: string): any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,6 +226,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
return `./${stringify(type)}`;
|
return `./${stringify(type)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceUri(type: any): string { return `./${stringify(type)}`; }
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
||||||
return runtime;
|
return runtime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ export class Reflector extends ReflectorReader {
|
||||||
|
|
||||||
importUri(type: any): string { return this.reflectionCapabilities.importUri(type); }
|
importUri(type: any): string { return this.reflectionCapabilities.importUri(type); }
|
||||||
|
|
||||||
|
resourceUri(type: any): string { return this.reflectionCapabilities.resourceUri(type); }
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
||||||
return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
|
return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export abstract class ReflectorReader {
|
||||||
abstract annotations(typeOrFunc: /*Type*/ any): any[];
|
abstract annotations(typeOrFunc: /*Type*/ any): any[];
|
||||||
abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
|
abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
|
||||||
abstract importUri(typeOrFunc: /*Type*/ any): string;
|
abstract importUri(typeOrFunc: /*Type*/ any): string;
|
||||||
|
abstract resourceUri(typeOrFunc: /*Type*/ any): string;
|
||||||
abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
||||||
abstract resolveEnum(identifier: any, name: string): any;
|
abstract resolveEnum(identifier: any, name: string): any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,12 @@ cp -v package.json $TMP
|
||||||
# Generate the metadata for the third-party modules
|
# Generate the metadata for the third-party modules
|
||||||
node ./node_modules/@angular/tsc-wrapped/src/main -p third_party_src/tsconfig-build.json
|
node ./node_modules/@angular/tsc-wrapped/src/main -p third_party_src/tsconfig-build.json
|
||||||
|
|
||||||
|
# Generate the the bundle modules
|
||||||
|
node ./node_modules/@angular/tsc-wrapped/src/main -p flat_module/tsconfig-build.json
|
||||||
|
|
||||||
|
# Copy the html files from source to the emitted output
|
||||||
|
cp flat_module/src/*.html node_modules/flat_module/src
|
||||||
|
|
||||||
./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xlf --locale=fi --i18nFormat=xlf
|
./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xlf --locale=fi --i18nFormat=xlf
|
||||||
|
|
||||||
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xlf --locale=fr
|
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xlf --locale=fr
|
||||||
|
|
Loading…
Reference in New Issue