117 lines
4.6 KiB
TypeScript
Raw Normal View History

import {writeFileSync} from 'fs';
import {convertDecorators} from 'tsickle';
2016-05-26 10:45:37 -07:00
import * as ts from 'typescript';
import NgOptions from './options';
import {MetadataCollector} from './collector';
2016-05-26 10:45:37 -07:00
/**
* Implementation of CompilerHost that forwards all methods to another instance.
* Useful for partial implementations to override only methods they care about.
*/
export abstract class DelegatingHost implements ts.CompilerHost {
constructor(protected delegate: ts.CompilerHost) {}
getSourceFile =
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) =>
this.delegate.getSourceFile(fileName, languageVersion, onError);
getCancellationToken = () => this.delegate.getCancellationToken();
getDefaultLibFileName = (options: ts.CompilerOptions) =>
this.delegate.getDefaultLibFileName(options);
getDefaultLibLocation = () => this.delegate.getDefaultLibLocation();
writeFile: ts.WriteFileCallback = this.delegate.writeFile;
getCurrentDirectory = () => this.delegate.getCurrentDirectory();
getCanonicalFileName = (fileName: string) => this.delegate.getCanonicalFileName(fileName);
useCaseSensitiveFileNames = () => this.delegate.useCaseSensitiveFileNames();
getNewLine = () => this.delegate.getNewLine();
fileExists = (fileName: string) => this.delegate.fileExists(fileName);
readFile = (fileName: string) => this.delegate.readFile(fileName);
trace = (s: string) => this.delegate.trace(s);
directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName);
}
export class TsickleHost extends DelegatingHost {
// Additional diagnostics gathered by pre- and post-emit transformations.
public diagnostics: ts.Diagnostic[] = [];
private TSICKLE_SUPPORT = `
interface DecoratorInvocation {
type: Function;
args?: any[];
}
`;
constructor(delegate: ts.CompilerHost, private program: ts.Program) { super(delegate); }
getSourceFile =
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => {
const originalContent = this.delegate.readFile(fileName);
let newContent = originalContent;
if (!/\.d\.ts$/.test(fileName)) {
try {
const converted = convertDecorators(
this.program.getTypeChecker(), this.program.getSourceFile(fileName));
if (converted.diagnostics) {
this.diagnostics.push(...converted.diagnostics);
}
newContent = converted.output + this.TSICKLE_SUPPORT;
} catch (e) {
console.error('Cannot convertDecorators on file', fileName);
throw e;
}
}
return ts.createSourceFile(fileName, newContent, languageVersion, true);
};
}
const IGNORED_FILES = /\.ngfactory\.js$|\.css\.js$|\.css\.shim\.js$/;
export class MetadataWriterHost extends DelegatingHost {
private metadataCollector = new MetadataCollector();
constructor(
delegate: ts.CompilerHost, private program: ts.Program, private ngOptions: NgOptions) {
super(delegate);
}
private writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) {
// TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is
// released
if (/*DTS*/ /\.js$/.test(emitFilePath)) {
const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json');
const metadata =
this.metadataCollector.getMetadata(sourceFile, !!this.ngOptions.strictMetadataEmit);
if (metadata && metadata.metadata) {
const metadataText = JSON.stringify(metadata);
writeFileSync(path, metadataText, {encoding: 'utf-8'});
}
}
}
2016-05-26 10:45:37 -07:00
writeFile: ts.WriteFileCallback =
(fileName: string, data: string, writeByteOrderMark: boolean,
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
if (/\.d\.ts$/.test(fileName)) {
// Let the original file be written first; this takes care of creating parent directories
this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
2016-05-26 10:45:37 -07:00
// TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412
// is
// released
return;
}
2016-05-26 10:45:37 -07:00
if (IGNORED_FILES.test(fileName)) {
return;
}
2016-05-26 10:45:37 -07:00
if (!sourceFiles) {
throw new Error(
'Metadata emit requires the sourceFiles are passed to WriteFileCallback. ' +
'Update to TypeScript ^1.9.0-dev');
}
if (sourceFiles.length > 1) {
throw new Error('Bundled emit with --out is not supported');
}
this.writeMetadata(fileName, sourceFiles[0]);
};
}