139 lines
4.9 KiB
TypeScript
Raw Normal View History

/**
* @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
*/
/**
* 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 {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import {readFileSync} from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {NgHost, NgHostContext} from './ng_host';
import {PathMappedNgHost} from './path_mapped_ng_host';
import {Console} from './private_import_core';
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const PREAMBLE = `/**
* @fileoverview This file is generated by the Angular 2 template compiler.
* Do not edit.
* @suppress {suspiciousCode,uselessCode,missingProperties}
*/
/* tslint:disable */
`;
export class CodeGenerator {
constructor(
private options: AngularCompilerOptions, private program: ts.Program,
public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector,
private compiler: compiler.AotCompiler, private ngHost: NgHost) {}
// Write codegen in a directory structure matching the sources.
private calculateEmitPath(filePath: string): string {
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
2016-05-03 18:49:59 -07:00
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
codegen(options: {transitiveModules: boolean}): Promise<any> {
const staticSymbols =
extractProgramSymbols(this.program, this.staticReflector, this.ngHost, this.options);
return this.compiler.compileModules(staticSymbols, options).then(generatedModules => {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
this.host.writeFile(
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
});
});
}
static create(
options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program,
compilerHost: ts.CompilerHost, ngHost?: NgHost): CodeGenerator {
const transFile = cliOptions.i18nFile;
const locale = cliOptions.locale;
let transContent: string = '';
if (transFile) {
if (!locale) {
throw new Error(
`The translation file (${transFile}) locale must be provided. Use the --locale option.`);
}
transContent = readFileSync(transFile, 'utf8');
}
const {compiler: aotCompiler, reflector} = compiler.createAotCompiler(ngHost, {
debug: options.debug === true,
translations: transContent,
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale
});
return new CodeGenerator(options, program, compilerHost, reflector, aotCompiler, ngHost);
}
}
export function extractProgramSymbols(
program: ts.Program, staticReflector: compiler.StaticReflector, ngHost: NgHost,
options: AngularCompilerOptions): compiler.StaticSymbol[] {
// Compare with false since the default should be true
const skipFileNames =
options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
const staticSymbols: compiler.StaticSymbol[] = [];
program.getSourceFiles()
.filter(sourceFile => !skipFileNames.test(sourceFile.fileName))
.forEach(sourceFile => {
const absSrcPath = ngHost.getCanonicalFileName(sourceFile.fileName);
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSrcPath}`);
return;
}
const metadata = moduleMetadata['metadata'];
if (!metadata) {
return;
}
for (const symbol of Object.keys(metadata)) {
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
// Ignore symbols that are only included to record error information.
continue;
}
staticSymbols.push(staticReflector.findDeclaration(absSrcPath, symbol, absSrcPath));
}
});
return staticSymbols;
}