feat(compiler): use typescript for resolving resource paths
This can also be customized via the new method `resourceNameToFileName` in the `CompilerHost`.
This commit is contained in:
parent
2572bf508f
commit
43226cb93d
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotCompilerHost, StaticSymbol, syntaxError} from '@angular/compiler';
|
import {AotCompilerHost, StaticSymbol, UrlResolver, createOfflineCompileUrlResolver, syntaxError} from '@angular/compiler';
|
||||||
import {AngularCompilerOptions, CollectorOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, CollectorOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
@ -40,6 +40,8 @@ export abstract class BaseAotCompilerHost<C extends BaseAotCompilerHostContext>
|
||||||
|
|
||||||
abstract moduleNameToFileName(m: string, containingFile: string): string|null;
|
abstract moduleNameToFileName(m: string, containingFile: string): string|null;
|
||||||
|
|
||||||
|
abstract resourceNameToFileName(m: string, containingFile: string): string|null;
|
||||||
|
|
||||||
abstract fileNameToModuleName(importedFile: string, containingFile: string): string|null;
|
abstract fileNameToModuleName(importedFile: string, containingFile: string): string|null;
|
||||||
|
|
||||||
abstract toSummaryFileName(fileName: string, referringSrcFileName: string): string;
|
abstract toSummaryFileName(fileName: string, referringSrcFileName: string): string;
|
||||||
|
@ -234,6 +236,7 @@ export class CompilerHost extends BaseAotCompilerHost<CompilerHostContext> {
|
||||||
private isGenDirChildOfRootDir: boolean;
|
private isGenDirChildOfRootDir: boolean;
|
||||||
private genDir: string;
|
private genDir: string;
|
||||||
protected resolveModuleNameHost: CompilerHostContext;
|
protected resolveModuleNameHost: CompilerHostContext;
|
||||||
|
private urlResolver: UrlResolver;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext,
|
program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext,
|
||||||
|
@ -266,6 +269,7 @@ export class CompilerHost extends BaseAotCompilerHost<CompilerHostContext> {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
this.urlResolver = createOfflineCompileUrlResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
toSummaryFileName(fileName: string, referringSrcFileName: string): string {
|
toSummaryFileName(fileName: string, referringSrcFileName: string): string {
|
||||||
|
@ -382,6 +386,10 @@ export class CompilerHost extends BaseAotCompilerHost<CompilerHostContext> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceNameToFileName(m: string, containingFile: string): string {
|
||||||
|
return this.urlResolver.resolve(containingFile, m);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotSummaryResolver, CompileMetadataResolver, CompilePipeSummary, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, analyzeNgModules, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
|
import {AotSummaryResolver, CompileMetadataResolver, CompilePipeSummary, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, analyzeNgModules, extractProgramSymbols} from '@angular/compiler';
|
||||||
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
|
@ -126,10 +126,13 @@ export interface CompilerHost extends ts.CompilerHost {
|
||||||
/**
|
/**
|
||||||
* Converts a file path to a module name that can be used as an `import ...`
|
* Converts a file path to a module name that can be used as an `import ...`
|
||||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
||||||
*
|
|
||||||
* See ImportResolver.
|
|
||||||
*/
|
*/
|
||||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string|null;
|
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string|null;
|
||||||
|
/**
|
||||||
|
* Converts a file path for a resource that is used in a source file or another resource
|
||||||
|
* into a filepath.
|
||||||
|
*/
|
||||||
|
resourceNameToFileName(resourceName: string, containingFilePath: string): string|null;
|
||||||
/**
|
/**
|
||||||
* Converts a file name into a representation that should be stored in a summary file.
|
* Converts a file name into a representation that should be stored in a summary file.
|
||||||
* This has to include changing the suffix as well.
|
* This has to include changing the suffix as well.
|
||||||
|
|
|
@ -26,6 +26,7 @@ export function createCompilerHost(
|
||||||
host.fileNameToModuleName = mixin.fileNameToModuleName.bind(mixin);
|
host.fileNameToModuleName = mixin.fileNameToModuleName.bind(mixin);
|
||||||
host.toSummaryFileName = mixin.toSummaryFileName.bind(mixin);
|
host.toSummaryFileName = mixin.toSummaryFileName.bind(mixin);
|
||||||
host.fromSummaryFileName = mixin.fromSummaryFileName.bind(mixin);
|
host.fromSummaryFileName = mixin.fromSummaryFileName.bind(mixin);
|
||||||
|
host.resourceNameToFileName = mixin.resourceNameToFileName.bind(mixin);
|
||||||
|
|
||||||
// Make sure we do not `host.realpath()` from TS as we do not want to resolve symlinks.
|
// Make sure we do not `host.realpath()` from TS as we do not want to resolve symlinks.
|
||||||
// https://github.com/Microsoft/TypeScript/issues/9552
|
// https://github.com/Microsoft/TypeScript/issues/9552
|
||||||
|
@ -35,26 +36,23 @@ export function createCompilerHost(
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompilerHostMixin {
|
class CompilerHostMixin {
|
||||||
private moduleFileNames = new Map<string, string|null>();
|
|
||||||
private rootDirs: string[];
|
private rootDirs: string[];
|
||||||
private basePath: string;
|
private basePath: string;
|
||||||
private moduleResolutionHost: ModuleFilenameResolutionHost;
|
private moduleResolutionHost: ModuleFilenameResolutionHost;
|
||||||
|
private moduleResolutionCache: ts.ModuleResolutionCache;
|
||||||
|
|
||||||
constructor(private context: ts.ModuleResolutionHost, private options: CompilerOptions) {
|
constructor(private context: ts.CompilerHost, private options: CompilerOptions) {
|
||||||
// normalize the path so that it never ends with '/'.
|
// normalize the path so that it never ends with '/'.
|
||||||
this.basePath = normalizePath(this.options.basePath !);
|
this.basePath = normalizePath(this.options.basePath !);
|
||||||
this.rootDirs = (this.options.rootDirs || [
|
this.rootDirs = (this.options.rootDirs || [
|
||||||
this.options.basePath !
|
this.options.basePath !
|
||||||
]).map(p => path.resolve(this.basePath, normalizePath(p)));
|
]).map(p => path.resolve(this.basePath, normalizePath(p)));
|
||||||
this.moduleResolutionHost = createModuleFilenameResolverHost(context);
|
this.moduleResolutionHost = createModuleFilenameResolverHost(context);
|
||||||
|
this.moduleResolutionCache = ts.createModuleResolutionCache(
|
||||||
|
this.context.getCurrentDirectory !(), this.context.getCanonicalFileName.bind(this.context));
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleNameToFileName(m: string, containingFile: string): string|null {
|
moduleNameToFileName(m: string, containingFile: string): string|null {
|
||||||
const key = m + ':' + (containingFile || '');
|
|
||||||
let result: string|null = this.moduleFileNames.get(key) || null;
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (!containingFile) {
|
if (!containingFile) {
|
||||||
if (m.indexOf('.') === 0) {
|
if (m.indexOf('.') === 0) {
|
||||||
throw new Error('Resolution of relative paths requires a containing file.');
|
throw new Error('Resolution of relative paths requires a containing file.');
|
||||||
|
@ -62,17 +60,17 @@ class CompilerHostMixin {
|
||||||
// Any containing file gives the same result for absolute imports
|
// Any containing file gives the same result for absolute imports
|
||||||
containingFile = path.join(this.basePath, 'index.ts');
|
containingFile = path.join(this.basePath, 'index.ts');
|
||||||
}
|
}
|
||||||
const resolved =
|
const resolved = ts.resolveModuleName(
|
||||||
ts.resolveModuleName(m, containingFile, this.options, this.moduleResolutionHost)
|
m, containingFile, this.options, this.moduleResolutionHost,
|
||||||
|
this.moduleResolutionCache)
|
||||||
.resolvedModule;
|
.resolvedModule;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||||
}
|
}
|
||||||
result = resolved.resolvedFileName;
|
return resolved.resolvedFileName;
|
||||||
}
|
}
|
||||||
this.moduleFileNames.set(key, result);
|
return null;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,6 +138,17 @@ class CompilerHostMixin {
|
||||||
}
|
}
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceNameToFileName(resourceName: string, containingFile: string): string|null {
|
||||||
|
// Note: we convert package paths into relative paths to be compatible with the the
|
||||||
|
// previous implementation of UrlResolver.
|
||||||
|
if (resourceName && resourceName.charAt(0) !== '.' && !path.isAbsolute(resourceName)) {
|
||||||
|
resourceName = `./${resourceName}`;
|
||||||
|
}
|
||||||
|
const filePathWithNgResource =
|
||||||
|
this.moduleNameToFileName(addNgResourceSuffix(resourceName), containingFile);
|
||||||
|
return filePathWithNgResource ? stripNgResourceSuffix(filePathWithNgResource) : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModuleFilenameResolutionHost extends ts.ModuleResolutionHost {
|
interface ModuleFilenameResolutionHost extends ts.ModuleResolutionHost {
|
||||||
|
@ -156,6 +165,7 @@ function createModuleFilenameResolverHost(host: ts.ModuleResolutionHost):
|
||||||
// This is needed as we use ts.resolveModuleName in DefaultModuleFilenameResolver
|
// This is needed as we use ts.resolveModuleName in DefaultModuleFilenameResolver
|
||||||
// and it should be able to resolve summary file names.
|
// and it should be able to resolve summary file names.
|
||||||
resolveModuleNameHost.fileExists = (fileName: string): boolean => {
|
resolveModuleNameHost.fileExists = (fileName: string): boolean => {
|
||||||
|
fileName = stripNgResourceSuffix(fileName);
|
||||||
if (assumedExists.has(fileName)) {
|
if (assumedExists.has(fileName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -217,3 +227,11 @@ function getNodeModulesPrefix(filePath: string): string|null {
|
||||||
function normalizePath(p: string): string {
|
function normalizePath(p: string): string {
|
||||||
return path.normalize(path.join(p, '.')).replace(/\\/g, '/');
|
return path.normalize(path.join(p, '.')).replace(/\\/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripNgResourceSuffix(fileName: string): string {
|
||||||
|
return fileName.replace(/\.\$ngresource\$.*/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNgResourceSuffix(fileName: string): string {
|
||||||
|
return `${fileName}.$ngresource$`;
|
||||||
|
}
|
||||||
|
|
|
@ -315,6 +315,10 @@ class AotCompilerHostImpl extends BaseAotCompilerHost<CompilerHost> {
|
||||||
return this.context.fileNameToModuleName(importedFile, containingFile);
|
return this.context.fileNameToModuleName(importedFile, containingFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceNameToFileName(resourceName: string, containingFile: string): string|null {
|
||||||
|
return this.context.resourceNameToFileName(resourceName, containingFile);
|
||||||
|
}
|
||||||
|
|
||||||
toSummaryFileName(fileName: string, referringSrcFileName: string): string {
|
toSummaryFileName(fileName: string, referringSrcFileName: string): string {
|
||||||
return this.context.toSummaryFileName(fileName, referringSrcFileName);
|
return this.context.toSummaryFileName(fileName, referringSrcFileName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,4 +100,27 @@ describe('NgCompilerHost', () => {
|
||||||
.toBe('/tmp/src/a/child.d.ts');
|
.toBe('/tmp/src/a/child.d.ts');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resourceNameToFileName', () => {
|
||||||
|
it('should resolve a relative import', () => {
|
||||||
|
const ngHost = createHost({files: {'tmp': {'src': {'a': {'child.html': '<div>'}}}}});
|
||||||
|
expect(ngHost.resourceNameToFileName('./a/child.html', '/tmp/src/index.ts'))
|
||||||
|
.toBe('/tmp/src/a/child.html');
|
||||||
|
|
||||||
|
expect(ngHost.resourceNameToFileName('./a/non-existing.html', '/tmp/src/index.ts'))
|
||||||
|
.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve package paths as relative paths', () => {
|
||||||
|
const ngHost = createHost({files: {'tmp': {'src': {'a': {'child.html': '<div>'}}}}});
|
||||||
|
expect(ngHost.resourceNameToFileName('a/child.html', '/tmp/src/index.ts'))
|
||||||
|
.toBe('/tmp/src/a/child.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve absolute paths', () => {
|
||||||
|
const ngHost = createHost({files: {'tmp': {'src': {'a': {'child.html': '<div>'}}}}});
|
||||||
|
expect(ngHost.resourceNameToFileName('/tmp/src/a/child.html', '/tmp/src/index.ts'))
|
||||||
|
.toBe('/tmp/src/a/child.html');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MissingTranslationStrategy, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
import {MissingTranslationStrategy, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
|
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveResolver} from '../directive_resolver';
|
import {DirectiveResolver} from '../directive_resolver';
|
||||||
|
@ -22,7 +23,8 @@ import {PipeResolver} from '../pipe_resolver';
|
||||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||||
import {StyleCompiler} from '../style_compiler';
|
import {StyleCompiler} from '../style_compiler';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {createOfflineCompileUrlResolver} from '../url_resolver';
|
import {UrlResolver} from '../url_resolver';
|
||||||
|
import {syntaxError} from '../util';
|
||||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
import {AotCompiler} from './compiler';
|
import {AotCompiler} from './compiler';
|
||||||
|
@ -33,6 +35,19 @@ import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {StaticSymbolResolver} from './static_symbol_resolver';
|
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {AotSummaryResolver} from './summary_resolver';
|
import {AotSummaryResolver} from './summary_resolver';
|
||||||
|
|
||||||
|
export function createAotUrlResolver(host: {
|
||||||
|
resourceNameToFileName(resourceName: string, containingFileName: string): string | null;
|
||||||
|
}): UrlResolver {
|
||||||
|
return {
|
||||||
|
resolve: (basePath: string, url: string) => {
|
||||||
|
const filePath = host.resourceNameToFileName(url, basePath);
|
||||||
|
if (!filePath) {
|
||||||
|
throw syntaxError(`Couldn't resolve resource ${url} from ${basePath}`);
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new AotCompiler based on options and a host.
|
* Creates a new AotCompiler based on options and a host.
|
||||||
|
@ -41,7 +56,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||||
{compiler: AotCompiler, reflector: StaticReflector} {
|
{compiler: AotCompiler, reflector: StaticReflector} {
|
||||||
let translations: string = options.translations || '';
|
let translations: string = options.translations || '';
|
||||||
|
|
||||||
const urlResolver = createOfflineCompileUrlResolver();
|
const urlResolver = createAotUrlResolver(compilerHost);
|
||||||
const symbolCache = new StaticSymbolCache();
|
const symbolCache = new StaticSymbolCache();
|
||||||
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
|
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
|
||||||
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
|
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
|
||||||
|
|
|
@ -14,6 +14,11 @@ import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||||
|
/**
|
||||||
|
* Converts a path that refers to a resource into an absolute filePath
|
||||||
|
* that can be later on used for loading the resource via `loadResource.
|
||||||
|
*/
|
||||||
|
resourceNameToFileName(resourceName: string, containingFileName: string): string|null;
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
|
|
||||||
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
||||||
|
import {createAotUrlResolver} from '../aot/compiler_factory';
|
||||||
import {StaticReflector} from '../aot/static_reflector';
|
import {StaticReflector} from '../aot/static_reflector';
|
||||||
import {StaticSymbolCache} from '../aot/static_symbol';
|
import {StaticSymbolCache} from '../aot/static_symbol';
|
||||||
import {StaticSymbolResolver, StaticSymbolResolverHost} from '../aot/static_symbol_resolver';
|
import {StaticSymbolResolver, StaticSymbolResolverHost} from '../aot/static_symbol_resolver';
|
||||||
|
@ -28,14 +29,21 @@ import {NgModuleResolver} from '../ng_module_resolver';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
import {PipeResolver} from '../pipe_resolver';
|
import {PipeResolver} from '../pipe_resolver';
|
||||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||||
import {createOfflineCompileUrlResolver} from '../url_resolver';
|
import {syntaxError} from '../util';
|
||||||
|
|
||||||
import {MessageBundle} from './message_bundle';
|
import {MessageBundle} from './message_bundle';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface ExtractorHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
export interface ExtractorHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||||
|
/**
|
||||||
|
* Converts a path that refers to a resource into an absolute filePath
|
||||||
|
* that can be lateron used for loading the resource via `loadResource.
|
||||||
|
*/
|
||||||
|
resourceNameToFileName(path: string, containingFile: string): string|null;
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
@ -87,7 +95,7 @@ export class Extractor {
|
||||||
{extractor: Extractor, staticReflector: StaticReflector} {
|
{extractor: Extractor, staticReflector: StaticReflector} {
|
||||||
const htmlParser = new HtmlParser();
|
const htmlParser = new HtmlParser();
|
||||||
|
|
||||||
const urlResolver = createOfflineCompileUrlResolver();
|
const urlResolver = createAotUrlResolver(host);
|
||||||
const symbolCache = new StaticSymbolCache();
|
const symbolCache = new StaticSymbolCache();
|
||||||
const summaryResolver = new AotSummaryResolver(host, symbolCache);
|
const summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||||
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
|
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
|
||||||
|
|
|
@ -11,14 +11,6 @@ import {Inject, InjectionToken, PACKAGE_ROOT_URL} from '@angular/core';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link UrlResolver} with no package prefix.
|
|
||||||
*/
|
|
||||||
export function createUrlResolverWithoutPackagePrefix(): UrlResolver {
|
|
||||||
return new UrlResolver();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createOfflineCompileUrlResolver(): UrlResolver {
|
export function createOfflineCompileUrlResolver(): UrlResolver {
|
||||||
return new UrlResolver('.');
|
return new UrlResolver('.');
|
||||||
}
|
}
|
||||||
|
@ -47,9 +39,12 @@ export const DEFAULT_PACKAGE_URL_PROVIDER = {
|
||||||
* Attacker-controlled data introduced by a template could expose your
|
* Attacker-controlled data introduced by a template could expose your
|
||||||
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
||||||
*/
|
*/
|
||||||
@CompilerInjectable()
|
export interface UrlResolver { resolve(baseUrl: string, url: string): string; }
|
||||||
export class UrlResolver {
|
|
||||||
constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string|null = null) {}
|
export interface UrlResolverCtor { new (packagePrefix?: string|null): UrlResolver; }
|
||||||
|
|
||||||
|
export const UrlResolver: UrlResolverCtor = class UrlResolverImpl {
|
||||||
|
constructor(private _packagePrefix: string|null = null) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the `url` given the `baseUrl`:
|
* Resolves the `url` given the `baseUrl`:
|
||||||
|
@ -75,7 +70,7 @@ export class UrlResolver {
|
||||||
}
|
}
|
||||||
return resolvedUrl;
|
return resolvedUrl;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the scheme of a URL.
|
* Extract the scheme of a URL.
|
||||||
|
|
|
@ -330,8 +330,15 @@ export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
private metadataCollector = new MetadataCollector();
|
private metadataCollector = new MetadataCollector();
|
||||||
private metadataVisible: boolean = true;
|
private metadataVisible: boolean = true;
|
||||||
private dtsAreSource: boolean = true;
|
private dtsAreSource: boolean = true;
|
||||||
|
private resolveModuleNameHost: ts.ModuleResolutionHost;
|
||||||
|
|
||||||
constructor(private tsHost: MockCompilerHost) {}
|
constructor(private tsHost: MockCompilerHost) {
|
||||||
|
this.resolveModuleNameHost = Object.create(tsHost);
|
||||||
|
this.resolveModuleNameHost.fileExists = (fileName) => {
|
||||||
|
fileName = stripNgResourceSuffix(fileName);
|
||||||
|
return tsHost.fileExists(fileName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
hideMetadata() { this.metadataVisible = false; }
|
hideMetadata() { this.metadataVisible = false; }
|
||||||
|
|
||||||
|
@ -369,11 +376,22 @@ export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
moduleName = moduleName.replace(EXT, '');
|
moduleName = moduleName.replace(EXT, '');
|
||||||
const resolved = ts.resolveModuleName(
|
const resolved = ts.resolveModuleName(
|
||||||
moduleName, containingFile.replace(/\\/g, '/'),
|
moduleName, containingFile.replace(/\\/g, '/'),
|
||||||
{baseDir: '/', genDir: '/'}, this.tsHost)
|
{baseDir: '/', genDir: '/'}, this.resolveModuleNameHost)
|
||||||
.resolvedModule;
|
.resolvedModule;
|
||||||
return resolved ? resolved.resolvedFileName : null;
|
return resolved ? resolved.resolvedFileName : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceNameToFileName(resourceName: string, containingFile: string) {
|
||||||
|
// Note: we convert package paths into relative paths to be compatible with the the
|
||||||
|
// previous implementation of UrlResolver.
|
||||||
|
if (resourceName && resourceName.charAt(0) !== '.' && !path.isAbsolute(resourceName)) {
|
||||||
|
resourceName = `./${resourceName}`;
|
||||||
|
}
|
||||||
|
const filePathWithNgResource =
|
||||||
|
this.moduleNameToFileName(addNgResourceSuffix(resourceName), containingFile);
|
||||||
|
return filePathWithNgResource ? stripNgResourceSuffix(filePathWithNgResource) : null;
|
||||||
|
}
|
||||||
|
|
||||||
// AotSummaryResolverHost
|
// AotSummaryResolverHost
|
||||||
loadSummary(filePath: string): string|null { return this.tsHost.readFile(filePath); }
|
loadSummary(filePath: string): string|null { return this.tsHost.readFile(filePath); }
|
||||||
|
|
||||||
|
@ -647,3 +665,11 @@ export function compile(
|
||||||
}
|
}
|
||||||
return {genFiles, outDir};
|
return {genFiles, outDir};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripNgResourceSuffix(fileName: string): string {
|
||||||
|
return fileName.replace(/\.\$ngresource\$.*/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNgResourceSuffix(fileName: string): string {
|
||||||
|
return `${fileName}.$ngresource$`;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue