Skip to content

Commit d077396

Browse files
committed
Functional extension loading & lint passes
1 parent 420b920 commit d077396

File tree

12 files changed

+804
-26
lines changed

12 files changed

+804
-26
lines changed

Jakefile.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ var harnessSources = harnessCoreSources.concat([
155155
"commandLineParsing.ts",
156156
"convertCompilerOptionsFromJson.ts",
157157
"convertTypingOptionsFromJson.ts",
158-
"tsserverProjectSystem.ts"
158+
"tsserverProjectSystem.ts",
159+
"extensionAPI.ts",
159160
].map(function (f) {
160161
return path.join(unittestsDirectory, f);
161162
})).concat([
@@ -525,7 +526,7 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca
525526

526527
// Node package definition file to be distributed without the package. Created by replacing
527528
// 'ts' namespace with '"typescript"' as a module.
528-
var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"');
529+
var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts {/g, 'declare module "typescript" {\n import * as ts from "typescript";');
529530
fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents);
530531
});
531532

src/compiler/checker.ts

+4
Original file line numberDiff line numberDiff line change
@@ -17258,6 +17258,10 @@ namespace ts {
1725817258
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent, /*includeOptionality*/ true);
1725917259
}
1726017260

17261+
if (node.kind === SyntaxKind.SourceFile) {
17262+
return unknownType;
17263+
}
17264+
1726117265
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
1726217266
const symbol = getSymbolAtLocation(node);
1726317267
const declaredType = symbol && getDeclaredTypeOfSymbol(symbol);

src/compiler/commandLineParser.ts

