Cleanup and reorganize fakes

This commit is contained in:
Ron Buckton 2018-04-19 11:30:03 -07:00
parent 1658770015
commit 0322d71515
14 changed files with 329 additions and 355 deletions

View File

@ -5,229 +5,19 @@
/// <reference path="./vfs.ts" />
/// <reference path="./vfsutils.ts" />
/// <reference path="./utils.ts" />
/// <reference path="./fakes.ts" />
// 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.
namespace compiler {
/**
* A `ts.CompilerHost` that leverages a virtual file system.
*/
export class CompilerHost implements ts.CompilerHost {
public readonly vfs: vfs.FileSystem;
public readonly defaultLibLocation: string;
public readonly outputs: documents.TextDocument[] = [];
public readonly traces: string[] = [];
public readonly shouldAssertInvariants = !Harness.lightMode;
private _setParentNodes: boolean;
private _sourceFiles: core.SortedMap<string, ts.SourceFile>;
private _newLine: string;
private _parseConfigHost: ParseConfigHost;
constructor(vfs: vfs.FileSystem, options: ts.CompilerOptions, setParentNodes = false) {
this.vfs = vfs;
this.defaultLibLocation = vfs.meta.get("defaultLibLocation") || "";
this._sourceFiles = new core.SortedMap<string, ts.SourceFile>({ comparer: this.vfs.stringComparer, sort: "insertion" });
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 {
return this.vfs.cwd();
}
public useCaseSensitiveFileNames(): boolean {
return !this.vfs.ignoreCase;
}
public getNewLine(): string {
return this._newLine;
}
public getCanonicalFileName(fileName: string): string {
return this.vfs.ignoreCase ? fileName.toLowerCase() : fileName;
}
public fileExists(fileName: string): boolean {
return vfsutils.fileExists(this.vfs, fileName);
}
public directoryExists(directoryName: string): boolean {
return vfsutils.directoryExists(this.vfs, directoryName);
}
public getDirectories(path: string): string[] {
return vfsutils.getDirectories(this.vfs, path);
}
public readFile(path: string): string | undefined {
if (path.endsWith("lib.d.ts")) debugger;
return vfsutils.readFile(this.vfs, path);
}
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
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;
}
}
public trace(s: string): void {
this.traces.push(s);
}
public realpath(path: string): string {
return this.vfs.realpathSync(path);
}
public getDefaultLibLocation(): string {
return vpath.resolve(this.vfs.cwd(), this.defaultLibLocation);
}
public getDefaultLibFileName(options: ts.CompilerOptions): string {
// 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";
}
}
}
public getSourceFile(fileName: string, languageVersion: number): ts.SourceFile | undefined {
const canonicalFileName = this.getCanonicalFileName(vpath.resolve(this.vfs.cwd(), fileName));
const existing = this._sourceFiles.get(canonicalFileName);
if (existing) return existing;
const content = this.readFile(canonicalFileName);
if (content === undefined) return undefined;
// 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).
const cacheKey = this.vfs.shadowRoot && `SourceFile[languageVersion=${languageVersion},setParentNodes=${this._setParentNodes}]`;
if (cacheKey) {
const meta = this.vfs.filemeta(canonicalFileName);
const sourceFileFromMetadata = meta.get(cacheKey) as ts.SourceFile | undefined;
if (sourceFileFromMetadata) {
this._sourceFiles.set(canonicalFileName, sourceFileFromMetadata);
return sourceFileFromMetadata;
}
}
const parsed = ts.createSourceFile(fileName, content, languageVersion, this._setParentNodes || this.shouldAssertInvariants);
if (this.shouldAssertInvariants) {
Utils.assertInvariants(parsed, /*parent*/ undefined);
}
this._sourceFiles.set(canonicalFileName, parsed);
if (cacheKey) {
// store the cached source file on the unshadowed file with the same version.
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;
}
}
if (fs !== this.vfs) {
fs.filemeta(canonicalFileName).set(cacheKey, parsed);
}
}
return parsed;
}
}
/**
* A `ts.ParseConfigHost` that leverages a virtual file system.
*/
export class ParseConfigHost implements ts.ParseConfigHost {
public readonly vfs: vfs.FileSystem;
constructor(vfs: vfs.FileSystem) {
this.vfs = vfs;
}
public get useCaseSensitiveFileNames() {
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);
}
public readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[] {
return ts.matchFiles(
path,
extensions,
excludes,
includes,
!this.vfs.ignoreCase,
this.vfs.cwd(),
depth,
path => vfsutils.getAccessibleFileSystemEntries(this.vfs, path));
}
}
export interface Project {
file: string;
config?: ts.ParsedCommandLine;
errors?: ts.Diagnostic[];
}
export function readProject(host: ParseConfigHost, project: string | undefined, existingOptions?: ts.CompilerOptions): Project | undefined {
export function readProject(host: fakes.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");
@ -265,7 +55,7 @@ namespace compiler {
}
export class CompilationResult {
public readonly host: CompilerHost;
public readonly host: fakes.CompilerHost;
public readonly program: ts.Program | undefined;
public readonly result: ts.EmitResult | undefined;
public readonly options: ts.CompilerOptions;
@ -277,7 +67,7 @@ namespace compiler {
private _inputs: documents.TextDocument[] = [];
private _inputsAndOutputs: core.SortedMap<string, CompilationOutput>;
constructor(host: CompilerHost, options: ts.CompilerOptions, program: ts.Program | undefined, result: ts.EmitResult | undefined, diagnostics: ts.Diagnostic[]) {
constructor(host: fakes.CompilerHost, options: ts.CompilerOptions, program: ts.Program | undefined, result: ts.EmitResult | undefined, diagnostics: ts.Diagnostic[]) {
this.host = host;
this.program = program;
this.result = result;
@ -438,7 +228,7 @@ namespace compiler {
}
}
export function compileFiles(host: CompilerHost, rootFiles: string[] | undefined, compilerOptions: ts.CompilerOptions): CompilationResult {
export function compileFiles(host: fakes.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) {

View File

@ -15,16 +15,12 @@ namespace core {
}
export function getByteOrderMarkLength(text: string): number {
if (text.length >= 2) {
if (text.length >= 1) {
const ch0 = text.charCodeAt(0);
const ch1 = text.charCodeAt(1);
if ((ch0 === 0xff && ch1 === 0xfe) ||
(ch0 === 0xfe && ch1 === 0xff)) {
return 2;
}
if (text.length >= 3 && ch0 === 0xef && ch1 === 0xbb && text.charCodeAt(2) === 0xbf) {
return 3;
}
if (ch0 === 0xfeff) return 1;
if (ch0 === 0xfe) return text.length >= 2 && text.charCodeAt(1) === 0xff ? 2 : 0;
if (ch0 === 0xff) return text.length >= 2 && text.charCodeAt(1) === 0xfe ? 2 : 0;
if (ch0 === 0xef) return text.length >= 3 && text.charCodeAt(1) === 0xbb && text.charCodeAt(2) === 0xbf ? 3 : 0;
}
return 0;
}

View File

@ -9,76 +9,63 @@
namespace fakes {
const processExitSentinel = new Error("System exit");
export interface ServerHostOptions {
/**
* The virtual path to tsc.js. If not specified, a default of `"/.ts/tsc.js"` is used.
*/
export interface SystemOptions {
executingFilePath?: string;
newLine?: "\r\n" | "\n";
safeList?: boolean;
lib?: boolean;
dos?: boolean;
env?: Record<string, string>;
}
export class ServerHost implements ts.server.ServerHost, ts.FormatDiagnosticsHost {
/**
* A fake `ts.System` that leverages a virtual file system.
*/
export class System implements ts.System {
public readonly vfs: vfs.FileSystem;
public readonly args: string[] = [];
public readonly output: string[] = [];
public readonly newLine: string;
public readonly useCaseSensitiveFileNames: boolean;
public exitCode: number;
private readonly _output: string[] = [];
private readonly _executingFilePath: string;
private readonly _getCanonicalFileName: (file: string) => string;
constructor(vfs: vfs.FileSystem, options: ServerHostOptions = {}) {
const {
dos = false,
executingFilePath = dos
? vfsutils.dosTscPath
: vfsutils.tscPath,
newLine = "\n",
safeList = false,
lib = false,
} = options;
private readonly _executingFilePath: string | undefined;
private readonly _env: Record<string, string> | undefined;
constructor(vfs: vfs.FileSystem, { executingFilePath, newLine = "\n", env }: SystemOptions = {}) {
this.vfs = vfs.isReadonly ? vfs.shadow() : vfs;
this.useCaseSensitiveFileNames = !this.vfs.ignoreCase;
this.newLine = newLine;
this._executingFilePath = executingFilePath;
this._getCanonicalFileName = ts.createGetCanonicalFileName(this.useCaseSensitiveFileNames);
if (safeList) {
const safelistPath = dos ? vfsutils.dosSafelistPath : vfsutils.safelistPath;
this.vfs.mkdirpSync(vpath.dirname(safelistPath));
this.vfs.writeFileSync(safelistPath, vfsutils.safelistContent);
}
if (lib) {
const libPath = dos ? vfsutils.dosLibPath : vfsutils.libPath;
this.vfs.mkdirpSync(vpath.dirname(libPath));
this.vfs.writeFileSync(libPath, vfsutils.emptyLibContent);
}
this._env = env;
}
// #region System members
public readonly newLine: string;
public readonly useCaseSensitiveFileNames: boolean;
public write(message: string) {
this._output.push(message);
this.output.push(message);
}
public readFile(path: string) {
return vfsutils.readFile(this.vfs, path);
try {
const content = this.vfs.readFileSync(path, "utf8");
return content === undefined ? undefined :
vpath.extname(path) === ".json" ? utils.removeComments(core.removeByteOrderMark(content), utils.CommentRemoval.leadingAndTrailing) :
core.removeByteOrderMark(content);
}
catch {
return undefined;
}
}
public writeFile(path: string, data: string, writeByteOrderMark?: boolean): void {
vfsutils.writeFile(this.vfs, path, data, writeByteOrderMark);
this.vfs.mkdirpSync(vpath.dirname(path));
this.vfs.writeFileSync(path, writeByteOrderMark ? core.addUTF8ByteOrderMark(data) : data);
}
public fileExists(path: string) {
return vfsutils.fileExists(this.vfs, path);
const stats = this._getStats(path);
return stats ? stats.isFile() : false;
}
public directoryExists(path: string) {
return vfsutils.directoryExists(this.vfs, path);
const stats = this._getStats(path);
return stats ? stats.isDirectory() : false;
}
public createDirectory(path: string): void {
@ -90,12 +77,38 @@ namespace fakes {
}
public getDirectories(path: string) {
return vfsutils.getDirectories(this.vfs, path);
const result: string[] = [];
try {
for (const file of this.vfs.readdirSync(path)) {
if (this.vfs.statSync(vpath.combine(path, file)).isDirectory()) {
result.push(file);
}
}
}
catch { /*ignore*/ }
return result;
}
public readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.vfs.cwd(), depth, path => {
return vfsutils.getAccessibleFileSystemEntries(this.vfs, path);
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => {
const files: string[] = [];
const directories: string[] = [];
try {
for (const file of this.vfs.readdirSync(path)) {
try {
const stats = this.vfs.statSync(vpath.combine(path, file));
if (stats.isFile()) {
files.push(file);
}
else if (stats.isDirectory()) {
directories.push(file);
}
}
catch { /*ignored*/ }
}
}
catch { /*ignored*/ }
return { files, directories };
});
}
@ -104,18 +117,9 @@ namespace fakes {
throw processExitSentinel;
}
public readonly args: string[] = [];
public getFileSize(path: string) {
return vfsutils.getFileSize(this.vfs, path);
}
public watchFile(_path: string, _cb: ts.FileWatcherCallback): ts.FileWatcher {
return { close(): void { /*ignored*/ } };
}
public watchDirectory(_path: string, _cb: ts.DirectoryWatcherCallback, _recursive?: boolean): ts.FileWatcher {
return { close(): void { /*ignored*/ } };
const stats = this._getStats(path);
return stats && stats.isFile() ? stats.size : 0;
}
public resolvePath(path: string) {
@ -123,11 +127,13 @@ namespace fakes {
}
public getExecutingFilePath() {
if (this._executingFilePath === undefined) return ts.notImplemented();
return this._executingFilePath;
}
public getModifiedTime(path: string) {
return vfsutils.getModifiedTime(this.vfs, path);
const stats = this._getStats(path);
return stats ? stats.mtime : undefined;
}
public createHash(data: string): string {
@ -143,38 +149,231 @@ namespace fakes {
}
}
public getEnvironmentVariable(_name: string): string | undefined {
return undefined;
public getEnvironmentVariable(name: string): string | undefined {
return this._env && this._env[name];
}
public setTimeout(_callback: (...args: any[]) => void, _timeout: number, ..._args: any[]) {
return ts.notImplemented();
private _getStats(path: string) {
try {
return this.vfs.statSync(path);
}
catch {
return undefined;
}
}
}
/**
* A fake `ts.ParseConfigHost` that leverages a virtual file system.
*/
export class ParseConfigHost implements ts.ParseConfigHost {
public readonly sys: System;
constructor(sys: System | vfs.FileSystem) {
if (sys instanceof vfs.FileSystem) sys = new System(sys);
this.sys = sys;
}
public clearTimeout(_timeoutId: any): void {
return ts.notImplemented();
}
// #endregion System members
// #region FormatDiagnosticsHost members
public getNewLine() {
return this.newLine;
public get vfs() {
return this.sys.vfs;
}
public getCanonicalFileName(fileName: string) {
return this._getCanonicalFileName(fileName);
}
// #endregion FormatDiagnosticsHost members
// #region ServerHost members
public setImmediate(_callback: (...args: any[]) => void, ..._args: any[]): any {
return ts.notImplemented();
public get useCaseSensitiveFileNames() {
return this.sys.useCaseSensitiveFileNames;
}
public clearImmediate(_timeoutId: any): void {
return ts.notImplemented();
public fileExists(fileName: string): boolean {
return this.sys.fileExists(fileName);
}
public directoryExists(directoryName: string): boolean {
return this.sys.directoryExists(directoryName);
}
public readFile(path: string): string | undefined {
return this.sys.readFile(path);
}
public readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[] {
return this.sys.readDirectory(path, extensions, excludes, includes, depth);
}
}
/**
* A fake `ts.CompilerHost` that leverages a virtual file system.
*/
export class CompilerHost implements ts.CompilerHost {
public readonly sys: System;
public readonly defaultLibLocation: string;
public readonly outputs: documents.TextDocument[] = [];
public readonly traces: string[] = [];
public readonly shouldAssertInvariants = !Harness.lightMode;
private _setParentNodes: boolean;
private _sourceFiles: core.SortedMap<string, ts.SourceFile>;
private _parseConfigHost: ParseConfigHost;
private _newLine: string;
constructor(sys: System | vfs.FileSystem, options: ts.CompilerOptions, setParentNodes = false) {
if (sys instanceof vfs.FileSystem) sys = new System(sys, { newLine: "\r\n" });
this.sys = sys;
this.defaultLibLocation = sys.vfs.meta.get("defaultLibLocation") || "";
this._newLine = ts.getNewLineCharacter(options, () => this.sys.newLine);
this._sourceFiles = new core.SortedMap<string, ts.SourceFile>({ comparer: sys.vfs.stringComparer, sort: "insertion" });
this._setParentNodes = setParentNodes;
}
public get vfs() {
return this.sys.vfs;
}
public get parseConfigHost() {
return this._parseConfigHost || (this._parseConfigHost = new ParseConfigHost(this.sys));
}
public getCurrentDirectory(): string {
return this.sys.getCurrentDirectory();
}
public useCaseSensitiveFileNames(): boolean {
return this.sys.useCaseSensitiveFileNames;
}
public getNewLine(): string {
return this._newLine;
}
public getCanonicalFileName(fileName: string): string {
return this.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
}
public fileExists(fileName: string): boolean {
return this.sys.fileExists(fileName);
}
public directoryExists(directoryName: string): boolean {
return this.sys.directoryExists(directoryName);
}
public getDirectories(path: string): string[] {
return this.sys.getDirectories(path);
}
public readFile(path: string): string | undefined {
return this.sys.readFile(path);
}
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
if (writeByteOrderMark) content = core.addUTF8ByteOrderMark(content);
this.sys.writeFile(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;
}
}
public trace(s: string): void {
this.traces.push(s);
}
public realpath(path: string): string {
return this.sys.realpath(path);
}
public getDefaultLibLocation(): string {
return vpath.resolve(this.getCurrentDirectory(), this.defaultLibLocation);
}
public getDefaultLibFileName(options: ts.CompilerOptions): string {
// 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";
}
}
}
public getSourceFile(fileName: string, languageVersion: number): ts.SourceFile | undefined {
const canonicalFileName = this.getCanonicalFileName(vpath.resolve(this.getCurrentDirectory(), fileName));
const existing = this._sourceFiles.get(canonicalFileName);
if (existing) return existing;
const content = this.readFile(canonicalFileName);
if (content === undefined) return undefined;
// 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).
const cacheKey = this.vfs.shadowRoot && `SourceFile[languageVersion=${languageVersion},setParentNodes=${this._setParentNodes}]`;
if (cacheKey) {
const meta = this.vfs.filemeta(canonicalFileName);
const sourceFileFromMetadata = meta.get(cacheKey) as ts.SourceFile | undefined;
if (sourceFileFromMetadata) {
this._sourceFiles.set(canonicalFileName, sourceFileFromMetadata);
return sourceFileFromMetadata;
}
}
const parsed = ts.createSourceFile(fileName, content, languageVersion, this._setParentNodes || this.shouldAssertInvariants);
if (this.shouldAssertInvariants) {
Utils.assertInvariants(parsed, /*parent*/ undefined);
}
this._sourceFiles.set(canonicalFileName, parsed);
if (cacheKey) {
// store the cached source file on the unshadowed file with the same version.
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;
}
}
if (fs !== this.vfs) {
fs.filemeta(canonicalFileName).set(cacheKey, parsed);
}
}
return parsed;
}
// #endregion ServerHost members
}
}

View File

@ -276,7 +276,7 @@ namespace FourSlash {
if (configFileName) {
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
const host = new compiler.ParseConfigHost(vfsutils.createFromMap(baseDir, /*ignoreCase*/ true, this.inputFiles));
const host = new fakes.ParseConfigHost(vfsutils.createFromMap(baseDir, /*ignoreCase*/ true, this.inputFiles));
const configJsonObj = ts.parseConfigFileTextToJson(configFileName, this.inputFiles.get(configFileName));
assert.isTrue(configJsonObj.config !== undefined);

View File

@ -1248,7 +1248,7 @@ namespace Harness {
}
return compiler.compileFiles(
new compiler.CompilerHost(
new fakes.CompilerHost(
vfsutils.createFromDocuments(
useCaseSensitiveFileNames,
inputFiles.concat(otherFiles).map(documents.TextDocument.fromTestFile),

View File

@ -117,8 +117,8 @@ namespace Harness.LanguageService {
}
export abstract class LanguageServiceAdapterHost {
public typesRegistry: ts.Map<void> | undefined;
public readonly vfs = new vfs.FileSystem(/*ignoreCase*/ true, { cwd: virtualFileSystemRoot });
public typesRegistry: ts.Map<void> | undefined;
private scriptInfos: core.SortedMap<string, ScriptInfo>;
constructor(protected cancellationToken = DefaultHostCancellationToken.instance,
@ -139,17 +139,6 @@ namespace Harness.LanguageService {
fileNames.push(scriptInfo.fileName);
}
});
// this.vfs.scanSync("/", "descendants-or-self", {
// accept: (path, stats) => {
// if (stats.isFile()) {
// const scriptInfo = this.vfs.filemeta(path).get("scriptInfo") as ScriptInfo;
// if (scriptInfo && scriptInfo.isRootFile) {
// fileNames.push(scriptInfo.fileName);
// }
// }
// return false;
// }
// });
return fileNames;
}

View File

@ -72,17 +72,17 @@ namespace project {
}
}
class ProjectCompilerHost extends compiler.CompilerHost {
class ProjectCompilerHost extends fakes.CompilerHost {
private _testCase: ProjectRunnerTestCase & ts.CompilerOptions;
private _projectParseConfigHost: ProjectParseConfigHost;
constructor(vfs: vfs.FileSystem, compilerOptions: ts.CompilerOptions, _testCaseJustName: string, testCase: ProjectRunnerTestCase & ts.CompilerOptions, _moduleKind: ts.ModuleKind) {
super(vfs, compilerOptions);
constructor(sys: fakes.System | vfs.FileSystem, compilerOptions: ts.CompilerOptions, _testCaseJustName: string, testCase: ProjectRunnerTestCase & ts.CompilerOptions, _moduleKind: ts.ModuleKind) {
super(sys, compilerOptions);
this._testCase = testCase;
}
public get parseConfigHost(): compiler.ParseConfigHost {
return this._projectParseConfigHost || (this._projectParseConfigHost = new ProjectParseConfigHost(this.vfs, this._testCase));
public get parseConfigHost(): fakes.ParseConfigHost {
return this._projectParseConfigHost || (this._projectParseConfigHost = new ProjectParseConfigHost(this.sys, this._testCase));
}
public getDefaultLibFileName(_options: ts.CompilerOptions) {
@ -90,11 +90,11 @@ namespace project {
}
}
class ProjectParseConfigHost extends compiler.ParseConfigHost {
class ProjectParseConfigHost extends fakes.ParseConfigHost {
private _testCase: ProjectRunnerTestCase & ts.CompilerOptions;
constructor(vfs: vfs.FileSystem, testCase: ProjectRunnerTestCase & ts.CompilerOptions) {
super(vfs);
constructor(sys: fakes.System, testCase: ProjectRunnerTestCase & ts.CompilerOptions) {
super(sys);
this._testCase = testCase;
}
@ -149,7 +149,7 @@ namespace project {
if (configFileName) {
const result = ts.readJsonConfigFile(configFileName, path => vfsutils.readFile(this.vfs, path));
configFileSourceFiles.push(result);
const configParseHost = new ProjectParseConfigHost(this.vfs, this.testCase);
const configParseHost = new ProjectParseConfigHost(new fakes.System(this.vfs), this.testCase);
const configParseResult = ts.parseJsonSourceFileConfigFileContent(result, configParseHost, ts.getDirectoryPath(configFileName), this.compilerOptions);
inputFiles = configParseResult.fileNames;
this.compilerOptions = configParseResult.options;

View File

@ -115,10 +115,10 @@ namespace ts {
}
const caseInsensitiveBasePath = "c:/dev/";
const caseInsensitiveHost = new compiler.ParseConfigHost(createFileSystem(/*ignoreCase*/ true, caseInsensitiveBasePath, "c:/"));
const caseInsensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreCase*/ true, caseInsensitiveBasePath, "c:/"));
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new compiler.ParseConfigHost(createFileSystem(/*ignoreCase*/ false, caseSensitiveBasePath, "/"));
const caseSensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreCase*/ false, caseSensitiveBasePath, "/"));
function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) {
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
@ -132,7 +132,7 @@ namespace ts {
}
describe("configurationExtension", () => {
forEach<[string, string, compiler.ParseConfigHost], void>([
forEach<[string, string, fakes.ParseConfigHost], void>([
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
], ([testName, basePath, host]) => {

View File

@ -33,7 +33,7 @@ namespace ts {
const result = parseJsonText(configFileName, fileText);
assert(!result.parseDiagnostics.length);
assert(!!result.endOfFileToken);
const host: ParseConfigHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const host: ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
expectedResult.compilerOptions.configFilePath = configFileName;

View File

@ -45,7 +45,7 @@ namespace ts {
const result = parseJsonText(configFileName, fileText);
assert(!result.parseDiagnostics.length);
assert(!!result.endOfFileToken);
const host: ParseConfigHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const host: ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const { typeAcquisition: actualTypeAcquisition, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
verifyAcquisition(actualTypeAcquisition, expectedResult);

View File

@ -1,6 +1,5 @@
/// <reference path="..\harness.ts" />
/// <reference path="tsserverProjectSystem.ts" />
/// <reference path="../fakes.ts" />
namespace ts {
interface Range {
@ -111,7 +110,7 @@ namespace ts {
function runBaseline(extension: Extension) {
const path = "/a" + extension;
const program = makeProgram(path, t.source, includeLib);
const program = makeProgram({ path, content: t.source }, includeLib);
if (hasSyntacticDiagnostics(program)) {
// Don't bother generating JS baselines for inputs that aren't valid JS.
@ -147,19 +146,17 @@ namespace ts {
const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation);
data.push(newTextWithRename);
const diagProgram = makeProgram(path, newText, includeLib);
const diagProgram = makeProgram({ path, content: newText }, includeLib);
assert.isFalse(hasSyntacticDiagnostics(diagProgram));
}
return data.join(newLineCharacter);
});
}
function makeProgram(path: string, content: string, includeLib?: boolean) {
// libFile is expensive to parse repeatedly - only test when required
const fs = new vfs.FileSystem(/*ignoreCase*/ true, { files: { [path]: content } });
const host = new fakes.ServerHost(fs, { lib: includeLib });
function makeProgram(f: { path: string, content: string }, includeLib?: boolean) {
const host = projectSystem.createServerHost(includeLib ? [f, projectSystem.libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(path);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
return program;
}
@ -177,12 +174,15 @@ namespace ts {
if (!selectionRange) {
throw new Error(`Test ${caption} does not specify selection range`);
}
const fs = new vfs.FileSystem(/*ignoreCase*/ true, { files: { "/a.ts": t.source } });
const host = new fakes.ServerHost(fs, { lib: true });
const f = {
path: "/a.ts",
content: t.source
};
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile("/a.ts");
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const sourceFile = program.getSourceFile("/a.ts");
const sourceFile = program.getSourceFile(f.path);
const context: RefactorContext = {
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
program,

View File

@ -5,7 +5,7 @@
namespace ts {
const caseInsensitiveBasePath = "c:/dev/";
const caseInsensitiveTsconfigPath = "c:/dev/tsconfig.json";
const caseInsensitiveHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
const caseInsensitiveHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
"c:/dev/a.ts": "",
"c:/dev/a.d.ts": "",
"c:/dev/a.js": "",
@ -32,7 +32,7 @@ namespace ts {
}}));
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: caseSensitiveBasePath, files: {
const caseSensitiveHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: caseSensitiveBasePath, files: {
"/dev/a.ts": "",
"/dev/a.d.ts": "",
"/dev/a.js": "",
@ -56,7 +56,7 @@ namespace ts {
"/dev/js/b.js": "",
}}));
const caseInsensitiveMixedExtensionHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
const caseInsensitiveMixedExtensionHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
"c:/dev/a.ts": "",
"c:/dev/a.d.ts": "",
"c:/dev/a.js": "",
@ -70,7 +70,7 @@ namespace ts {
"c:/dev/f.other": "",
}}));
const caseInsensitiveCommonFoldersHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
const caseInsensitiveCommonFoldersHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
"c:/dev/a.ts": "",
"c:/dev/a.d.ts": "",
"c:/dev/a.js": "",
@ -81,7 +81,7 @@ namespace ts {
"c:/dev/jspm_packages/a.ts": "",
}}));
const caseInsensitiveDottedFoldersHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
const caseInsensitiveDottedFoldersHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
"c:/dev/x/d.ts": "",
"c:/dev/x/y/d.ts": "",
"c:/dev/x/y/.e.ts": "",
@ -92,13 +92,13 @@ namespace ts {
"c:/dev/g.min.js/.g/g.ts": "",
}}));
const caseInsensitiveOrderingDiffersWithCaseHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
const caseInsensitiveOrderingDiffersWithCaseHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: caseInsensitiveBasePath, files: {
"c:/dev/xylophone.ts": "",
"c:/dev/Yosemite.ts": "",
"c:/dev/zebra.ts": "",
}}));
const caseSensitiveOrderingDiffersWithCaseHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: caseSensitiveBasePath, files: {
const caseSensitiveOrderingDiffersWithCaseHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: caseSensitiveBasePath, files: {
"/dev/xylophone.ts": "",
"/dev/Yosemite.ts": "",
"/dev/zebra.ts": "",

View File

@ -36,7 +36,7 @@ namespace ts {
"/// <reference path=\"nonexistent4\"/>\n" // No extension
);
const testCompilerHost = new compiler.CompilerHost(
const testCompilerHost = new fakes.CompilerHost(
vfsutils.createFromDocuments(
/*useCaseSensitiveFileNames*/ false,
[emptyFile, referenceFile],

View File

@ -34,14 +34,14 @@ namespace ts {
function getParsedCommandJson(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
const parsed = parseConfigFileTextToJson(configFileName, jsonText);
const files = allFileList.reduce((files, value) => (files[value] = "", files), {} as vfs.FileSet);
const host: ParseConfigHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
const host: ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
return parseJsonConfigFileContent(parsed.config, host, basePath, /*existingOptions*/ undefined, configFileName);
}
function getParsedCommandJsonNode(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
const parsed = parseJsonText(configFileName, jsonText);
const files = allFileList.reduce((files, value) => (files[value] = "", files), {} as vfs.FileSet);
const host: ParseConfigHost = new compiler.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
const host: ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
return parseJsonSourceFileConfigFileContent(parsed, host, basePath, /*existingOptions*/ undefined, configFileName);
}