refactor(compiler): store metadata of top level symbols also in summaries (#13289)

This allows a build using summaries to not need .metadata.json files at all
any more.

Part of #12787
This commit is contained in:
Tobias Bosch
2016-12-15 09:12:40 -08:00
committed by Igor Minar
parent 01ca2db6ae
commit 33910ddfc9
52 changed files with 1702 additions and 1011 deletions

View File

@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT ex (#PCDATA)>
]>
<messagebundle>
<msg id="3772663375917578720">other-3rdP-component</msg>
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
<msg id="3492007542396725315">Welcome</msg>
<msg id="3772663375917578720">other-3rdP-component</msg>
</messagebundle>
`;
@ -44,10 +44,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source>
<target/>
@ -58,6 +54,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<source>Welcome</source>
<target/>
</trans-unit>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -52,12 +52,9 @@ function codeGenTest() {
const config = tsc.readConfiguration(project, basePath);
const hostContext = new NodeCompilerHostContext();
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
const host: ts.CompilerHost = Object.assign({}, delegateHost, {
writeFile: (fileName: string, ...rest: any[]) => {
wroteFiles.push(fileName);
return delegateHost.writeFile.call(delegateHost, fileName, ...rest);
}
});
const host: ts.CompilerHost = Object.assign(
{}, delegateHost,
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath;

View File

@ -27,9 +27,14 @@ function main() {
const basePath = path.resolve(__dirname, '..');
const project = path.resolve(basePath, 'tsconfig-build.json');
const readFiles: string[] = [];
const writtenFiles: {fileName: string, content: string}[] = [];
class AssertingHostContext extends NodeCompilerHostContext {
readFile(fileName: string): string {
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName)) {
// Only allow to read summaries from node_modules
return null;
}
readFiles.push(path.relative(basePath, fileName));
return super.readFile(fileName);
}
@ -45,16 +50,29 @@ function main() {
config.ngOptions.generateCodeForLibraries = false;
console.log(`>>> running codegen for ${project}`);
codegen(config, (host) => new AssertingHostContext())
codegen(
config,
(host) => {
host.writeFile = (fileName: string, content: string) => {
fileName = path.relative(basePath, fileName);
writtenFiles.push({fileName, content});
};
return new AssertingHostContext();
})
.then((exitCode: any) => {
console.log(`>>> codegen done, asserting read files`);
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
console.log(`>>> asserting written files`);
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
console.log(`done, no errors.`);
process.exit(exitCode);
})
@ -97,4 +115,11 @@ function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
}
function assertWrittenFile(
files: {fileName: string, content: string}[], filePattern: RegExp, contentPattern: RegExp) {
assert(
files.some(file => filePattern.test(file.fileName) && contentPattern.test(file.content)),
`Expected some written files for ${filePattern} and content ${contentPattern}`);
}
main();

View File

@ -21,9 +21,7 @@ import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core';
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_META_FILES = /\.json$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
const PREAMBLE = `/**
* @fileoverview This file is generated by the Angular 2 template compiler.
@ -102,14 +100,8 @@ export class CodeGenerator {
debug: options.debug === true,
translations: transContent,
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale,
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
GENERATED_FILES
locale: cliOptions.locale
});
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
}
}
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
}

View File

@ -16,6 +16,8 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
export interface CompilerHostContext extends ts.ModuleResolutionHost {
readResource(fileName: string): Promise<string>;
@ -28,6 +30,7 @@ export class CompilerHost implements AotCompilerHost {
protected basePath: string;
private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>();
protected resolveModuleNameHost: CompilerHostContext;
constructor(
protected program: ts.Program, protected options: AngularCompilerOptions,
@ -38,12 +41,31 @@ export class CompilerHost implements AotCompilerHost {
const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
this.resolveModuleNameHost = Object.create(this.context);
// When calling ts.resolveModuleName,
// additional allow checks for .d.ts files to be done based on
// checks for .ngsummary.json files,
// so that our codegen depends on fewer inputs and requires to be called
// less often.
// This is needed as we use ts.resolveModuleName in reflector_host
// and it should be able to resolve summary file names.
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
if (this.context.fileExists(fileName)) {
return true;
}
if (DTS.test(fileName)) {
const base = fileName.substring(0, fileName.length - 5);
return this.context.fileExists(base + '.ngsummary.json');
}
return false;
};
}
// We use absolute paths on disk as canonical.
getCanonicalFileName(fileName: string): string { return fileName; }
moduleNameToFileName(m: string, containingFile: string) {
moduleNameToFileName(m: string, containingFile: string): string|null {
if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
@ -53,7 +75,8 @@ export class CompilerHost implements AotCompilerHost {
}
m = m.replace(EXT, '');
const resolved =
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule;
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
};
@ -213,11 +236,21 @@ export class CompilerHost implements AotCompilerHost {
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
loadSummary(filePath: string): string { return this.context.readFile(filePath); }
loadSummary(filePath: string): string|null {
if (this.context.fileExists(filePath)) {
return this.context.readFile(filePath);
}
}
getOutputFileName(sourceFilePath: string): string {
return sourceFilePath.replace(EXT, '') + '.d.ts';
}
isSourceFile(filePath: string): boolean {
const excludeRegex =
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
return !excludeRegex.test(filePath);
}
}
export class CompilerHostContextAdapter {

View File

@ -17,7 +17,6 @@ import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {excludeFilePattern} from './codegen';
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
export class Extractor {
@ -36,8 +35,7 @@ export class Extractor {
if (!ngCompilerHost)
ngCompilerHost =
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
const {extractor: ngExtractor} = compiler.Extractor.create(
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
return new Extractor(ngExtractor, ngCompilerHost, program);
}
}

View File

@ -13,7 +13,7 @@
* something else.
*/
import {AotCompilerHost, StaticReflector} from '@angular/compiler';
import {AotCompilerHost, AotSummaryResolver, StaticReflector, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
@ -111,7 +111,10 @@ export class NgTools_InternalApi_NG_2 {
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
const staticReflector = new StaticReflector(ngCompilerHost);
const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver);
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
return Object.keys(routeMap).reduce(

View File

@ -29,13 +29,17 @@ describe('compiler-cli', () => {
"types": [],
"outDir": "built",
"declaration": true,
"module": "es2015"
"module": "es2015",
"moduleResolution": "node"
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"files": ["test.ts"]
}`);
const nodeModulesPath = path.resolve(basePath, 'node_modules');
fs.mkdirSync(nodeModulesPath);
fs.symlinkSync(path.resolve(__dirname, '..', '..'), path.resolve(nodeModulesPath, '@angular'));
});
// Restore reflector since AoT compiler will update it with a new static reflector