Skip to content

Commit

Permalink
switch enums in protocol to unions of literal types
Browse files Browse the repository at this point in the history
  • Loading branch information
vladima committed Oct 15, 2016
1 parent 1635679 commit 135c6b5
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 121 deletions.
68 changes: 51 additions & 17 deletions scripts/buildProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ function endsWith(s: string, suffix: string) {
class DeclarationsWalker {
private visitedTypes: ts.Type[] = [];
private text = "";
private removedTypes: ts.Type[] = [];

private constructor(private typeChecker: ts.TypeChecker, private protocolFile: ts.SourceFile) {
}

static getExtraDeclarations(typeChecker: ts.TypeChecker, protocolFile: ts.SourceFile): string {
let text = "declare namespace ts.server.protocol {\n";
var walker = new DeclarationsWalker(typeChecker, protocolFile);
walker.visitTypeNodes(protocolFile);
return walker.text
text = walker.text
? `declare namespace ts.server.protocol {\n${walker.text}}`
: "";
if (walker.removedTypes) {
text += "\ndeclare namespace ts {\n";
text += " // these types are empty stubs for types from services and should not be used directly\n"
for (const type of walker.removedTypes) {
text += ` export type ${type.symbol.name} = never;\n`;
}
text += "}"
}
return text;
}

private processType(type: ts.Type): void {
Expand All @@ -41,19 +52,18 @@ class DeclarationsWalker {
if (sourceFile === this.protocolFile || path.basename(sourceFile.fileName) === "lib.d.ts") {
return;
}
// splice declaration in final d.ts file
let text = decl.getFullText();
if (decl.kind === ts.SyntaxKind.EnumDeclaration && !(decl.flags & ts.NodeFlags.Const)) {
// patch enum declaration to make them constan
const declStart = decl.getStart() - decl.getFullStart();
const prefix = text.substring(0, declStart);
const suffix = text.substring(declStart + "enum".length, decl.getEnd() - decl.getFullStart());
text = prefix + "const enum" + suffix;
if (decl.kind === ts.SyntaxKind.EnumDeclaration) {
this.removedTypes.push(type);
return;
}
this.text += `${text}\n`;
else {
// splice declaration in final d.ts file
let text = decl.getFullText();
this.text += `${text}\n`;
// recursively pull all dependencies into result dts file

// recursively pull all dependencies into result dts file
this.visitTypeNodes(decl);
this.visitTypeNodes(decl);
}
}
}
}
Expand All @@ -69,15 +79,37 @@ class DeclarationsWalker {
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.IndexSignature:
if (((<ts.VariableDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature | ts.IndexSignatureDeclaration>node.parent).type) === node) {
const type = this.typeChecker.getTypeAtLocation(node);
if (type && !(type.flags & ts.TypeFlags.TypeParameter)) {
this.processType(type);
}
this.processTypeOfNode(node);
}
break;
case ts.SyntaxKind.InterfaceDeclaration:
const heritageClauses = (<ts.InterfaceDeclaration>node.parent).heritageClauses;
if (heritageClauses) {
if (heritageClauses[0].token !== ts.SyntaxKind.ExtendsKeyword) {
throw new Error(`Unexpected kind of heritage clause: ${ts.SyntaxKind[heritageClauses[0].kind]}`);
}
for (const type of heritageClauses[0].types) {
this.processTypeOfNode(type);
}
}
break;
}
}
ts.forEachChild(node, n => this.visitTypeNodes(n));
}

private processTypeOfNode(node: ts.Node): void {
if (node.kind === ts.SyntaxKind.UnionType) {
for (const t of (<ts.UnionTypeNode>node).types) {
this.processTypeOfNode(t);
}
}
else {
const type = this.typeChecker.getTypeAtLocation(node);
if (type && !(type.flags & (ts.TypeFlags.TypeParameter))) {
this.processType(type);
}
}
}
}

