Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APIs for enabling CompileOnSave on tsserver #9837

Merged
merged 28 commits into from
Aug 23, 2016
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
305a48f
Add API to get only the emited declarations output
Jul 17, 2016
415662c
Add nonModuleBuilder
Jul 17, 2016
b0144b0
WIP
Jul 18, 2016
d27c4b2
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
Jul 18, 2016
6fae29e
WIP
Jul 19, 2016
4c962e0
WIP
Jul 19, 2016
c2b0023
Merge branch 'compileOnSave0' of https://github.com/zhengbli/TypeScri…
Jul 19, 2016
acfa893
Add basic tests for CompileOnSaveAffectedFileList API
Jul 20, 2016
9bcb9b7
Add API for compile single file
Jul 20, 2016
fb57e08
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
Jul 20, 2016
d1f6a6f
refactor
Jul 20, 2016
4270ee1
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
Jul 22, 2016
9f4833a
Avoid invoking project.languageService directly
Jul 25, 2016
2d66aad
Add API to query if compileOnSave is enabled for a project
zhengbli Jul 27, 2016
b1a1658
Seperate check and emit signatures
Jul 29, 2016
4e05b34
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
Jul 29, 2016
a661bad
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
zhengbli Aug 5, 2016
3367f79
Refactor and address cr
Aug 8, 2016
6f0b332
Add more tests
Aug 10, 2016
dc359e7
Refactor
zhengbli Aug 14, 2016
46e0dee
Always return cascaded affected list
zhengbli Aug 14, 2016
7d1517b
Correct the tsconfig file in compileOnSave tests
zhengbli Aug 16, 2016
2f49d16
reduce string to path conversion
Aug 16, 2016
2a021bd
refactor
zhengbli Aug 22, 2016
54f5c72
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
zhengbli Aug 22, 2016
600b05d
refactor again
zhengbli Aug 23, 2016
f1880d4
Merge Microsoft/tsserverVS-WIP
zhengbli Aug 23, 2016
f4a0a34
Merge branch 'tsserverVS-WIP' of https://github.com/Microsoft/TypeScr…
zhengbli Aug 23, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
/// <reference path="scanner.ts"/>

namespace ts {
/* @internal */
export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" };
/* @internal */
export const optionDeclarations: CommandLineOption[] = [
{
name: "charset",
type: "string",
},
compileOnSaveCommandLineOption,
{
name: "declaration",
shortName: "d",
Expand Down Expand Up @@ -709,14 +712,16 @@ namespace ts {
options.configFilePath = configFileName;

const { fileNames, wildcardDirectories } = getFileNames(errors);
const compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);

return {
options,
fileNames,
typingOptions,
raw: json,
errors,
wildcardDirectories
wildcardDirectories,
compileOnSave
};

function getFileNames(errors: Diagnostic[]): ExpandResult {
Expand Down Expand Up @@ -771,6 +776,17 @@ namespace ts {
}
}

export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
if (!hasProperty(jsonOption, "compileOnSave")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasProperty(jsonOption, compileOnSaveCommandLineOption.name)

return false;
}
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption["compileOnSave"], basePath, errors);
if (typeof result == "boolean") {
Copy link
Contributor

@vladima vladima Aug 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typeof result === "boolean" && result;

return result;
}
return false;
}

export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
const errors: Diagnostic[] = [];
const options = convertCompilerOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
Expand Down
26 changes: 21 additions & 5 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference path="types.ts"/>
/// <reference path="types.ts"/>
/// <reference path="performance.ts" />


Expand Down Expand Up @@ -27,6 +27,7 @@ namespace ts {
contains,
remove,
forEachValue: forEachValueInMap,
getKeys,
clear,
};

Expand All @@ -36,6 +37,14 @@ namespace ts {
}
}

function getKeys() {
const keys: Path[] = [];
for (const key in files) {
keys.push(<Path>key);
}
return keys;
}

// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
Expand Down Expand Up @@ -239,18 +248,25 @@ namespace ts {
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch(array: number[], value: number): number {
export function binarySearch<T>(array: T[], value: T, comparer?: (v1: T, v2: T) => number): number {
if (!array || array.length === 0) {
return -1;
}

let low = 0;
let high = array.length - 1;
comparer = comparer !== undefined
? comparer
: (v1, v2) => (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0));

while (low <= high) {
const middle = low + ((high - low) >> 1);
const midValue = array[middle];

if (midValue === value) {
if (comparer(midValue, value) === 0) {
return middle;
}
else if (midValue > value) {
else if (comparer(midValue, value) > 0) {
high = middle - 1;
}
else {
Expand Down Expand Up @@ -942,7 +958,7 @@ namespace ts {
* [^./] # matches everything up to the first . character (excluding directory seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentOther = "[^/]*";

export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") {
Expand Down
60 changes: 32 additions & 28 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference path="checker.ts"/>
/// <reference path="checker.ts"/>
/// <reference path="sourcemap.ts" />
/// <reference path="declarationEmitter.ts"/>

Expand Down Expand Up @@ -336,7 +336,7 @@ namespace ts {
}

// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult {
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult {
// emit output for the __extends helper function
const extendsHelper = `
var __extends = (this && this.__extends) || function (d, b) {
Expand Down Expand Up @@ -396,7 +396,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
const newLine = host.getNewLine();

const emitJavaScript = createFileEmitter();
forEachExpectedEmitFile(host, emitFile, targetSourceFile);
forEachExpectedEmitFile(host, emitFile, targetSourceFile, emitOnlyDtsFiles);

return {
emitSkipped,
Expand Down Expand Up @@ -1615,7 +1615,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
else if (declaration.kind === SyntaxKind.ImportSpecifier) {
// Identifier references named import
write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent));
const name = (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name;
const name = (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name;
const identifier = getTextOfNodeFromSourceText(currentText, name);
if (languageVersion === ScriptTarget.ES3 && identifier === "default") {
write('["default"]');
Expand Down Expand Up @@ -3253,19 +3253,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
write("var ");
let seen: Map<string>;
for (const id of convertedLoopState.hoistedLocalVariables) {
// Don't initialize seen unless we have at least one element.
// Emit a comma to separate for all but the first element.
if (!seen) {
seen = {};
}
else {
write(", ");
}

if (!hasProperty(seen, id.text)) {
emit(id);
seen[id.text] = id.text;
}
// Don't initialize seen unless we have at least one element.
// Emit a comma to separate for all but the first element.
if (!seen) {
seen = {};
}
else {
write(", ");
}

if (!hasProperty(seen, id.text)) {
emit(id);
seen[id.text] = id.text;
}
}
write(";");
writeLine();
Expand Down Expand Up @@ -7409,7 +7409,7 @@ const _super = (function (geti, seti) {
// - import equals declarations that import external modules are not emitted
continue;
}
// fall-though for import declarations that import internal modules
// fall-though for import declarations that import internal modules
default:
writeLine();
emit(statement);
Expand Down Expand Up @@ -8358,24 +8358,28 @@ const _super = (function (geti, seti) {
}
}

function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath}: { jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string },
function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames,
sourceFiles: SourceFile[], isBundledEmit: boolean) {
// Make sure not to write js File and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
emitJavaScript(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
else {
emitSkipped = true;
if (!emitOnlyDtsFiles) {
// Make sure not to write js File and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
emitJavaScript(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
else {
emitSkipped = true;
}
}

if (declarationFilePath) {
emitSkipped = writeDeclarationFile(declarationFilePath, sourceFiles, isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped;
}

if (!emitSkipped && emittedFilesList) {
emittedFilesList.push(jsFilePath);
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
if (!emitOnlyDtsFiles) {
emittedFilesList.push(jsFilePath);
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference path="utilities.ts"/>
/// <reference path="utilities.ts"/>
/// <reference path="scanner.ts"/>

namespace ts {
Expand Down
9 changes: 5 additions & 4 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1415,15 +1415,15 @@ namespace ts {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}

function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken));
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles));
}

function isEmitBlocked(emitFileName: string): boolean {
return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName));
}

function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken): EmitResult {
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
let declarationDiagnostics: Diagnostic[] = [];

if (options.noEmit) {
Expand Down Expand Up @@ -1468,7 +1468,8 @@ namespace ts {
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
sourceFile,
emitOnlyDtsFiles);

performance.measure("Emit", start);
return emitResult;
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace ts {
remove(fileName: Path): void;

forEachValue(f: (key: Path, v: T) => void): void;
getKeys(): Path[];
clear(): void;
}

Expand Down Expand Up @@ -1730,7 +1731,7 @@ namespace ts {
* used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter
* will be invoked when writing the JavaScript and declaration files.
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult;

getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
Expand Down Expand Up @@ -2707,6 +2708,7 @@ namespace ts {
raw?: any;
errors: Diagnostic[];
wildcardDirectories?: Map<WatchDirectoryFlags>;
compileOnSave?: boolean;
}

export const enum WatchDirectoryFlags {
Expand Down
18 changes: 9 additions & 9 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference path="sys.ts" />
/// <reference path="sys.ts" />

/* @internal */
namespace ts {
Expand Down Expand Up @@ -2243,12 +2243,10 @@ namespace ts {
const options = host.getCompilerOptions();
const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified

if (options.declaration) {
const path = outputDir
? getSourceFilePathInNewDir(sourceFile, host, outputDir)
: sourceFile.fileName;
return removeFileExtension(path) + ".d.ts";
}
const path = outputDir
? getSourceFilePathInNewDir(sourceFile, host, outputDir)
: sourceFile.fileName;
return removeFileExtension(path) + ".d.ts";
}

export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
Expand All @@ -2269,7 +2267,8 @@ namespace ts {

export function forEachExpectedEmitFile(host: EmitHost,
action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) => void,
targetSourceFile?: SourceFile) {
targetSourceFile?: SourceFile,
emitOnlyDtsFiles?: boolean) {
const options = host.getCompilerOptions();
// Emit on each source file
if (options.outFile || options.out) {
Expand Down Expand Up @@ -2302,10 +2301,11 @@ namespace ts {
}
}
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension);
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
const emitFileNames: EmitFileNames = {
jsFilePath,
sourceMapFilePath: getSourceMapFilePath(jsFilePath, options),
declarationFilePath: !isSourceFileJavaScript(sourceFile) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined
declarationFilePath
};
action(emitFileNames, [sourceFile], /*isBundledEmit*/false);
}
Expand Down
Loading