Misko Hevery 80deac5cde Revert "chore: Remove unnecessary calls to Parse5DomAdapter"
This reverts commit 387a90e5465ceed7b2c634695d17f4f835dd5687.
2016-06-14 19:40:43 -07:00

178 lines
7.7 KiB
TypeScript

/**
* Transform template html and css into executable code.
* Intended to be used in a build step.
*/
import * as compiler from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core';
import {Parse5DomAdapter} from '@angular/platform-server';
import {AngularCompilerOptions} from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const PREAMBLE = `/**
* This file is generated by the Angular 2 template compiler.
* Do not edit.
*/
/* tslint:disable */
`;
export class CodeGenerator {
constructor(
private options: AngularCompilerOptions, private program: ts.Program,
public host: ts.CompilerHost, private staticReflector: StaticReflector,
private resolver: CompileMetadataResolver, private compiler: compiler.OfflineCompiler,
private reflectorHost: ReflectorHost) {}
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
const directiveType = metadata.type.runtime;
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
return Promise.all(directives.map(d => this.compiler.normalizeDirectiveMetadata(d)))
.then(normalizedDirectives => {
const pipes = this.resolver.getViewPipesMetadata(directiveType);
return new compiler.NormalizedComponentWithViewDirectives(
metadata, normalizedDirectives, pipes);
});
};
return Promise.all(metadatas.map(normalize))
.then(
normalizedCompWithDirectives =>
this.compiler.compileTemplates(normalizedCompWithDirectives));
}
private readComponents(absSourcePath: string) {
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
const moduleMetadata = this.staticReflector.getModuleMetadata(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);
let directive: compiler.CompileDirectiveMetadata;
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);
if (!directive || !directive.isComponent) {
continue;
}
result.push(this.compiler.normalizeDirectiveMetadata(directive));
}
return result;
}
// Write codegen in a directory structure matching the sources.
private calculateEmitPath(filePath: string) {
let root = this.options.basePath;
for (let eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
return path.join(this.options.genDir, path.relative(root, filePath));
}
// TODO(tbosch): add a cache for shared css files
// TODO(tbosch): detect cycles!
private generateStylesheet(filepath: string, shim: boolean): Promise<any> {
return this.compiler.loadAndCompileStylesheet(filepath, shim, '.ts')
.then((sourceWithImports) => {
const emitPath = this.calculateEmitPath(sourceWithImports.source.moduleUrl);
// TODO(alexeagle): should include the sourceFile to the WriteFileCallback
this.host.writeFile(emitPath, PREAMBLE + sourceWithImports.source.source, false);
return Promise.all(
sourceWithImports.importedUrls.map(url => this.generateStylesheet(url, shim)));
});
}
codegen(): Promise<any> {
Parse5DomAdapter.makeCurrent();
let stylesheetPromises: Promise<any>[] = [];
const generateOneFile = (absSourcePath: string) =>
Promise.all(this.readComponents(absSourcePath))
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
if (!metadatas || !metadatas.length) {
return;
}
metadatas.forEach((metadata) => {
let stylesheetPaths = metadata && metadata.template && metadata.template.styleUrls;
if (stylesheetPaths) {
stylesheetPaths.forEach((path) => {
stylesheetPromises.push(this.generateStylesheet(
path, metadata.template.encapsulation === ViewEncapsulation.Emulated));
});
}
});
return this.generateSource(metadatas);
})
.then(generated => {
if (generated) {
const sourceFile = this.program.getSourceFile(absSourcePath);
const emitPath = this.calculateEmitPath(generated.moduleUrl);
this.host.writeFile(
emitPath, PREAMBLE + generated.source, false, () => {}, [sourceFile]);
}
})
.catch((e) => { console.error(e.stack); });
var compPromises = this.program.getSourceFiles()
.map(sf => sf.fileName)
.filter(f => !GENERATED_FILES.test(f))
.map(generateOneFile);
return Promise.all(stylesheetPromises.concat(compPromises));
}
static create(
options: AngularCompilerOptions, program: ts.Program, compilerHost: ts.CompilerHost,
reflectorHostContext?: ReflectorHostContext): CodeGenerator {
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
const staticReflector = new StaticReflector(reflectorHost);
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser = new HtmlParser();
const config = new compiler.CompilerConfig({
genDebugInfo: true,
defaultEncapsulation: ViewEncapsulation.Emulated,
logBindingUpdate: false,
useJit: false,
platformDirectives: [],
platformPipes: []
});
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
const parser = new Parser(new Lexer());
const tmplParser = new TemplateParser(
parser, new DomElementSchemaRegistry(), htmlParser,
/*console*/ null, []);
const offlineCompiler = new compiler.OfflineCompiler(
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new TypeScriptEmitter(reflectorHost), xhr);
const resolver = new CompileMetadataResolver(
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), config, staticReflector);
return new CodeGenerator(
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost);
}
}