Expand Down Expand Up @@ -128,9 +160,11 @@ function generateProtocolFile(protocolTs: string, typeScriptServicesDts: string)
if (extraDeclarations) {
protocolDts += extraDeclarations;
}
protocolDts += "\nimport protocol = ts.server.protocol;";
protocolDts += "\nexport = protocol;";
// do sanity check and try to compile generated text as standalone program
const sanityCheckProgram = getProgramWithProtocolText(protocolDts, /*includeTypeScriptServices*/ false);
const diagnostics = [...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics(), ...program.getGlobalDiagnostics()];
const diagnostics = [...sanityCheckProgram.getSyntacticDiagnostics(), ...sanityCheckProgram.getSemanticDiagnostics(), ...sanityCheckProgram.getGlobalDiagnostics()];
if (diagnostics.length) {
const flattenedDiagnostics = diagnostics.map(d => ts.flattenDiagnosticMessageText(d.messageText, "\n")).join("\n");
throw new Error(`Unexpected errors during sanity check: ${flattenedDiagnostics}`);
Expand Down
121 changes: 68 additions & 53 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,69 @@
namespace ts {
/* @internal */
export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" };
/* @internal */
export const jsxCompilerOption: CommandLineOptionOfCustomType = {
name: "jsx",
type: createMap({
"preserve": JsxEmit.Preserve,
"react": JsxEmit.React
}),
paramType: Diagnostics.KIND,
description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react,
};

/* @internal */
export const moduleCommandLineOption: CommandLineOptionOfCustomType = {
name: "module",
shortName: "m",
type: createMap({
"none": ModuleKind.None,
"commonjs": ModuleKind.CommonJS,
"amd": ModuleKind.AMD,
"system": ModuleKind.System,
"umd": ModuleKind.UMD,
"es6": ModuleKind.ES6,
"es2015": ModuleKind.ES2015,
}),
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015,
paramType: Diagnostics.KIND,
};

/* @internal */
export const newLineCommandLineOption: CommandLineOptionOfCustomType = {
name: "newLine",
type: createMap({
"crlf": NewLineKind.CarriageReturnLineFeed,
"lf": NewLineKind.LineFeed
}),
description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix,
paramType: Diagnostics.NEWLINE,
};

/* @internal */
export const moduleResolutionCommandLineOption: CommandLineOptionOfCustomType = {
name: "moduleResolution",
type: createMap({
"node": ModuleResolutionKind.NodeJs,
"classic": ModuleResolutionKind.Classic,
}),
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
};

/* @internal */
export const targetCommandLineOption: CommandLineOptionOfCustomType = {
name: "target",
shortName: "t",
type: createMap({
"es3": ScriptTarget.ES3,
"es5": ScriptTarget.ES5,
"es6": ScriptTarget.ES6,
"es2015": ScriptTarget.ES2015,
}),
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015,
paramType: Diagnostics.VERSION,
};

/* @internal */
export const optionDeclarations: CommandLineOption[] = [
{
Expand Down Expand Up @@ -62,15 +125,7 @@ namespace ts {
name: "inlineSources",
type: "boolean",
},
{
name: "jsx",
type: createMap({
"preserve": JsxEmit.Preserve,
"react": JsxEmit.React
}),
paramType: Diagnostics.KIND,
description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react,
},
jsxCompilerOption,
{
name: "reactNamespace",
type: "string",
Expand All @@ -91,30 +146,8 @@ namespace ts {
description: Diagnostics.Specify_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations,
paramType: Diagnostics.LOCATION,
},
{
name: "module",
shortName: "m",
type: createMap({
"none": ModuleKind.None,
"commonjs": ModuleKind.CommonJS,
"amd": ModuleKind.AMD,
"system": ModuleKind.System,
"umd": ModuleKind.UMD,
"es6": ModuleKind.ES6,
"es2015": ModuleKind.ES2015,
}),
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015,
paramType: Diagnostics.KIND,
},
{
name: "newLine",
type: createMap({
"crlf": NewLineKind.CarriageReturnLineFeed,
"lf": NewLineKind.LineFeed
}),
description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix,
paramType: Diagnostics.NEWLINE,
},
moduleCommandLineOption,
newLineCommandLineOption,
{
name: "noEmit",
type: "boolean",
Expand Down Expand Up @@ -254,18 +287,7 @@ namespace ts {
description: Diagnostics.Do_not_emit_declarations_for_code_that_has_an_internal_annotation,
experimental: true
},
{
name: "target",
shortName: "t",
type: createMap({
"es3": ScriptTarget.ES3,
"es5": ScriptTarget.ES5,
"es6": ScriptTarget.ES6,
"es2015": ScriptTarget.ES2015,
}),
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015,
paramType: Diagnostics.VERSION,
},
targetCommandLineOption,
{
name: "version",
shortName: "v",
Expand All @@ -289,14 +311,7 @@ namespace ts {
experimental: true,
description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators
},
{
name: "moduleResolution",
type: createMap({
"node": ModuleResolutionKind.NodeJs,
"classic": ModuleResolutionKind.Classic,
}),
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
},
moduleResolutionCommandLineOption,
{
name: "allowUnusedLabels",
type: "boolean",
Expand Down
Loading

0 comments on commit 135c6b5

Please sign in to comment.