diff --git a/lib/compiler.ts b/lib/compiler.ts index de75edb0..5df1bdbb 100644 --- a/lib/compiler.ts +++ b/lib/compiler.ts @@ -33,7 +33,7 @@ export class ProjectCompiler implements ICompiler { finalTransformers: FinalTransformers; host: Host; project: ProjectInfo; - program: ts.Program; + program: ts.BuilderProgram; private hasSourceMap: boolean; prepare(project: ProjectInfo, finalTransformers?: FinalTransformers) { @@ -68,24 +68,21 @@ export class ProjectCompiler implements ICompiler { rootFilenames.map(fileName => this.project.input.getFile(fileName).gulp.cwd) ); - this.host = new Host( - this.project.typescript, - currentDirectory, - this.project.input, - this.project.options - ); - - // Calling `createProgram` with an object is only supported in 3.0. Only call this overload - // if we have project references (also only supported in 3.0) - this.program = this.project.projectReferences - ? this.project.typescript.createProgram({ + if (this.program === undefined) { + this.host = new Host( + this.project.typescript, + currentDirectory, + this.project.input, + this.project.options + ); + this.program = this.project.typescript.createIncrementalProgram({ rootNames: rootFilenames, options: this.project.options, projectReferences: this.project.projectReferences, host: this.host, - oldProgram: this.program - }) - : this.project.typescript.createProgram(rootFilenames, this.project.options, this.host, this.program); + createProgram: this.project.typescript.createAbstractBuilder + }); + } const result = emptyCompilationResult(this.project.options.noEmit); @@ -178,7 +175,7 @@ export class ProjectCompiler implements ICompiler { callback, undefined, false, - this.finalTransformers ? this.finalTransformers(this.program) : undefined, + this.finalTransformers ? this.finalTransformers(this.program.getProgram()) : undefined, ); result.emitSkipped = emitOutput.emitSkipped; diff --git a/lib/host.ts b/lib/host.ts index 6408f7ec..de60a00c 100644 --- a/lib/host.ts +++ b/lib/host.ts @@ -81,7 +81,10 @@ export class Host implements ts.CompilerHost { let sourceFile = this.input.getFile(fileName); if (sourceFile) return sourceFile.ts; - return this.fallback.getSourceFile(fileName, languageVersion, onError); + const file = this.fallback.getSourceFile(fileName, languageVersion, onError); + if (file === undefined) return undefined; + (file as any).version = this.input.versionString; + return file; } realpath = (path: string) => this.fallback.realpath(path); @@ -91,5 +94,7 @@ export class Host implements ts.CompilerHost { directoryExists = (path: string) => this.fallback.directoryExists(path); readDirectory = (rootDir: string, extensions: string[], excludes: string[], includes: string[], depth?: number) => - this.fallback.readDirectory(rootDir, extensions, excludes, includes, depth) + this.fallback.readDirectory(rootDir, extensions, excludes, includes, depth); + + createHash = (data: string) => this.fallback.createHash(data); } diff --git a/lib/input.ts b/lib/input.ts index 61d87ae4..4127d832 100644 --- a/lib/input.ts +++ b/lib/input.ts @@ -156,10 +156,11 @@ export class FileCache { current: FileDictionary; options: ts.CompilerOptions; caseSensitive: boolean; - noParse: boolean = false; // true when using a file based compiler. + noParse = false; // true when using a file based compiler. typescript: typeof ts; - version: number = 0; + version = 0; + versionString = "0"; constructor(typescript: typeof ts, options: ts.CompilerOptions, caseSensitive: boolean) { this.typescript = typescript; @@ -177,6 +178,7 @@ export class FileCache { reset() { this.version++; + this.versionString = this.version.toString(); this.previous = this.current; this.createDictionary(); } @@ -196,6 +198,7 @@ export class FileCache { } } file.ts = this.typescript.createSourceFile(file.fileNameOriginal, file.content, this.options.target); + (file.ts as any).version = this.versionString; } getFile(name: string) { diff --git a/release/compiler.d.ts b/release/compiler.d.ts index 266f7216..1f9b5615 100644 --- a/release/compiler.d.ts +++ b/release/compiler.d.ts @@ -15,7 +15,7 @@ export declare class ProjectCompiler implements ICompiler { finalTransformers: FinalTransformers; host: Host; project: ProjectInfo; - program: ts.Program; + program: ts.BuilderProgram; private hasSourceMap; prepare(project: ProjectInfo, finalTransformers?: FinalTransformers): void; inputFile(file: File): void; diff --git a/release/compiler.js b/release/compiler.js index 5b3f1c4c..7f11f72d 100644 --- a/release/compiler.js +++ b/release/compiler.js @@ -33,18 +33,16 @@ class ProjectCompiler { } this.project.options.sourceMap = this.hasSourceMap; const currentDirectory = utils.getCommonBasePathOfArray(rootFilenames.map(fileName => this.project.input.getFile(fileName).gulp.cwd)); - this.host = new host_1.Host(this.project.typescript, currentDirectory, this.project.input, this.project.options); - // Calling `createProgram` with an object is only supported in 3.0. Only call this overload - // if we have project references (also only supported in 3.0) - this.program = this.project.projectReferences - ? this.project.typescript.createProgram({ + if (this.program === undefined) { + this.host = new host_1.Host(this.project.typescript, currentDirectory, this.project.input, this.project.options); + this.program = this.project.typescript.createIncrementalProgram({ rootNames: rootFilenames, options: this.project.options, projectReferences: this.project.projectReferences, host: this.host, - oldProgram: this.program - }) - : this.project.typescript.createProgram(rootFilenames, this.project.options, this.host, this.program); + createProgram: this.project.typescript.createAbstractBuilder + }); + } const result = reporter_1.emptyCompilationResult(this.project.options.noEmit); const optionErrors = this.program.getOptionsDiagnostics(); const syntaxErrors = this.program.getSyntacticDiagnostics(); @@ -73,22 +71,24 @@ class ProjectCompiler { const output = {}; const input = this.host.input.getFileNames(true); for (let i = 0; i < input.length; i++) { - const fileName = utils.normalizePath(input[i]); + const fileName = this.host.getCanonicalFileName(input[i]); const file = this.project.input.getFile(fileName); output[fileName] = { file }; } this.emit(result, preEmitDiagnostics, (fileName, content, writeByteOrderMark, onError, sourceFiles) => { + if (sourceFiles === undefined) + return; // .tsbuildinfo file, ignore if (sourceFiles.length !== 1) { throw new Error("Failure: sourceFiles in WriteFileCallback should have length 1, got " + sourceFiles.length); } - const fileNameOriginal = utils.normalizePath(sourceFiles[0].fileName); + const fileNameOriginal = this.host.getCanonicalFileName(sourceFiles[0].fileName); const file = output[fileNameOriginal]; if (!file) return; this.attachContentToFile(file, fileName, content); }); for (let i = 0; i < input.length; i++) { - const fileName = utils.normalizePath(input[i]); + const fileName = this.host.getCanonicalFileName(input[i]); this.emitFile(output[fileName], currentDirectory); } } @@ -116,7 +116,7 @@ class ProjectCompiler { } } emit(result, preEmitDiagnostics, callback) { - const emitOutput = this.program.emit(undefined, callback, undefined, false, this.finalTransformers ? this.finalTransformers(this.program) : undefined); + const emitOutput = this.program.emit(undefined, callback, undefined, false, this.finalTransformers ? this.finalTransformers(this.program.getProgram()) : undefined); result.emitSkipped = emitOutput.emitSkipped; // `emitOutput.diagnostics` might contain diagnostics that were already part of `preEmitDiagnostics`. // See https://github.com/Microsoft/TypeScript/issues/20876 @@ -130,8 +130,6 @@ class ProjectCompiler { } } emitFile({ file, jsFileName, dtsFileName, dtsMapFileName, jsContent, dtsContent, dtsMapContent, jsMapContent }, currentDirectory) { - if (!jsFileName) - return; let base; let baseDeclarations; if (file) { @@ -153,7 +151,9 @@ class ProjectCompiler { else { base = this.project.directory; baseDeclarations = base; - jsFileName = path.resolve(base, jsFileName); + if (jsFileName !== undefined) { + jsFileName = path.resolve(base, jsFileName); + } if (dtsFileName !== undefined) { dtsFileName = path.resolve(base, dtsFileName); } diff --git a/release/host.d.ts b/release/host.d.ts index 85ccf367..51b78334 100644 --- a/release/host.d.ts +++ b/release/host.d.ts @@ -20,4 +20,5 @@ export declare class Host implements ts.CompilerHost { getDirectories: (path: string) => string[]; directoryExists: (path: string) => boolean; readDirectory: (rootDir: string, extensions: string[], excludes: string[], includes: string[], depth?: number) => string[]; + createHash: (data: string) => string; } diff --git a/release/host.js b/release/host.js index e2d05870..96187121 100644 --- a/release/host.js +++ b/release/host.js @@ -24,12 +24,17 @@ class Host { let sourceFile = this.input.getFile(fileName); if (sourceFile) return sourceFile.ts; - return this.fallback.getSourceFile(fileName, languageVersion, onError); + const file = this.fallback.getSourceFile(fileName, languageVersion, onError); + if (file === undefined) + return undefined; + file.version = this.input.versionString; + return file; }; this.realpath = (path) => this.fallback.realpath(path); this.getDirectories = (path) => this.fallback.getDirectories(path); this.directoryExists = (path) => this.fallback.directoryExists(path); this.readDirectory = (rootDir, extensions, excludes, includes, depth) => this.fallback.readDirectory(rootDir, extensions, excludes, includes, depth); + this.createHash = (data) => this.fallback.createHash(data); this.typescript = typescript; this.fallback = typescript.createCompilerHost(options); this.currentDirectory = currentDirectory; @@ -39,10 +44,10 @@ class Host { return '\n'; } useCaseSensitiveFileNames() { - return false; + return this.fallback.useCaseSensitiveFileNames(); } getCanonicalFileName(filename) { - return utils.normalizePath(filename); + return utils.normalizePath(this.useCaseSensitiveFileNames(), filename); } getDefaultLibFileName(options) { return this.fallback.getDefaultLibFileName(options); diff --git a/release/input.d.ts b/release/input.d.ts index 805f34c2..2196efde 100644 --- a/release/input.d.ts +++ b/release/input.d.ts @@ -26,16 +26,17 @@ export interface File { ts?: ts.SourceFile; } export declare module File { - function fromContent(fileName: string, content: string): File; - function fromGulp(file: VinylFile): File; + function fromContent(caseSensitive: boolean, fileName: string, content: string): File; + function fromGulp(caseSensitive: boolean, file: VinylFile): File; function equal(a: File, b: File): boolean; function getChangeState(previous: File, current: File): FileChangeState; } export declare class FileDictionary { files: utils.Map; firstSourceFile: File; + caseSensitive: boolean; typescript: typeof ts; - constructor(typescript: typeof ts); + constructor(caseSensitive: boolean, typescript: typeof ts); addGulp(gFile: VinylFile): File; addContent(fileName: string, content: string): File; private addFile; @@ -50,10 +51,12 @@ export declare class FileCache { previous: FileDictionary; current: FileDictionary; options: ts.CompilerOptions; + caseSensitive: boolean; noParse: boolean; typescript: typeof ts; version: number; - constructor(typescript: typeof ts, options: ts.CompilerOptions); + versionString: string; + constructor(typescript: typeof ts, options: ts.CompilerOptions, caseSensitive: boolean); addGulp(gFile: VinylFile): File; addContent(fileName: string, content: string): File; reset(): void; diff --git a/release/input.js b/release/input.js index 09abebec..c8c6ca90 100644 --- a/release/input.js +++ b/release/input.js @@ -17,21 +17,21 @@ var FileKind; })(FileKind = exports.FileKind || (exports.FileKind = {})); var File; (function (File) { - function fromContent(fileName, content) { + function fromContent(caseSensitive, fileName, content) { let kind = FileKind.Source; if (path.extname(fileName).toLowerCase() === 'json') kind = FileKind.Config; return { - fileNameNormalized: utils.normalizePath(fileName), + fileNameNormalized: utils.normalizePath(caseSensitive, fileName), fileNameOriginal: fileName, content, kind }; } File.fromContent = fromContent; - function fromGulp(file) { + function fromGulp(caseSensitive, file) { let str = file.contents.toString('utf8'); - let data = fromContent(file.path, str); + let data = fromContent(caseSensitive, file.path, str); data.gulp = file; return data; } @@ -58,16 +58,17 @@ var File; File.getChangeState = getChangeState; })(File = exports.File || (exports.File = {})); class FileDictionary { - constructor(typescript) { + constructor(caseSensitive, typescript) { this.files = {}; this.firstSourceFile = undefined; + this.caseSensitive = caseSensitive; this.typescript = typescript; } addGulp(gFile) { - return this.addFile(File.fromGulp(gFile)); + return this.addFile(File.fromGulp(this.caseSensitive, gFile)); } addContent(fileName, content) { - return this.addFile(File.fromContent(fileName, content)); + return this.addFile(File.fromContent(this.caseSensitive, fileName, content)); } addFile(file) { if (file.kind === FileKind.Source) { @@ -79,7 +80,7 @@ class FileDictionary { return file; } getFile(name) { - return this.files[utils.normalizePath(name)]; + return this.files[utils.normalizePath(this.caseSensitive, name)]; } getFileNames(onlyGulp = false) { const fileNames = []; @@ -107,7 +108,7 @@ class FileDictionary { get commonBasePath() { const fileNames = this.getSourceFileNames(true); return utils.getCommonBasePathOfArray(fileNames.map(fileName => { - const file = this.files[utils.normalizePath(fileName)]; + const file = this.files[utils.normalizePath(this.caseSensitive, fileName)]; return path.resolve(process.cwd(), file.gulp.base); })); } @@ -117,7 +118,7 @@ class FileDictionary { get commonSourceDirectory() { const fileNames = this.getSourceFileNames(); return utils.getCommonBasePathOfArray(fileNames.map(fileName => { - const file = this.files[utils.normalizePath(fileName)]; + const file = this.files[utils.normalizePath(this.caseSensitive, fileName)]; return path.dirname(file.fileNameNormalized); })); } @@ -127,12 +128,14 @@ class FileDictionary { } exports.FileDictionary = FileDictionary; class FileCache { - constructor(typescript, options) { + constructor(typescript, options, caseSensitive) { this.previous = undefined; this.noParse = false; // true when using a file based compiler. this.version = 0; + this.versionString = "0"; this.typescript = typescript; this.options = options; + this.caseSensitive = caseSensitive; this.createDictionary(); } addGulp(gFile) { @@ -143,11 +146,12 @@ class FileCache { } reset() { this.version++; + this.versionString = this.version.toString(); this.previous = this.current; this.createDictionary(); } createDictionary() { - this.current = new FileDictionary(this.typescript); + this.current = new FileDictionary(this.caseSensitive, this.typescript); this.current.initTypeScriptSourceFile = (file) => this.initTypeScriptSourceFile(file); } initTypeScriptSourceFile(file) { @@ -161,6 +165,7 @@ class FileCache { } } file.ts = this.typescript.createSourceFile(file.fileNameOriginal, file.content, this.options.target); + file.ts.version = this.versionString; } getFile(name) { return this.current.getFile(name); diff --git a/release/main.js b/release/main.js index 38224361..da1575f8 100644 --- a/release/main.js +++ b/release/main.js @@ -4,8 +4,10 @@ var __rest = (this && this.__rest) || function (s, e) { for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) - t[p[i]] = s[p[i]]; + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } return t; }; const path = require("path"); diff --git a/release/output.js b/release/output.js index 3b9a0075..13daff59 100644 --- a/release/output.js +++ b/release/output.js @@ -1,9 +1,10 @@ "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; diff --git a/release/project.js b/release/project.js index 3d9b855c..9663d11c 100644 --- a/release/project.js +++ b/release/project.js @@ -4,8 +4,10 @@ var __rest = (this && this.__rest) || function (s, e) { for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) - t[p[i]] = s[p[i]]; + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); @@ -19,7 +21,8 @@ const input_1 = require("./input"); const output_1 = require("./output"); const compiler_1 = require("./compiler"); function setupProject(projectDirectory, configFileName, rawConfig, config, options, projectReferences, typescript, finalTransformers) { - const input = new input_1.FileCache(typescript, options); + const caseSensitive = typescript.createCompilerHost(options).useCaseSensitiveFileNames(); + const input = new input_1.FileCache(typescript, options, caseSensitive); const compiler = options.isolatedModules ? new compiler_1.FileCompiler() : new compiler_1.ProjectCompiler(); let running = false; if (options.isolatedModules) { diff --git a/release/utils.d.ts b/release/utils.d.ts index 263b102d..d9382d0a 100644 --- a/release/utils.d.ts +++ b/release/utils.d.ts @@ -5,7 +5,7 @@ export interface Map { [key: string]: T; } export declare function forwardSlashes(fileName: string): string; -export declare function normalizePath(pathString: string): string; +export declare function normalizePath(caseSensitive: boolean, pathString: string): string; /** * Splits a filename into an extensionless filename and an extension. * 'bar/foo.js' is turned into ['bar/foo', 'js'] diff --git a/release/utils.js b/release/utils.js index 488e76bf..38ce6c78 100644 --- a/release/utils.js +++ b/release/utils.js @@ -6,8 +6,11 @@ function forwardSlashes(fileName) { return fileName.replace(/\\/g, '/'); } exports.forwardSlashes = forwardSlashes; -function normalizePath(pathString) { - return path.normalize(pathString).toLowerCase(); +function normalizePath(caseSensitive, pathString) { + const normalized = path.normalize(pathString); + if (!caseSensitive) + return normalized.toLowerCase(); + return normalized; } exports.normalizePath = normalizePath; /** diff --git a/test/baselines/emitDeclarationOnly/2.9/dts/a.d.ts b/test/baselines/emitDeclarationOnly/3.6/dts/a.d.ts similarity index 62% rename from test/baselines/emitDeclarationOnly/2.9/dts/a.d.ts rename to test/baselines/emitDeclarationOnly/3.6/dts/a.d.ts index 17681864..b3a0b19d 100644 --- a/test/baselines/emitDeclarationOnly/2.9/dts/a.d.ts +++ b/test/baselines/emitDeclarationOnly/3.6/dts/a.d.ts @@ -1,3 +1,3 @@ export declare class Hello { - value: string; + value: string; } diff --git a/test/baselines/emitDeclarationOnly/2.9/errors.txt b/test/baselines/emitDeclarationOnly/3.6/errors.txt similarity index 100% rename from test/baselines/emitDeclarationOnly/2.9/errors.txt rename to test/baselines/emitDeclarationOnly/3.6/errors.txt