mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
I put an extractor into your extract so you can extract while you extract. This allows integrators to call Extractor as a library. Also refactors Extractor a bit so that callers need fewer arguments or arguments that are at the right semantic level. The refactoring causes no function change.
157 lines
6.4 KiB
TypeScript
157 lines
6.4 KiB
TypeScript
/**
|
|
* @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
|
|
*/
|
|
|
|
|
|
/**
|
|
* Extract i18n messages from source code
|
|
*
|
|
* TODO(vicb): factorize code with the CodeGenerator
|
|
*/
|
|
// Must be imported first, because angular2 decorators throws on load.
|
|
import 'reflect-metadata';
|
|
|
|
import * as compiler from '@angular/compiler';
|
|
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
|
|
import * as tsc from '@angular/tsc-wrapped';
|
|
import * as path from 'path';
|
|
import * as ts from 'typescript';
|
|
|
|
import {Console} from './private_import_core';
|
|
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
|
import {StaticReflector, StaticSymbol} from './static_reflector';
|
|
|
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
|
|
|
export class Extractor {
|
|
constructor(
|
|
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
|
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
|
private metadataResolver: compiler.CompileMetadataResolver,
|
|
private directiveNormalizer: compiler.DirectiveNormalizer) {}
|
|
|
|
private readFileMetadata(absSourcePath: string): FileMetadata {
|
|
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
|
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
|
|
if (!moduleMetadata) {
|
|
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
|
return result;
|
|
}
|
|
const metadata = moduleMetadata['metadata'];
|
|
const symbols = metadata && Object.keys(metadata);
|
|
if (!symbols || !symbols.length) {
|
|
return result;
|
|
}
|
|
for (const symbol of symbols) {
|
|
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
|
// Ignore symbols that are only included to record error information.
|
|
continue;
|
|
}
|
|
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
|
const annotations = this.staticReflector.annotations(staticType);
|
|
annotations.forEach((annotation) => {
|
|
if (annotation instanceof NgModule) {
|
|
result.ngModules.push(staticType);
|
|
} else if (annotation instanceof Component) {
|
|
result.components.push(staticType);
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
extract(): Promise<compiler.MessageBundle> {
|
|
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
|
GENERATED_OR_DTS_FILES :
|
|
GENERATED_FILES;
|
|
const filePaths =
|
|
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !skipFileNames.test(f));
|
|
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
|
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
|
ngModules.push(...fileMeta.ngModules);
|
|
return ngModules;
|
|
}, <StaticSymbol[]>[]);
|
|
const analyzedNgModules = compiler.analyzeModules(ngModules, this.metadataResolver);
|
|
const errors: compiler.ParseError[] = [];
|
|
|
|
let bundlePromise =
|
|
Promise
|
|
.all(fileMetas.map((fileMeta) => {
|
|
const url = fileMeta.fileUrl;
|
|
return Promise.all(fileMeta.components.map(compType => {
|
|
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
|
|
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
|
|
if (!ngModule) {
|
|
throw new Error(
|
|
`Cannot determine the module for component ${compMeta.type.name}!`);
|
|
}
|
|
return Promise
|
|
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
|
dirMeta =>
|
|
this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
|
.then((normalizedCompWithDirectives) => {
|
|
const compMeta = normalizedCompWithDirectives[0];
|
|
const html = compMeta.template.template;
|
|
const interpolationConfig =
|
|
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
|
errors.push(
|
|
...this.messageBundle.updateFromTemplate(html, url, interpolationConfig));
|
|
});
|
|
}));
|
|
}))
|
|
.then(_ => this.messageBundle);
|
|
|
|
if (errors.length) {
|
|
throw new Error(errors.map(e => e.toString()).join('\n'));
|
|
}
|
|
|
|
return bundlePromise;
|
|
}
|
|
|
|
static create(
|
|
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
|
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
|
|
reflectorHost?: ReflectorHost): Extractor {
|
|
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
|
|
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
|
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
|
|
const staticReflector = new StaticReflector(reflectorHost);
|
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
|
|
|
const config = new compiler.CompilerConfig({
|
|
genDebugInfo: options.debug === true,
|
|
defaultEncapsulation: ViewEncapsulation.Emulated,
|
|
logBindingUpdate: false,
|
|
useJit: false
|
|
});
|
|
|
|
const normalizer =
|
|
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
|
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
|
const resolver = new compiler.CompileMetadataResolver(
|
|
new compiler.NgModuleResolver(staticReflector),
|
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
|
elementSchemaRegistry, staticReflector);
|
|
|
|
// TODO(vicb): implicit tags & attributes
|
|
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
|
|
|
return new Extractor(
|
|
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
|
|
normalizer);
|
|
}
|
|
}
|
|
|
|
interface FileMetadata {
|
|
fileUrl: string;
|
|
components: StaticSymbol[];
|
|
ngModules: StaticSymbol[];
|
|
}
|