2017-11-10 01:01:33 +01:00
|
|
|
/// <reference path="./harness.ts" />
|
|
|
|
/// <reference path="./documents.ts" />
|
2017-11-10 03:35:24 +01:00
|
|
|
/// <reference path="./core.ts" />
|
2017-11-10 01:01:33 +01:00
|
|
|
/// <reference path="./vpath.ts" />
|
|
|
|
/// <reference path="./vfs.ts" />
|
2018-01-20 06:58:46 +01:00
|
|
|
/// <reference path="./vfsutils.ts" />
|
2017-11-10 01:01:33 +01:00
|
|
|
/// <reference path="./utils.ts" />
|
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
// NOTE: The contents of this file are all exported from the namespace 'compiler'. This is to
|
|
|
|
// support the eventual conversion of harness into a modular system.
|
2017-11-10 01:01:33 +01:00
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
namespace compiler {
|
2017-12-21 20:20:44 +01:00
|
|
|
/**
|
|
|
|
* A `ts.CompilerHost` that leverages a virtual file system.
|
|
|
|
*/
|
|
|
|
export class CompilerHost implements ts.CompilerHost {
|
2018-01-20 06:58:46 +01:00
|
|
|
public readonly vfs: vfs.FileSystem;
|
2017-11-10 01:01:33 +01:00
|
|
|
public readonly defaultLibLocation: string;
|
2017-11-10 03:35:24 +01:00
|
|
|
public readonly outputs: documents.TextDocument[] = [];
|
2017-11-10 01:01:33 +01:00
|
|
|
public readonly traces: string[] = [];
|
2017-11-10 01:08:40 +01:00
|
|
|
public readonly shouldAssertInvariants = !Harness.lightMode;
|
2017-11-10 01:01:33 +01:00
|
|
|
|
|
|
|
private _setParentNodes: boolean;
|
2017-11-10 03:35:24 +01:00
|
|
|
private _sourceFiles: core.KeyedCollection<string, ts.SourceFile>;
|
2017-11-10 01:01:33 +01:00
|
|
|
private _newLine: string;
|
|
|
|
private _parseConfigHost: ParseConfigHost;
|
|
|
|
|
2018-01-20 06:58:46 +01:00
|
|
|
constructor(vfs: vfs.FileSystem, options: ts.CompilerOptions, setParentNodes = false) {
|
2017-11-10 01:01:33 +01:00
|
|
|
this.vfs = vfs;
|
2018-01-20 06:58:46 +01:00
|
|
|
this.defaultLibLocation = vfs.meta.get("defaultLibLocation") || "";
|
|
|
|
this._sourceFiles = new core.KeyedCollection<string, ts.SourceFile>(this.vfs.stringComparer);
|
2017-11-10 01:01:33 +01:00
|
|
|
this._newLine = options.newLine === ts.NewLineKind.LineFeed ? "\n" : "\r\n";
|
|
|
|
this._setParentNodes = setParentNodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get parseConfigHost() {
|
|
|
|
return this._parseConfigHost || (this._parseConfigHost = new ParseConfigHost(this.vfs));
|
|
|
|
}
|
|
|
|
|
|
|
|
public getCurrentDirectory(): string {
|
2018-01-20 06:58:46 +01:00
|
|
|
return this.vfs.cwd();
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public useCaseSensitiveFileNames(): boolean {
|
2018-01-20 06:58:46 +01:00
|
|
|
return !this.vfs.ignoreCase;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getNewLine(): string {
|
|
|
|
return this._newLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
public getCanonicalFileName(fileName: string): string {
|
2018-01-20 06:58:46 +01:00
|
|
|
return this.vfs.ignoreCase ? fileName.toLowerCase() : fileName;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public fileExists(fileName: string): boolean {
|
2018-01-20 06:58:46 +01:00
|
|
|
return vfsutils.fileExists(this.vfs, fileName);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public directoryExists(directoryName: string): boolean {
|
2018-01-20 06:58:46 +01:00
|
|
|
return vfsutils.directoryExists(this.vfs, directoryName);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getDirectories(path: string): string[] {
|
2018-01-20 06:58:46 +01:00
|
|
|
return vfsutils.getDirectories(this.vfs, path);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public readFile(path: string): string | undefined {
|
2018-01-20 06:58:46 +01:00
|
|
|
if (path.endsWith("lib.d.ts")) debugger;
|
|
|
|
return vfsutils.readFile(this.vfs, path);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
|
2018-01-20 06:58:46 +01:00
|
|
|
if (writeByteOrderMark) content = core.addUTF8ByteOrderMark(content);
|
|
|
|
vfsutils.writeFile(this.vfs, fileName, content);
|
|
|
|
const document = new documents.TextDocument(fileName, content);
|
|
|
|
document.meta.set("fileName", fileName);
|
|
|
|
this.vfs.filemeta(fileName).set("document", document);
|
|
|
|
const index = this.outputs.findIndex(output => this.vfs.stringComparer(document.file, output.file) === 0);
|
|
|
|
if (index < 0) {
|
|
|
|
this.outputs.push(document);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.outputs[index] = document;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public trace(s: string): void {
|
|
|
|
this.traces.push(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
public realpath(path: string): string {
|
2018-01-20 06:58:46 +01:00
|
|
|
return this.vfs.realpathSync(path);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getDefaultLibLocation(): string {
|
2018-01-20 06:58:46 +01:00
|
|
|
return vpath.resolve(this.vfs.cwd(), this.defaultLibLocation);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getDefaultLibFileName(options: ts.CompilerOptions): string {
|
2017-12-19 22:40:32 +01:00
|
|
|
// return vpath.resolve(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options));
|
|
|
|
|
|
|
|
// TODO(rbuckton): This patches the baseline to replace lib.es5.d.ts with lib.d.ts.
|
|
|
|
// This is only to make the PR for this change easier to read. A follow-up PR will
|
|
|
|
// revert this change and accept the new baselines.
|
|
|
|
// See https://github.com/Microsoft/TypeScript/pull/20763#issuecomment-352553264
|
|
|
|
return vpath.resolve(this.getDefaultLibLocation(), getDefaultLibFileName(options));
|
|
|
|
function getDefaultLibFileName(options: ts.CompilerOptions) {
|
|
|
|
switch (options.target) {
|
|
|
|
case ts.ScriptTarget.ESNext:
|
|
|
|
case ts.ScriptTarget.ES2017:
|
|
|
|
return "lib.es2017.d.ts";
|
|
|
|
case ts.ScriptTarget.ES2016:
|
|
|
|
return "lib.es2016.d.ts";
|
|
|
|
case ts.ScriptTarget.ES2015:
|
|
|
|
return "lib.es2015.d.ts";
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "lib.d.ts";
|
|
|
|
}
|
|
|
|
}
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getSourceFile(fileName: string, languageVersion: number): ts.SourceFile | undefined {
|
2018-01-20 06:58:46 +01:00
|
|
|
const canonicalFileName = this.getCanonicalFileName(vpath.resolve(this.vfs.cwd(), fileName));
|
2017-11-10 01:01:33 +01:00
|
|
|
const existing = this._sourceFiles.get(canonicalFileName);
|
|
|
|
if (existing) return existing;
|
2018-01-20 06:58:46 +01:00
|
|
|
|
|
|
|
const content = this.readFile(canonicalFileName);
|
|
|
|
if (content === undefined) return undefined;
|
2017-12-21 20:20:44 +01:00
|
|
|
|
|
|
|
// A virtual file system may shadow another existing virtual file system. This
|
|
|
|
// allows us to reuse a common virtual file system structure across multiple
|
|
|
|
// tests. If a virtual file is a shadow, it is likely that the file will be
|
|
|
|
// reused across multiple tests. In that case, we cache the SourceFile we parse
|
|
|
|
// so that it can be reused across multiple tests to avoid the cost of
|
|
|
|
// repeatedly parsing the same file over and over (such as lib.d.ts).
|
2018-01-20 06:58:46 +01:00
|
|
|
const cacheKey = this.vfs.shadowRoot && `SourceFile[languageVersion=${languageVersion},setParentNodes=${this._setParentNodes}]`;
|
2017-12-21 20:20:44 +01:00
|
|
|
if (cacheKey) {
|
2018-01-20 06:58:46 +01:00
|
|
|
const meta = this.vfs.filemeta(canonicalFileName);
|
|
|
|
const sourceFileFromMetadata = meta.get(cacheKey) as ts.SourceFile | undefined;
|
2017-12-21 20:20:44 +01:00
|
|
|
if (sourceFileFromMetadata) {
|
|
|
|
this._sourceFiles.set(canonicalFileName, sourceFileFromMetadata);
|
|
|
|
return sourceFileFromMetadata;
|
|
|
|
}
|
|
|
|
}
|
2017-11-10 01:01:33 +01:00
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
const parsed = ts.createSourceFile(fileName, content, languageVersion, this._setParentNodes || this.shouldAssertInvariants);
|
|
|
|
if (this.shouldAssertInvariants) {
|
|
|
|
Utils.assertInvariants(parsed, /*parent*/ undefined);
|
|
|
|
}
|
2017-12-11 02:35:42 +01:00
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
this._sourceFiles.set(canonicalFileName, parsed);
|
2017-11-10 01:01:33 +01:00
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
if (cacheKey) {
|
|
|
|
// store the cached source file on the unshadowed file with the same version.
|
2018-01-20 06:58:46 +01:00
|
|
|
const stats = this.vfs.statSync(canonicalFileName);
|
|
|
|
|
|
|
|
let fs = this.vfs;
|
|
|
|
while (fs.shadowRoot) {
|
|
|
|
try {
|
|
|
|
const shadowRootStats = fs.shadowRoot.statSync(canonicalFileName);
|
|
|
|
if (shadowRootStats.dev !== stats.dev ||
|
|
|
|
shadowRootStats.ino !== stats.ino ||
|
|
|
|
shadowRootStats.mtimeMs !== stats.mtimeMs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs = fs.shadowRoot;
|
|
|
|
}
|
|
|
|
catch {
|
|
|
|
break;
|
|
|
|
}
|
2017-12-21 20:20:44 +01:00
|
|
|
}
|
2018-01-20 06:58:46 +01:00
|
|
|
|
|
|
|
if (fs !== this.vfs) {
|
|
|
|
fs.filemeta(canonicalFileName).set(cacheKey, parsed);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-21 20:20:44 +01:00
|
|
|
|
|
|
|
return parsed;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
/**
|
|
|
|
* A `ts.ParseConfigHost` that leverages a virtual file system.
|
|
|
|
*/
|
|
|
|
export class ParseConfigHost implements ts.ParseConfigHost {
|
2018-01-20 06:58:46 +01:00
|
|
|
public readonly vfs: vfs.FileSystem;
|
2017-11-10 01:01:33 +01:00
|
|
|
|
2018-01-20 06:58:46 +01:00
|
|
|
constructor(vfs: vfs.FileSystem) {
|
2017-11-10 01:01:33 +01:00
|
|
|
this.vfs = vfs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get useCaseSensitiveFileNames() {
|
2018-01-20 06:58:46 +01:00
|
|
|
return !this.vfs.ignoreCase;
|
|
|
|
}
|
|
|
|
|
|
|
|
public fileExists(fileName: string): boolean {
|
|
|
|
return vfsutils.fileExists(this.vfs, fileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
public directoryExists(directoryName: string): boolean {
|
|
|
|
return vfsutils.directoryExists(this.vfs, directoryName);
|
|
|
|
}
|
|
|
|
|
|
|
|
public readFile(path: string): string | undefined {
|
|
|
|
return vfsutils.readFile(this.vfs, path);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[] {
|
|
|
|
return ts.matchFiles(
|
|
|
|
path,
|
|
|
|
extensions,
|
|
|
|
excludes,
|
|
|
|
includes,
|
2018-01-20 06:58:46 +01:00
|
|
|
!this.vfs.ignoreCase,
|
|
|
|
this.vfs.cwd(),
|
2017-11-10 01:01:33 +01:00
|
|
|
depth,
|
2018-01-20 06:58:46 +01:00
|
|
|
path => vfsutils.getAccessibleFileSystemEntries(this.vfs, path));
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface Project {
|
|
|
|
file: string;
|
|
|
|
config?: ts.ParsedCommandLine;
|
|
|
|
errors?: ts.Diagnostic[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readProject(host: ParseConfigHost, project: string | undefined, existingOptions?: ts.CompilerOptions): Project | undefined {
|
|
|
|
if (project) {
|
|
|
|
project = host.vfs.stringComparer(vpath.basename(project), "tsconfig.json") === 0 ? project :
|
|
|
|
vpath.combine(project, "tsconfig.json");
|
|
|
|
}
|
|
|
|
else {
|
2018-01-23 19:27:20 +01:00
|
|
|
[project] = host.vfs.scanSync(".", "ancestors-or-self", {
|
2018-01-23 19:40:56 +01:00
|
|
|
accept: (path, stats) => stats.isFile() && host.vfs.stringComparer(vpath.basename(path), "tsconfig.json") === 0
|
2018-01-23 19:27:20 +01:00
|
|
|
});
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (project) {
|
|
|
|
// TODO(rbuckton): Do we need to resolve this? Resolving breaks projects tests.
|
|
|
|
// project = vpath.resolve(host.vfs.currentDirectory, project);
|
|
|
|
|
|
|
|
// read the config file
|
|
|
|
const readResult = ts.readConfigFile(project, path => host.readFile(path));
|
|
|
|
if (readResult.error) {
|
|
|
|
return { file: project, errors: [readResult.error] };
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse the config file
|
|
|
|
const config = ts.parseJsonConfigFileContent(readResult.config, host, vpath.dirname(project), existingOptions);
|
|
|
|
return { file: project, errors: config.errors, config };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
/**
|
|
|
|
* Correlates compilation inputs and outputs
|
|
|
|
*/
|
2017-11-10 01:01:33 +01:00
|
|
|
export interface CompilationOutput {
|
2017-12-21 20:20:44 +01:00
|
|
|
readonly inputs: ReadonlyArray<documents.TextDocument>;
|
2017-11-10 03:35:24 +01:00
|
|
|
readonly js: documents.TextDocument | undefined;
|
|
|
|
readonly dts: documents.TextDocument | undefined;
|
|
|
|
readonly map: documents.TextDocument | undefined;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class CompilationResult {
|
|
|
|
public readonly host: CompilerHost;
|
|
|
|
public readonly program: ts.Program | undefined;
|
|
|
|
public readonly result: ts.EmitResult | undefined;
|
|
|
|
public readonly options: ts.CompilerOptions;
|
2017-11-10 22:52:12 +01:00
|
|
|
public readonly diagnostics: ReadonlyArray<ts.Diagnostic>;
|
|
|
|
public readonly js: core.ReadonlyKeyedCollection<string, documents.TextDocument>;
|
|
|
|
public readonly dts: core.ReadonlyKeyedCollection<string, documents.TextDocument>;
|
|
|
|
public readonly maps: core.ReadonlyKeyedCollection<string, documents.TextDocument>;
|
2017-11-10 01:01:33 +01:00
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
private _inputs: documents.TextDocument[] = [];
|
|
|
|
private _inputsAndOutputs: core.KeyedCollection<string, CompilationOutput>;
|
2017-11-10 01:01:33 +01:00
|
|
|
|
|
|
|
constructor(host: CompilerHost, options: ts.CompilerOptions, program: ts.Program | undefined, result: ts.EmitResult | undefined, diagnostics: ts.Diagnostic[]) {
|
|
|
|
this.host = host;
|
|
|
|
this.program = program;
|
|
|
|
this.result = result;
|
|
|
|
this.diagnostics = diagnostics;
|
|
|
|
this.options = program ? program.getCompilerOptions() : options;
|
|
|
|
|
|
|
|
// collect outputs
|
2018-01-20 06:58:46 +01:00
|
|
|
const js = this.js = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.stringComparer);
|
|
|
|
const dts = this.dts = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.stringComparer);
|
|
|
|
const maps = this.maps = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.stringComparer);
|
2017-11-10 01:01:33 +01:00
|
|
|
for (const document of this.host.outputs) {
|
2018-01-20 06:58:46 +01:00
|
|
|
if (vfsutils.isJavaScript(document.file)) {
|
2017-11-10 22:52:12 +01:00
|
|
|
js.set(document.file, document);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
2018-01-20 06:58:46 +01:00
|
|
|
else if (vfsutils.isDeclaration(document.file)) {
|
2017-11-10 22:52:12 +01:00
|
|
|
dts.set(document.file, document);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
2018-01-20 06:58:46 +01:00
|
|
|
else if (vfsutils.isSourceMap(document.file)) {
|
2017-11-10 22:52:12 +01:00
|
|
|
maps.set(document.file, document);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// correlate inputs and outputs
|
2018-01-20 06:58:46 +01:00
|
|
|
this._inputsAndOutputs = new core.KeyedCollection<string, CompilationOutput>(this.vfs.stringComparer);
|
2017-11-10 01:01:33 +01:00
|
|
|
if (program) {
|
2017-12-21 20:20:44 +01:00
|
|
|
if (this.options.out || this.options.outFile) {
|
2018-01-20 06:58:46 +01:00
|
|
|
const outFile = vpath.resolve(this.vfs.cwd(), this.options.outFile || this.options.out);
|
2017-12-21 20:20:44 +01:00
|
|
|
const inputs: documents.TextDocument[] = [];
|
|
|
|
for (const sourceFile of program.getSourceFiles()) {
|
|
|
|
if (sourceFile) {
|
|
|
|
const input = new documents.TextDocument(sourceFile.fileName, sourceFile.text);
|
|
|
|
this._inputs.push(input);
|
2018-01-20 06:58:46 +01:00
|
|
|
if (!vfsutils.isDeclaration(sourceFile.fileName)) {
|
2017-12-21 20:20:44 +01:00
|
|
|
inputs.push(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const outputs: CompilationOutput = {
|
|
|
|
inputs,
|
|
|
|
js: js.get(outFile),
|
|
|
|
dts: dts.get(vpath.changeExtension(outFile, ".d.ts")),
|
|
|
|
map: maps.get(outFile + ".map")
|
|
|
|
};
|
|
|
|
|
|
|
|
if (outputs.js) this._inputsAndOutputs.set(outputs.js.file, outputs);
|
|
|
|
if (outputs.dts) this._inputsAndOutputs.set(outputs.dts.file, outputs);
|
|
|
|
if (outputs.map) this._inputsAndOutputs.set(outputs.map.file, outputs);
|
|
|
|
|
|
|
|
for (const input of inputs) {
|
|
|
|
this._inputsAndOutputs.set(input.file, outputs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (const sourceFile of program.getSourceFiles()) {
|
|
|
|
if (sourceFile) {
|
|
|
|
const input = new documents.TextDocument(sourceFile.fileName, sourceFile.text);
|
|
|
|
this._inputs.push(input);
|
2018-01-20 06:58:46 +01:00
|
|
|
if (!vfsutils.isDeclaration(sourceFile.fileName)) {
|
2017-12-21 20:20:44 +01:00
|
|
|
const extname = ts.getOutputExtension(sourceFile, this.options);
|
|
|
|
const outputs: CompilationOutput = {
|
|
|
|
inputs: [input],
|
|
|
|
js: js.get(this.getOutputPath(sourceFile.fileName, extname)),
|
|
|
|
dts: dts.get(this.getOutputPath(sourceFile.fileName, ".d.ts")),
|
|
|
|
map: maps.get(this.getOutputPath(sourceFile.fileName, extname + ".map"))
|
|
|
|
};
|
|
|
|
|
|
|
|
this._inputsAndOutputs.set(sourceFile.fileName, outputs);
|
|
|
|
if (outputs.js) this._inputsAndOutputs.set(outputs.js.file, outputs);
|
|
|
|
if (outputs.dts) this._inputsAndOutputs.set(outputs.dts.file, outputs);
|
|
|
|
if (outputs.map) this._inputsAndOutputs.set(outputs.map.file, outputs);
|
|
|
|
}
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-10 22:52:12 +01:00
|
|
|
|
2017-11-10 23:55:04 +01:00
|
|
|
this.diagnostics = diagnostics;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
2018-01-20 06:58:46 +01:00
|
|
|
public get vfs(): vfs.FileSystem {
|
2017-11-10 01:01:33 +01:00
|
|
|
return this.host.vfs;
|
|
|
|
}
|
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
public get inputs(): ReadonlyArray<documents.TextDocument> {
|
2017-11-10 01:01:33 +01:00
|
|
|
return this._inputs;
|
|
|
|
}
|
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
public get outputs(): ReadonlyArray<documents.TextDocument> {
|
2017-11-10 01:01:33 +01:00
|
|
|
return this.host.outputs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get traces(): ReadonlyArray<string> {
|
|
|
|
return this.host.traces;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get emitSkipped(): boolean {
|
|
|
|
return this.result && this.result.emitSkipped || false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get singleFile(): boolean {
|
|
|
|
return !!this.options.outFile || !!this.options.out;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get commonSourceDirectory(): string {
|
|
|
|
const common = this.program && this.program.getCommonSourceDirectory() || "";
|
2018-01-20 06:58:46 +01:00
|
|
|
return common && vpath.combine(this.vfs.cwd(), common);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public getInputsAndOutputs(path: string): CompilationOutput | undefined {
|
2018-01-20 06:58:46 +01:00
|
|
|
return this._inputsAndOutputs.get(vpath.resolve(this.vfs.cwd(), path));
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
public getInputs(path: string): ReadonlyArray<documents.TextDocument> | undefined {
|
2017-11-10 01:01:33 +01:00
|
|
|
const outputs = this.getInputsAndOutputs(path);
|
2017-12-21 20:20:44 +01:00
|
|
|
return outputs && outputs.inputs;
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
public getOutput(path: string, kind: "js" | "dts" | "map"): documents.TextDocument | undefined {
|
2017-11-10 01:01:33 +01:00
|
|
|
const outputs = this.getInputsAndOutputs(path);
|
|
|
|
return outputs && outputs[kind];
|
|
|
|
}
|
|
|
|
|
2017-11-10 22:52:12 +01:00
|
|
|
public getSourceMapRecord(): string | undefined {
|
|
|
|
if (this.result.sourceMaps && this.result.sourceMaps.length > 0) {
|
2017-12-21 20:20:44 +01:00
|
|
|
return Harness.SourceMapRecorder.getSourceMapRecord(this.result.sourceMaps, this.program, this.js.values());
|
2017-11-10 22:52:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 03:35:24 +01:00
|
|
|
public getSourceMap(path: string): documents.SourceMap | undefined {
|
2018-01-20 06:58:46 +01:00
|
|
|
if (this.options.noEmit || vfsutils.isDeclaration(path)) return undefined;
|
2017-11-10 01:01:33 +01:00
|
|
|
if (this.options.inlineSourceMap) {
|
|
|
|
const document = this.getOutput(path, "js");
|
2017-11-10 03:35:24 +01:00
|
|
|
return document && documents.SourceMap.fromSource(document.text);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
if (this.options.sourceMap) {
|
|
|
|
const document = this.getOutput(path, "map");
|
2017-11-10 03:35:24 +01:00
|
|
|
return document && new documents.SourceMap(document.file, document.text);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 20:20:44 +01:00
|
|
|
public getOutputPath(path: string, ext: string): string {
|
|
|
|
if (this.options.outFile || this.options.out) {
|
2018-01-20 06:58:46 +01:00
|
|
|
path = vpath.resolve(this.vfs.cwd(), this.options.outFile || this.options.out);
|
2017-12-21 20:20:44 +01:00
|
|
|
}
|
|
|
|
else {
|
2018-01-20 06:58:46 +01:00
|
|
|
path = vpath.resolve(this.vfs.cwd(), path);
|
2017-12-21 20:20:44 +01:00
|
|
|
const outDir = ext === ".d.ts" ? this.options.declarationDir || this.options.outDir : this.options.outDir;
|
|
|
|
if (outDir) {
|
|
|
|
const common = this.commonSourceDirectory;
|
|
|
|
if (common) {
|
2018-01-20 06:58:46 +01:00
|
|
|
path = vpath.relative(common, path, this.vfs.ignoreCase);
|
|
|
|
path = vpath.combine(vpath.resolve(this.vfs.cwd(), this.options.outDir), path);
|
2017-12-21 20:20:44 +01:00
|
|
|
}
|
|
|
|
}
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
2017-12-21 20:20:44 +01:00
|
|
|
return vpath.changeExtension(path, ext);
|
2017-11-10 01:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 22:52:12 +01:00
|
|
|
export function compileFiles(host: CompilerHost, rootFiles: string[] | undefined, compilerOptions: ts.CompilerOptions): CompilationResult {
|
2017-11-10 01:01:33 +01:00
|
|
|
if (compilerOptions.project || !rootFiles || rootFiles.length === 0) {
|
|
|
|
const project = readProject(host.parseConfigHost, compilerOptions.project, compilerOptions);
|
|
|
|
if (project) {
|
|
|
|
if (project.errors && project.errors.length > 0) {
|
|
|
|
return new CompilationResult(host, compilerOptions, /*program*/ undefined, /*result*/ undefined, project.errors);
|
|
|
|
}
|
|
|
|
if (project.config) {
|
|
|
|
rootFiles = project.config.fileNames;
|
|
|
|
compilerOptions = project.config.options;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete compilerOptions.project;
|
|
|
|
}
|
|
|
|
|
2017-11-10 22:52:12 +01:00
|
|
|
// establish defaults (aligns with old harness)
|
2017-11-10 01:01:33 +01:00
|
|
|
if (compilerOptions.target === undefined) compilerOptions.target = ts.ScriptTarget.ES3;
|
|
|
|
if (compilerOptions.newLine === undefined) compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
|
|
|
|
if (compilerOptions.skipDefaultLibCheck === undefined) compilerOptions.skipDefaultLibCheck = true;
|
|
|
|
if (compilerOptions.noErrorTruncation === undefined) compilerOptions.noErrorTruncation = true;
|
|
|
|
|
|
|
|
const program = ts.createProgram(rootFiles || [], compilerOptions, host);
|
|
|
|
const emitResult = program.emit();
|
|
|
|
const errors = ts.getPreEmitDiagnostics(program);
|
|
|
|
return new CompilationResult(host, compilerOptions, program, emitResult, errors);
|
|
|
|
}
|
|
|
|
}
|