+6
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ namespace ts {
268268
experimental: true,
269269
description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators
270270
},
271+
{
272+
name: "extensions",
273+
type: "object",
274+
isTSConfigOnly: true,
275+
description: Diagnostics.List_of_compiler_extensions_to_require
276+
},
271277
{
272278
name: "moduleResolution",
273279
type: {

src/compiler/core.ts

+20
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,26 @@ namespace ts {
156156
return array1.concat(array2);
157157
}
158158

159+
export function flatten<T>(array1: T[][]): T[] {
160+
if (!array1 || !array1.length) return <any>array1;
161+
return [].concat(...array1);
162+
}
163+
164+
export function groupBy<T>(array: T[], classifier: (item: T) => string): {[index: string]: T[]};
165+
export function groupBy<T>(array: T[], classifier: (item: T) => number): {[index: number]: T[]};
166+
export function groupBy<T>(array: T[], classifier: (item: T) => (string | number)): {[index: string]: T[], [index: number]: T[]} {
167+
if (!array || !array.length) return undefined;
168+
const ret: {[index: string]: T[], [index: number]: T[]} = {};
169+
for (let i = 0, len = array.length; i < len; i++) {
170+
const result = classifier(array[i]);
171+
if (!ret[result]) {
172+
ret[result] = [];
173+
}
174+
ret[result].push(array[i]);
175+
}
176+
return ret;
177+
}
178+
159179
export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
160180
let result: T[];
161181
if (array) {

src/compiler/diagnosticMessages.json

+14-1
Original file line numberDiff line numberDiff line change
@@ -2648,7 +2648,7 @@
26482648
"category": "Message",
26492649
"code": 6099
26502650
},
2651-
"'package.json' does not have 'types' field.": {
2651+
"'package.json' does not have '{0}' field.": {
26522652
"category": "Message",
26532653
"code": 6100
26542654
},
@@ -2781,6 +2781,19 @@
27812781
"code": 6132
27822782
},
27832783

2784+
"List of compiler extensions to require.": {
2785+
"category": "Message",
2786+
"code": 6150
2787+
},
2788+
"Extension loading failed with error '{0}'.": {
2789+
"category": "Error",
2790+
"code": 6151
2791+
},
2792+
"Extension '{0}' exported member '{1}' has extension kind '{2}', but was type '{3}' when type '{4}' was expected.": {
2793+
"category": "Error",
2794+
"code": 6152
2795+
},
2796+
27842797
"Variable '{0}' implicitly has an '{1}' type.": {
27852798
"category": "Error",
27862799
"code": 7005

src/compiler/program.ts

+202-15
Large diffs are not rendered by default.

src/compiler/sys.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace ts {
3232
getMemoryUsage?(): number;
3333
exit(exitCode?: number): void;
3434
realpath?(path: string): string;
35+
loadExtension?(name: string): any;
3536
}
3637

3738
export interface FileWatcher {
@@ -556,6 +557,9 @@ namespace ts {
556557
},
557558
realpath(path: string): string {
558559
return _fs.realpathSync(path);
560+
},
561+
loadExtension(name) {
562+
return require(name);
559563
}
560564
};
561565
}

src/compiler/tsc.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,12 @@ namespace ts {
115115
}
116116

117117
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
118-
output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
118+
if (diagnostic.category === DiagnosticCategory.Extension) {
119+
output += `${ category } ${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
120+
}
121+
else {
122+
output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
123+
}
119124

120125
sys.write(output);
121126
}
@@ -604,13 +609,16 @@ namespace ts {
604609
// First get and report any syntactic errors.
605610
diagnostics = program.getSyntacticDiagnostics();
606611

612+
// Count extension diagnostics and ignore them for determining continued error reporting
613+
const extensionDiagnostics = filter(diagnostics, d => d.category === DiagnosticCategory.Extension).length;
614+
607615
// If we didn't have any syntactic errors, then also try getting the global and
608616
// semantic errors.
609-
if (diagnostics.length === 0) {
610-
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
617+
if (diagnostics.length === extensionDiagnostics) {
618+
diagnostics = diagnostics.concat(program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()));
611619

612-
if (diagnostics.length === 0) {
613-
diagnostics = program.getSemanticDiagnostics();
620+
if (diagnostics.length === extensionDiagnostics) {
621+
diagnostics = diagnostics.concat(program.getSemanticDiagnostics());
614622
}
615623
}
616624

src/compiler/types.ts

+68-1
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,11 @@ namespace ts {
17281728
*/
17291729
getTypeChecker(): TypeChecker;
17301730

1731+
/**
1732+
* Gets a map of loaded compiler extensions
1733+
*/
1734+
getCompilerExtensions(): ExtensionCollectionMap;
1735+
17311736
/* @internal */ getCommonSourceDirectory(): string;
17321737

17331738
// For testing purposes only. Should not be used by any other consumers (including the
@@ -1804,6 +1809,7 @@ namespace ts {
18041809
getSourceFiles(): SourceFile[];
18051810
getSourceFile(fileName: string): SourceFile;
18061811
getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
1812+
getCompilerExtensions(): ExtensionCollectionMap;
18071813
}
18081814

18091815
export interface TypeChecker {
@@ -2488,13 +2494,14 @@ namespace ts {
24882494
length: number;
24892495
messageText: string | DiagnosticMessageChain;
24902496
category: DiagnosticCategory;
2491-
code: number;
2497+
code: number | string;
24922498
}
24932499

24942500
export enum DiagnosticCategory {
24952501
Warning,
24962502
Error,
24972503
Message,
2504+
Extension,
24982505
}
24992506

25002507
export enum ModuleResolutionKind {
@@ -2578,6 +2585,7 @@ namespace ts {
25782585
typesSearchPaths?: string[];
25792586
/*@internal*/ version?: boolean;
25802587
/*@internal*/ watch?: boolean;
2588+
extensions?: string[] | Map<any>;
25812589

25822590
[option: string]: CompilerOptionsValue | undefined;
25832591
}
@@ -2874,6 +2882,57 @@ namespace ts {
28742882
failedLookupLocations: string[];
28752883
}
28762884

2885+
export type LintErrorMethod = (err: string, span: Node) => void;
2886+
export type LintAcceptMethod = () => void;
2887+
2888+
/*
2889+
* Walkers call accept to decend into the node's children
2890+
* Walkers call error to add errors to the output.
2891+
*/
2892+
export interface LintWalker {
2893+
visit(node: Node, accept: LintAcceptMethod, error: LintErrorMethod): void;
2894+
}
2895+
2896+
export interface SyntacticLintProviderStatic {
2897+
new (typescript: typeof ts, args: any): LintWalker;
2898+
}
2899+
2900+
export interface SemanticLintProviderStatic {
2901+
new (typescript: typeof ts, checker: TypeChecker, args: any): LintWalker;
2902+
}
2903+
2904+
export namespace ExtensionKind {
2905+
export const SemanticLint: "semantic-lint" = "semantic-lint";
2906+
export type SemanticLint = "semantic-lint";
2907+
export const SyntacticLint: "syntactic-lint" = "syntactic-lint";
2908+
export type SyntacticLint = "syntactic-lint";
2909+
}
2910+
export type ExtensionKind = ExtensionKind.SemanticLint | ExtensionKind.SyntacticLint;
2911+
2912+
export interface ExtensionCollectionMap {
2913+
"syntactic-lint"?: SyntacticLintExtension[];
2914+
"semantic-lint"?: SemanticLintExtension[];
2915+
[index: string]: Extension[] | undefined;
2916+
}
2917+
2918+
export interface ExtensionBase {
2919+
name: string;
2920+
args: any;
2921+
kind: ExtensionKind;
2922+
}
2923+
2924+
// @kind(ExtensionKind.SyntacticLint)
2925+
export interface SyntacticLintExtension extends ExtensionBase {
2926+
ctor: SyntacticLintProviderStatic;
2927+
}
2928+
2929+
// @kind(ExtensionKind.SemanticLint)
2930+
export interface SemanticLintExtension extends ExtensionBase {
2931+
ctor: SemanticLintProviderStatic;
2932+
}
2933+
2934+
export type Extension = SyntacticLintExtension | SemanticLintExtension;
2935+
28772936
export interface CompilerHost extends ModuleResolutionHost {
28782937
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
28792938
getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
@@ -2900,6 +2959,14 @@ namespace ts {
29002959
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
29012960
*/
29022961
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
2962+
2963+
/**
2964+
* Delegates the loading of compiler extensions to the compiler host.
2965+
* The function should return the result of executing the code of an extension
2966+
* - its exported members. These members will be searched for objects who have been decorated with
2967+
* specific flags.
2968+
*/
2969+
loadExtension?(extension: string): any;
29032970
}
29042971

29052972
export interface TextSpan {

src/compiler/utilities.ts

+13
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,19 @@ namespace ts {
453453
return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2);
454454
}
455455

456+
export function createExtensionDiagnosticForNode(node: Node, extension: string, message: string): Diagnostic {
457+
const sourceFile = getSourceFileOfNode(node);
458+
const span = getErrorSpanForNode(sourceFile, node);
459+
return {
460+
file: sourceFile,
461+
messageText: message,
462+
code: extension,
463+
start: span.start,
464+
length: span.length,
465+
category: DiagnosticCategory.Extension
466+
};
467+
}
468+
456469
export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain): Diagnostic {
457470
const sourceFile = getSourceFileOfNode(node);
458471
const span = getErrorSpanForNode(sourceFile, node);

src/services/shims.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -517,11 +517,11 @@ namespace ts {
517517
}
518518
}
519519

520-
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; }[] {
520+
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number | string; }[] {
521521
return diagnostics.map(d => realizeDiagnostic(d, newLine));
522522
}
523523

524-
function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; code: number; } {
524+
function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; code: number | string; } {
525525
return {
526526
message: flattenDiagnosticMessageText(diagnostic.messageText, newLine),
527527
start: diagnostic.start,

0 commit comments

Comments
 (0)