Deprecate CompilerResult

This commit is contained in:
Ron Buckton 2017-11-10 13:52:12 -08:00
parent 4697ba3e50
commit cd0809e4d6
12 changed files with 225 additions and 180 deletions

View File

@ -227,14 +227,22 @@ namespace compiler {
public readonly program: ts.Program | undefined;
public readonly result: ts.EmitResult | undefined;
public readonly options: ts.CompilerOptions;
public readonly diagnostics: ts.Diagnostic[];
public readonly js: core.KeyedCollection<string, documents.TextDocument>;
public readonly dts: core.KeyedCollection<string, documents.TextDocument>;
public readonly maps: core.KeyedCollection<string, documents.TextDocument>;
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>;
private _inputs: documents.TextDocument[] = [];
private _inputsAndOutputs: core.KeyedCollection<string, CompilationOutput>;
// from CompilerResult
public readonly files: ReadonlyArray<Harness.Compiler.GeneratedFile>;
public readonly declFilesCode: ReadonlyArray<Harness.Compiler.GeneratedFile>;
public readonly sourceMaps: ReadonlyArray<Harness.Compiler.GeneratedFile>;
public readonly errors: ReadonlyArray<ts.Diagnostic>;
public readonly currentDirectoryForProgram: string;
public readonly traceResults: ReadonlyArray<string>;
constructor(host: CompilerHost, options: ts.CompilerOptions, program: ts.Program | undefined, result: ts.EmitResult | undefined, diagnostics: ts.Diagnostic[]) {
this.host = host;
this.program = program;
@ -243,18 +251,18 @@ namespace compiler {
this.options = program ? program.getCompilerOptions() : options;
// collect outputs
this.js = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
this.dts = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
this.maps = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
const js = this.js = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
const dts = this.dts = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
const maps = this.maps = new core.KeyedCollection<string, documents.TextDocument>(this.vfs.pathComparer);
for (const document of this.host.outputs) {
if (vpath.isJavaScript(document.file)) {
this.js.set(document.file, document);
js.set(document.file, document);
}
else if (vpath.isDeclaration(document.file)) {
this.dts.set(document.file, document);
dts.set(document.file, document);
}
else if (vpath.isSourceMap(document.file)) {
this.maps.set(document.file, document);
maps.set(document.file, document);
}
}
@ -268,9 +276,9 @@ namespace compiler {
if (!vpath.isDeclaration(sourceFile.fileName)) {
const outputs = {
input,
js: this.js.get(this.getOutputPath(sourceFile.fileName, ts.getOutputExtension(sourceFile, this.options))),
dts: this.dts.get(this.getOutputPath(sourceFile.fileName, ".d.ts", this.options.declarationDir)),
map: this.maps.get(this.getOutputPath(sourceFile.fileName, ts.getOutputExtension(sourceFile, this.options) + ".map"))
js: js.get(this.getOutputPath(sourceFile.fileName, ts.getOutputExtension(sourceFile, this.options))),
dts: dts.get(this.getOutputPath(sourceFile.fileName, ".d.ts", this.options.declarationDir)),
map: maps.get(this.getOutputPath(sourceFile.fileName, ts.getOutputExtension(sourceFile, this.options) + ".map"))
};
this._inputsAndOutputs.set(sourceFile.fileName, outputs);
@ -281,9 +289,17 @@ namespace compiler {
}
}
}
// from CompilerResult
this.files = Array.from(this.js.values(), file => file.asGeneratedFile());
this.declFilesCode = Array.from(this.dts.values(), file => file.asGeneratedFile());
this.sourceMaps = Array.from(this.maps.values(), file => file.asGeneratedFile());
this.errors = diagnostics;
this.currentDirectoryForProgram = host.vfs.currentDirectory;
this.traceResults = host.traces;
}
public get vfs() {
public get vfs(): vfs.VirtualFileSystem {
return this.host.vfs;
}
@ -326,6 +342,12 @@ namespace compiler {
return outputs && outputs[kind];
}
public getSourceMapRecord(): string | undefined {
if (this.result.sourceMaps && this.result.sourceMaps.length > 0) {
return Harness.SourceMapRecorder.getSourceMapRecord(this.result.sourceMaps, this.program, this.files);
}
}
public getSourceMap(path: string): documents.SourceMap | undefined {
if (this.options.noEmit || vpath.isDeclaration(path)) return undefined;
if (this.options.inlineSourceMap) {
@ -338,7 +360,7 @@ namespace compiler {
}
}
public getOutputPath(path: string, ext: string, outDir: string | undefined = this.options.outDir) {
public getOutputPath(path: string, ext: string, outDir: string | undefined = this.options.outDir): string {
if (outDir) {
path = vpath.resolve(this.vfs.currentDirectory, path);
const common = this.commonSourceDirectory;
@ -352,8 +374,7 @@ namespace compiler {
}
}
export function compileFiles(host: CompilerHost, rootFiles: string[] | undefined, compilerOptions: ts.CompilerOptions) {
// establish defaults (aligns with old harness)
export function compileFiles(host: CompilerHost, rootFiles: string[] | undefined, compilerOptions: ts.CompilerOptions): CompilationResult {
if (compilerOptions.project || !rootFiles || rootFiles.length === 0) {
const project = readProject(host.parseConfigHost, compilerOptions.project, compilerOptions);
if (project) {
@ -368,6 +389,7 @@ namespace compiler {
delete compilerOptions.project;
}
// establish defaults (aligns with old harness)
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;

View File

@ -108,7 +108,7 @@ class CompilerTest {
private lastUnit: Harness.TestCaseParser.TestUnitData;
private harnessSettings: Harness.TestCaseParser.CompilerSettings;
private hasNonDtsFiles: boolean;
private result: Harness.Compiler.CompilerResult;
private result: compiler.CompilationResult;
private options: ts.CompilerOptions;
private tsConfigFiles: Harness.Compiler.TestFile[];
// equivalent to the files that will be passed on the command line

View File

@ -1,5 +1,3 @@
/// <reference path="./harness.ts" />
// NOTE: The contents of this file are all exported from the namespace 'core'. This is to
// support the eventual conversion of harness into a modular system.
@ -63,10 +61,20 @@ namespace core {
// Collections
//
export interface ReadonlyKeyedCollection<K, V> {
readonly size: number;
has(key: K): boolean;
get(key: K): V | undefined;
forEach(callback: (value: V, key: K, collection: this) => void): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
}
/**
* A collection of key/value pairs internally sorted by key.
*/
export class KeyedCollection<K, V> {
export class KeyedCollection<K, V> implements ReadonlyKeyedCollection<K, V> {
private _comparer: (a: K, b: K) => number;
private _keys: K[] = [];
private _values: V[] = [];
@ -101,7 +109,7 @@ namespace core {
insertAt(this._keys, ~index, key);
insertAt(this._values, ~index, value);
insertAt(this._order, ~index, this._version);
this._version++;
this.writePostScript();
}
return this;
}
@ -113,7 +121,7 @@ namespace core {
removeAt(this._keys, index);
removeAt(this._values, index);
removeAt(this._order, index);
this._version++;
this.writePostScript();
return true;
}
return false;
@ -125,7 +133,7 @@ namespace core {
this._keys.length = 0;
this._values.length = 0;
this._order.length = 0;
this._version = 0;
this.writePostScript();
}
}
@ -143,6 +151,46 @@ namespace core {
}
}
public * keys() {
const keys = this._keys;
const order = this.getInsertionOrder();
const version = this._version;
this._copyOnWrite = true;
for (const index of order) {
yield keys[index];
}
if (version === this._version) {
this._copyOnWrite = false;
}
}
public * values() {
const values = this._values;
const order = this.getInsertionOrder();
const version = this._version;
this._copyOnWrite = true;
for (const index of order) {
yield values[index];
}
if (version === this._version) {
this._copyOnWrite = false;
}
}
public * entries() {
const keys = this._keys;
const values = this._values;
const order = this.getInsertionOrder();
const version = this._version;
this._copyOnWrite = true;
for (const index of order) {
yield [keys[index], values[index]] as [K, V];
}
if (version === this._version) {
this._copyOnWrite = false;
}
}
private writePreamble() {
if (this._copyOnWrite) {
this._keys = this._keys.slice();
@ -152,10 +200,14 @@ namespace core {
}
}
private writePostScript() {
this._version++;
}
private getInsertionOrder() {
return this._order
.map((_, i) => i)
.sort((x, y) => compareNumbers(this._order[x], this._order[y]));
.sort((x, y) => this._order[x] - this._order[y]);
}
}
@ -325,6 +377,10 @@ namespace core {
return length ? text.slice(length) : text;
}
export function addUTF8ByteOrderMark(text: string) {
return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text;
}
function splitLinesWorker(text: string, lineStarts: number[] | undefined, lines: string[] | undefined, removeEmptyElements: boolean) {
let pos = 0;
let end = 0;

View File

@ -10,6 +10,8 @@ namespace documents {
public readonly text: string;
private _lineStarts: core.LineStarts | undefined;
private _testFile: Harness.Compiler.TestFile | undefined;
private _generatedFile: Harness.Compiler.GeneratedFile | undefined;
constructor(file: string, text: string, meta?: Map<string, string>) {
this.file = file;
@ -20,6 +22,37 @@ namespace documents {
public get lineStarts(): core.LineStarts {
return this._lineStarts || (this._lineStarts = core.computeLineStarts(this.text));
}
public static fromTestFile(file: Harness.Compiler.TestFile) {
return new TextDocument(
file.unitName,
file.content,
file.fileOptions && Object.keys(file.fileOptions)
.reduce((meta, key) => meta.set(key, file.fileOptions[key]), new Map<string, string>()));
}
public asTestFile() {
return this._testFile || (this._testFile = {
unitName: this.file,
content: this.text,
fileOptions: Array.from(this.meta)
.reduce((obj, [key, value]) => (obj[key] = value, obj), {} as Record<string, string>)
});
}
public static fromGeneratedFile(file: Harness.Compiler.GeneratedFile) {
return new TextDocument(
file.fileName,
file.writeByteOrderMark ? core.addUTF8ByteOrderMark(file.code) : file.code);
}
public asGeneratedFile() {
return this._generatedFile || (this._generatedFile = {
fileName: this.file,
code: core.removeByteOrderMark(this.text),
writeByteOrderMark: core.getByteOrderMarkLength(this.text) > 0
});
}
}
export interface RawSourceMap {

View File

@ -482,7 +482,7 @@ namespace Utils {
}
namespace Harness {
// tslint:disable-next-line:interface-name
// tslint:disable-next-line:interface-name
export interface IO {
newLine(): string;
getCurrentDirectory(): string;
@ -1163,7 +1163,7 @@ namespace Harness {
}
export interface CompilationOutput {
result: CompilerResult;
result: compiler.CompilationResult;
options: ts.CompilerOptions & HarnessOptions;
}
@ -1208,29 +1208,24 @@ namespace Harness {
}
}
const compilation = compiler.compileFiles(
const result = compiler.compileFiles(
new compiler.CompilerHost(
vfs.VirtualFileSystem.createFromTestFiles(
{ useCaseSensitiveFileNames, currentDirectory },
inputFiles.concat(otherFiles),
{ overwrite: true }
vfs.VirtualFileSystem.createFromDocuments(
useCaseSensitiveFileNames,
inputFiles.concat(otherFiles).map(documents.TextDocument.fromTestFile),
{ currentDirectory, overwrite: true }
),
options
),
programFileNames,
options);
const fileOutputs = compilation.outputs ? compilation.outputs.map(output => (<GeneratedFile>{
fileName: output.file,
code: core.removeByteOrderMark(output.text),
writeByteOrderMark: core.getByteOrderMarkLength(output.text) > 0
})) : [];
const traceResults = compilation.traces && compilation.traces.slice();
const program = compilation.program;
const emitResult = compilation.result;
const errors = compilation.diagnostics;
const result = new CompilerResult(fileOutputs, errors, program, compilation.vfs.currentDirectory, emitResult.sourceMaps, traceResults);
// const fileOutputs = compilation.outputs.map(output => output.asGeneratedFile());
// const traceResults = compilation.traces && compilation.traces.slice();
// const program = compilation.program;
// const emitResult = compilation.result;
// const errors = compilation.diagnostics;
// const result = new CompilerResult(fileOutputs, errors, program, compilation.vfs.currentDirectory, emitResult.sourceMaps, traceResults);
return { result, options };
}
@ -1242,9 +1237,9 @@ namespace Harness {
currentDirectory: string;
}
export function prepareDeclarationCompilationContext(inputFiles: TestFile[],
otherFiles: TestFile[],
result: CompilerResult,
export function prepareDeclarationCompilationContext(inputFiles: ReadonlyArray<TestFile>,
otherFiles: ReadonlyArray<TestFile>,
result: compiler.CompilationResult,
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions,
options: ts.CompilerOptions,
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
@ -1264,10 +1259,10 @@ namespace Harness {
}
function addDtsFile(file: TestFile, dtsFiles: TestFile[]) {
if (isDTS(file.unitName)) {
if (vpath.isDeclaration(file.unitName)) {
dtsFiles.push(file);
}
else if (isTS(file.unitName)) {
else if (vpath.isTypeScript(file.unitName)) {
const declFile = findResultCodeFile(file.unitName);
if (declFile && !findUnit(declFile.fileName, declInputFiles) && !findUnit(declFile.fileName, declOtherFiles)) {
dtsFiles.push({ unitName: declFile.fileName, content: declFile.code });
@ -1465,7 +1460,7 @@ namespace Harness {
assert.equal(totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length, "total number of errors");
}
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[], pretty?: boolean) {
export function doErrorBaseline(baselinePath: string, inputFiles: ReadonlyArray<TestFile>, errors: ReadonlyArray<ts.Diagnostic>, pretty?: boolean) {
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string => {
if (!errors || (errors.length === 0)) {
/* tslint:disable:no-null-keyword */
@ -1611,7 +1606,7 @@ namespace Harness {
return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : "";
}
export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: CompilerResult, harnessSettings: Harness.TestCaseParser.CompilerSettings) {
export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: compiler.CompilationResult, harnessSettings: Harness.TestCaseParser.CompilerSettings) {
if (options.inlineSourceMap) {
if (result.sourceMaps.length > 0) {
throw new Error("No sourcemap files should be generated if inlineSourceMaps was set.");
@ -1642,7 +1637,7 @@ namespace Harness {
}
}
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, tsConfigFiles: Harness.Compiler.TestFile[], toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) {
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: compiler.CompilationResult, tsConfigFiles: ReadonlyArray<Harness.Compiler.TestFile>, toBeCompiled: ReadonlyArray<Harness.Compiler.TestFile>, otherFiles: ReadonlyArray<Harness.Compiler.TestFile>, harnessSettings: Harness.TestCaseParser.CompilerSettings) {
if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) {
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
}
@ -1698,7 +1693,7 @@ namespace Harness {
return "//// [" + fileName + "]\r\n" + getByteOrderMarkText(file) + utils.removeTestPathPrefixes(file.code);
}
export function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): string {
export function collateOutputs(outputFiles: ReadonlyArray<Harness.Compiler.GeneratedFile>): string {
const gen = iterateOutputs(outputFiles);
// Emit them
let result = "";
@ -1714,9 +1709,9 @@ namespace Harness {
return result;
}
export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> {
export function *iterateOutputs(outputFiles: ReadonlyArray<Harness.Compiler.GeneratedFile>): IterableIterator<[string, string]> {
// Collect, test, and sort the fileNames
outputFiles.sort((a, b) => ts.compareStringsCaseSensitive(cleanName(a.fileName), cleanName(b.fileName)));
outputFiles.slice().sort((a, b) => ts.compareStringsCaseSensitive(cleanName(a.fileName), cleanName(b.fileName)));
const dupeCase = ts.createMap<number>();
// Yield them
for (const outputFile of outputFiles) {
@ -1751,78 +1746,11 @@ namespace Harness {
return path;
}
// This does not need to exist strictly speaking, but many tests will need to be updated if it's removed
export function compileString(_code: string, _unitName: string, _callback: (result: CompilerResult) => void) {
// NEWTODO: Re-implement 'compileString'
return ts.notImplemented();
}
export interface GeneratedFile {
fileName: string;
code: string;
writeByteOrderMark: boolean;
}
export function isTS(fileName: string) {
return ts.endsWith(fileName, ts.Extension.Ts);
}
export function isTSX(fileName: string) {
return ts.endsWith(fileName, ts.Extension.Tsx);
}
export function isDTS(fileName: string) {
return ts.endsWith(fileName, ts.Extension.Dts);
}
export function isJS(fileName: string) {
return ts.endsWith(fileName, ts.Extension.Js);
}
export function isJSX(fileName: string) {
return ts.endsWith(fileName, ts.Extension.Jsx);
}
export function isJSMap(fileName: string) {
return ts.endsWith(fileName, ".js.map") || ts.endsWith(fileName, ".jsx.map");
}
/** Contains the code and errors of a compilation and some helper methods to check its status. */
export class CompilerResult {
public files: GeneratedFile[] = [];
public errors: ts.Diagnostic[] = [];
public declFilesCode: GeneratedFile[] = [];
public sourceMaps: GeneratedFile[] = [];
/** @param fileResults an array of strings for the fileName and an ITextWriter with its code */
constructor(fileResults: GeneratedFile[], errors: ts.Diagnostic[], public program: ts.Program,
public currentDirectoryForProgram: string, private sourceMapData: ts.SourceMapData[], public traceResults: string[]) {
for (const emittedFile of fileResults) {
if (isDTS(emittedFile.fileName)) {
// .d.ts file, add to declFiles emit
this.declFilesCode.push(emittedFile);
}
else if (isJS(emittedFile.fileName) || isJSX(emittedFile.fileName)) {
// .js file, add to files
this.files.push(emittedFile);
}
else if (isJSMap(emittedFile.fileName)) {
this.sourceMaps.push(emittedFile);
}
else {
throw new Error("Unrecognized file extension for file " + emittedFile.fileName);
}
}
this.errors = errors;
}
public getSourceMapRecord() {
if (this.sourceMapData && this.sourceMapData.length > 0) {
return Harness.SourceMapRecorder.getSourceMapRecord(this.sourceMapData, this.program, this.files);
}
}
}
}
export namespace TestCaseParser {

View File

@ -1,5 +1,6 @@
///<reference path="harness.ts" />
///<reference path="runnerbase.ts" />
/// <reference path="harness.ts" />
/// <reference path="runnerbase.ts" />
/// <reference path="./vpath.ts" />
// Test case is json of below type in tests/cases/project/
interface ProjectRunnerTestCase {
@ -319,19 +320,19 @@ class ProjectRunner extends RunnerBase {
// we need to instead create files that can live in the project reference folder
// but make sure extension of these files matches with the fileName the compiler asked to write
diskRelativeName = "diskFile" + nonSubfolderDiskFiles +
(Harness.Compiler.isDTS(fileName) ? ts.Extension.Dts :
Harness.Compiler.isJS(fileName) ? ts.Extension.Js : ".js.map");
(vpath.isDeclaration(fileName) ? ts.Extension.Dts :
vpath.isJavaScript(fileName) ? ts.Extension.Js : ".js.map");
nonSubfolderDiskFiles++;
}
if (Harness.Compiler.isJS(fileName)) {
if (vpath.isJavaScript(fileName)) {
// Make sure if there is URl we have it cleaned up
const indexOfSourceMapUrl = data.lastIndexOf(`//# ${"sourceMappingURL"}=`); // This line can be seen as a sourceMappingURL comment
if (indexOfSourceMapUrl !== -1) {
data = data.substring(0, indexOfSourceMapUrl + 21) + cleanProjectUrl(data.substring(indexOfSourceMapUrl + 21));
}
}
else if (Harness.Compiler.isJSMap(fileName)) {
else if (vpath.isJavaScriptSourceMap(fileName)) {
// Make sure sources list is cleaned
const sourceMapData = JSON.parse(data);
for (let i = 0; i < sourceMapData.sources.length; i++) {

View File

@ -30,7 +30,7 @@ namespace RWC {
let inputFiles: Harness.Compiler.TestFile[] = [];
let otherFiles: Harness.Compiler.TestFile[] = [];
let tsconfigFiles: Harness.Compiler.TestFile[] = [];
let compilerResult: Harness.Compiler.CompilerResult;
let compilerResult: compiler.CompilationResult;
let compilerOptions: ts.CompilerOptions;
const baselineOpts: Harness.Baseline.BaselineOptions = {
Subfolder: "rwc",

View File

@ -434,7 +434,7 @@ namespace Harness.SourceMapRecorder {
}
}
export function getSourceMapRecord(sourceMapDataList: ts.SourceMapData[], program: ts.Program, jsFiles: Compiler.GeneratedFile[]) {
export function getSourceMapRecord(sourceMapDataList: ReadonlyArray<ts.SourceMapData>, program: ts.Program, jsFiles: ReadonlyArray<Compiler.GeneratedFile>) {
const sourceMapRecorder = new Compiler.WriterAggregator();
for (let i = 0; i < sourceMapDataList.length; i++) {

View File

@ -31,7 +31,7 @@ class Test262BaselineRunner extends RunnerBase {
// Everything declared here should be cleared out in the "after" callback.
let testState: {
filename: string;
compilerResult: Harness.Compiler.CompilerResult;
compilerResult: compiler.CompilationResult;
inputFiles: Harness.Compiler.TestFile[];
};

View File

@ -1,4 +1,5 @@
/// <reference path="..\harness.ts" />
/// <reference path="../documents.ts" />
/// <reference path="../vfs.ts" />
namespace ts {
@ -23,27 +24,23 @@ namespace ts {
const emptyFileName = "empty.ts";
const emptyFileRelativePath = "./" + emptyFileName;
const emptyFile: Harness.Compiler.TestFile = {
unitName: emptyFileName,
content: ""
};
const emptyFile = new documents.TextDocument(emptyFileName, "");
const referenceFileName = "reference.ts";
const referenceFileRelativePath = "./" + referenceFileName;
const referenceFile: Harness.Compiler.TestFile = {
unitName: referenceFileName,
content:
"/// <reference path=\"d:/imaginary/nonexistent1.ts\"/>\n" + // Absolute
"/// <reference path=\"./nonexistent2.ts\"/>\n" + // Relative
"/// <reference path=\"nonexistent3.ts\"/>\n" + // Unqualified
"/// <reference path=\"nonexistent4\"/>\n" // No extension
};
const referenceFile = new documents.TextDocument(referenceFileName,
"/// <reference path=\"d:/imaginary/nonexistent1.ts\"/>\n" + // Absolute
"/// <reference path=\"./nonexistent2.ts\"/>\n" + // Relative
"/// <reference path=\"nonexistent3.ts\"/>\n" + // Unqualified
"/// <reference path=\"nonexistent4\"/>\n" // No extension
);
const testCompilerHost = new compiler.CompilerHost(
vfs.VirtualFileSystem.createFromTestFiles(
{ useCaseSensitiveFileNames: false, currentDirectory: "d:\\pretend\\" },
[emptyFile, referenceFile]),
vfs.VirtualFileSystem.createFromDocuments(
/*useCaseSensitiveFileNames*/ false,
[emptyFile, referenceFile],
{ currentDirectory: "d:\\pretend\\" }),
{ newLine: NewLineKind.LineFeed });
it("handles no missing root files", () => {

View File

@ -1,8 +1,9 @@
/// <reference path="../compiler/commandLineParser.ts"/>
/// <reference path="./harness.ts" />
/// <reference path="./core.ts" />
/// <reference path="./vpath.ts" />
/// <reference path="./events.ts" />
/// <reference path="./vpath.ts" />
/// <reference path="./documents.ts" />
// NOTE: The contents of this file are all exported from the namespace 'vfs'. This is to
// support the eventual conversion of harness into a modular system.
@ -190,7 +191,7 @@ namespace vfs {
}
/**
* Gets a virtual file system with the following directories:
* Gets a read-only virtual file system with the following directories:
*
* | path | physical/virtual |
* |:-------|:----------------------|
@ -198,7 +199,7 @@ namespace vfs {
* | /.lib | physical: tests/lib |
* | /.src | virtual |
*/
public static getBuiltLocal(useCaseSensitiveFileNames: boolean = Harness.IO.useCaseSensitiveFileNames()): VirtualFileSystem {
public static getBuiltLocal(useCaseSensitiveFileNames: boolean): VirtualFileSystem {
let vfs = useCaseSensitiveFileNames ? this._builtLocalCS : this._builtLocalCI;
if (!vfs) {
vfs = this._builtLocal;
@ -228,27 +229,24 @@ namespace vfs {
return vfs;
}
public static createFromOptions(options: { useCaseSensitiveFileNames?: boolean, currentDirectory?: string }) {
const vfs = this.getBuiltLocal(options.useCaseSensitiveFileNames).shadow();
if (options.currentDirectory) {
public static createFromDocuments(useCaseSensitiveFileNames: boolean, documents: documents.TextDocument[], options?: { currentDirectory?: string, overwrite?: boolean }) {
const vfs = this.getBuiltLocal(useCaseSensitiveFileNames).shadow();
if (options && options.currentDirectory) {
vfs.addDirectory(options.currentDirectory);
vfs.changeDirectory(options.currentDirectory);
}
return vfs;
}
public static createFromTestFiles(options: { useCaseSensitiveFileNames?: boolean, currentDirectory?: string }, documents: Harness.Compiler.TestFile[], fileOptions?: { overwrite?: boolean }) {
const vfs = this.createFromOptions(options);
const fileOptions = options && options.overwrite ? { overwrite: true } : undefined;
for (const document of documents) {
const file = vfs.addFile(document.unitName, document.content, fileOptions)!;
assert.isDefined(file, `Failed to add file: '${document.unitName}'`);
const file = vfs.addFile(document.file, document.text, fileOptions)!;
assert.isDefined(file, `Failed to add file: '${document.file}'`);
file.metadata.set("document", document);
// Add symlinks
const symlink = document.fileOptions && document.fileOptions.symlink;
const symlink = document.meta.get("symlink");
if (file && symlink) {
for (const link of symlink.split(",")) {
const symlink = vfs.addSymlink(vpath.resolve(vfs.currentDirectory, link.trim()), file)!;
assert.isDefined(symlink, `Failed to symlink: '${link}'`);
assert.isDefined(symlink, `Failed to create symlink: '${link}'`);
symlink.metadata.set("document", document);
}
}

View File

@ -59,7 +59,7 @@ namespace vpath {
return hasTrailingSeparator(path) ? path.slice(0, -1) : path;
}
function reduce(components: string[]) {
function reduce(components: ReadonlyArray<string>) {
const normalized = [components[0]];
for (let i = 1; i < components.length; i++) {
const component = components[i];
@ -242,7 +242,7 @@ namespace vpath {
/**
* Formats a parsed path consisting of a root component and zero or more path segments.
*/
export function format(components: string[]) {
export function format(components: ReadonlyArray<string>) {
return components.length ? components[0] + components.slice(1).join(sep) : "";
}
@ -262,31 +262,33 @@ namespace vpath {
* Gets the portion of a path following the last separator (`/`).
* If the base name has any one of the provided extensions, it is removed.
*/
export function basename(path: string, extensions: string | string[], ignoreCase: boolean): string;
export function basename(path: string, extensions?: string | string[], ignoreCase?: boolean) {
export function basename(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
export function basename(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
path = normalizeSeparators(path);
const name = path.substr(Math.max(getRootLength(path), path.lastIndexOf(sep) + 1));
const extension = extensions ? extname(path, extensions, ignoreCase) : undefined;
return extension ? name.slice(0, name.length - extension.length) : name;
}
const extRegExp = /\.\w+$/;
function extnameWorker(path: string, extensions: string | string[], stringEqualityComparer: core.EqualityComparer<string>) {
function extnameWorker(path: string, extensions: string | ReadonlyArray<string>, stringEqualityComparer: core.EqualityComparer<string>) {
const manyExtensions = Array.isArray(extensions) ? extensions : undefined;
const singleExtension = Array.isArray(extensions) ? undefined : extensions;
const length = manyExtensions ? manyExtensions.length : 1;
for (let i = 0; i < length; i++) {
let extension = manyExtensions ? manyExtensions[i] : singleExtension;
if (!extension.startsWith(".")) extension = "." + extension;
if (path.length >= extension.length &&
stringEqualityComparer(path.slice(path.length - extension.length), extension)) {
return extension;
if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") {
const pathExtension = path.slice(path.length - extension.length);
if (stringEqualityComparer(pathExtension, extension)) {
return pathExtension;
}
}
}
return "";
}
const extRegExp = /\.\w+$/;
/**
* Gets the file extension for a path.
*/
@ -294,8 +296,8 @@ namespace vpath {
/**
* Gets the file extension for a path, provided it is one of the provided extensions.
*/
export function extname(path: string, extensions: string | string[], ignoreCase: boolean): string;
export function extname(path: string, extensions?: string | string[], ignoreCase?: boolean) {
export function extname(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
export function extname(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
if (extensions) {
return extnameWorker(path, extensions, ignoreCase ? core.equateStringsCaseInsensitive : core.equateStringsCaseSensitive);
}
@ -305,32 +307,40 @@ namespace vpath {
}
export function changeExtension(path: string, ext: string): string;
export function changeExtension(path: string, ext: string, extensions: string | string[], ignoreCase: boolean): string;
export function changeExtension(path: string, ext: string, extensions?: string | string[], ignoreCase?: boolean) {
export function changeExtension(path: string, ext: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
const pathext = extensions ? extname(path, extensions, ignoreCase) : extname(path);
return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith(".") ? ext : "." + ext) : path;
}
const typeScriptExtensions: ReadonlyArray<string> = [".ts", ".tsx"];
export function isTypeScript(path: string) {
return path.endsWith(".ts")
|| path.endsWith(".tsx");
return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0;
}
const javaScriptExtensions: ReadonlyArray<string> = [".js", ".jsx"];
export function isJavaScript(path: string) {
return path.endsWith(".js")
|| path.endsWith(".jsx");
return extname(path, javaScriptExtensions, /*ignoreCase*/ false).length > 0;
}
export function isDeclaration(path: string) {
return path.endsWith(".d.ts");
return extname(path, ".d.ts", /*ignoreCase*/ false).length > 0;
}
export function isSourceMap(path: string) {
return path.endsWith(".map");
return extname(path, ".map", /*ignoreCase*/ false).length > 0;
}
const javaScriptSourceMapExtensions: ReadonlyArray<string> = [".js.map", ".jsx.map"];
export function isJavaScriptSourceMap(path: string) {
return extname(path, javaScriptSourceMapExtensions, /*ignoreCase*/ false).length > 0;
}
export function isJson(path: string) {
return path.endsWith(".json");
return extname(path, ".json", /*ignoreCase*/ false).length > 0;
}
export function isDefaultLibrary(path: string) {