Skip to content

Salsa: JS support for discovering and acquiring d.ts files #7179

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

Merged
merged 16 commits into from
Mar 2, 2016
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
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
1 change: 1 addition & 0 deletions Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,7 @@ var servicesLintTargets = [
"patternMatcher.ts",
"services.ts",
"shims.ts",
"jsTyping.ts"
].map(function (s) {
return path.join(servicesDirectory, s);
});
Expand Down
77 changes: 55 additions & 22 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ namespace ts {
return {
options,
fileNames: getFileNames(),
typingOptions: getTypingOptions(),
errors
};

Expand Down Expand Up @@ -601,6 +602,35 @@ namespace ts {
}
return fileNames;
}

function getTypingOptions(): TypingOptions {
const options: TypingOptions = getBaseFileName(configFileName) === "jsconfig.json"
? { enableAutoDiscovery: true, include: [], exclude: [] }
: { enableAutoDiscovery: false, include: [], exclude: [] };
const jsonTypingOptions = json["typingOptions"];
if (jsonTypingOptions) {
for (const id in jsonTypingOptions) {
if (id === "enableAutoDiscovery") {
if (typeof jsonTypingOptions[id] === "boolean") {
Copy link
Member

Choose a reason for hiding this comment

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

else push an error?

Copy link
Member Author

Choose a reason for hiding this comment

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

sounds good - we're doing it if the other options are invalid so makes sense here as well.

options.enableAutoDiscovery = jsonTypingOptions[id];
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here id is "enableAutoDiscovery" which is known pretty well, so the generated error is incorrect: Unknown typing option 'enableAutoDiscovery'

Instead it should complain about enableAutoDiscovery not being boolean

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point.

}
}
else if (id === "include") {
options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors);
}
else if (id === "exclude") {
options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id));
}
}
}
return options;
}
}

export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
Expand Down Expand Up @@ -641,28 +671,7 @@ namespace ts {
break;
case "object":
// "object" options with 'isFilePath' = true expected to be string arrays
let paths: string[] = [];
let invalidOptionType = false;
if (!isArray(value)) {
invalidOptionType = true;
}
else {
for (const element of <any[]>value) {
if (typeof element === "string") {
paths.push(normalizePath(combinePaths(basePath, element)));
}
else {
invalidOptionType = true;
break;
}
}
}
if (invalidOptionType) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_should_have_array_of_strings_as_a_value, opt.name));
}
else {
value = paths;
}
value = convertJsonOptionToStringArray(opt.name, value, errors, (element) => normalizePath(combinePaths(basePath, element)));
break;
}
if (value === "") {
Expand All @@ -682,4 +691,28 @@ namespace ts {

return { options, errors };
}

function convertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] {
const items: string[] = [];
let invalidOptionType = false;
if (!isArray(optionJson)) {
invalidOptionType = true;
}
else {
for (const element of <any[]>optionJson) {
if (typeof element === "string") {
const item = func ? func(element) : element;
items.push(item);
}
else {
invalidOptionType = true;
break;
}
}
}
if (invalidOptionType) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName));
}
return items;
}
}
34 changes: 34 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,14 @@ namespace ts {
return hasOwnProperty.call(map, key);
}

export function getKeys<T>(map: Map<T>): string[] {
const keys: string[] = [];
for (const key in map) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why no hasOwnProperty check here?

Copy link
Contributor

Choose a reason for hiding this comment

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

It should have. Thanks for pointing out.

keys.push(key);
}
return keys;
}

export function getProperty<T>(map: Map<T>, key: string): T {
return hasOwnProperty.call(map, key) ? map[key] : undefined;
}
Expand Down Expand Up @@ -778,6 +786,32 @@ namespace ts {
return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension;
}

export function ensureScriptKind(fileName: string, scriptKind?: ScriptKind): ScriptKind {
// Using scriptKind as a condition handles both:
// - 'scriptKind' is unspecified and thus it is `undefined`
// - 'scriptKind' is set and it is `Unknown` (0)
// If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt
// to get the ScriptKind from the file name. If it cannot be resolved
// from the file name then the default 'TS' script kind is returned.
return (scriptKind || getScriptKindFromFileName(fileName)) || ScriptKind.TS;
}

export function getScriptKindFromFileName(fileName: string): ScriptKind {
const ext = fileName.substr(fileName.lastIndexOf("."));
switch (ext.toLowerCase()) {
case ".js":
return ScriptKind.JS;
case ".jsx":
return ScriptKind.JSX;
case ".ts":
return ScriptKind.TS;
case ".tsx":
return ScriptKind.TSX;
default:
return ScriptKind.Unknown;
}
}

/**
* List of supported extensions in order of file resolution precedence.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2804,5 +2804,9 @@
"'super' must be called before accessing 'this' in the constructor of a derived class.": {
"category": "Error",
"code": 17009
},
"Unknown typing option '{0}'.": {
"category": "Error",
"code": 17010
}
}
24 changes: 1 addition & 23 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,23 +407,6 @@ namespace ts {
return result;
}

/* @internal */
export function getScriptKindFromFileName(fileName: string): ScriptKind {
const ext = fileName.substr(fileName.lastIndexOf("."));
switch (ext.toLowerCase()) {
case ".js":
return ScriptKind.JS;
case ".jsx":
return ScriptKind.JSX;
case ".ts":
return ScriptKind.TS;
case ".tsx":
return ScriptKind.TSX;
default:
return ScriptKind.TS;
}
}

// Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
// indicates what changed between the 'text' that this SourceFile has and the 'newText'.
// The SourceFile will be created with the compiler attempting to reuse as many nodes from
Expand Down Expand Up @@ -551,12 +534,7 @@ namespace ts {
let parseErrorBeforeNextFinishedNode = false;

export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile {
// Using scriptKind as a condition handles both:
// - 'scriptKind' is unspecified and thus it is `undefined`
// - 'scriptKind' is set and it is `Unknown` (0)
// If the 'scriptKind' is 'undefined' or 'Unknown' then attempt
// to get the ScriptKind from the file name.
scriptKind = scriptKind ? scriptKind : getScriptKindFromFileName(fileName);
scriptKind = ensureScriptKind(fileName, scriptKind);

initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind);

Expand Down
18 changes: 18 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,23 @@ namespace ts {
[option: string]: string | number | boolean | TsConfigOnlyOptions;
}

export interface TypingOptions {
enableAutoDiscovery?: boolean;
include?: string[];
exclude?: string[];
[option: string]: string[] | boolean;
}

export interface DiscoverTypingsSettings {
fileNames: string[]; // The file names that belong to the same project.
cachePath: string; // The path to the typings cache
projectRootPath: string; // The path to the project root directory
safeListPath: string; // The path used to retrieve the safe list
packageNameToTypingLocation: Map<string>; // The map of package names to their cached typing locations
typingOptions: TypingOptions; // Used to customize the typing inference process
compilerOptions: CompilerOptions; // Used as a source for typing inference
}

export enum ModuleKind {
None = 0,
CommonJS = 1,
Expand Down Expand Up @@ -2490,6 +2507,7 @@ namespace ts {

export interface ParsedCommandLine {
options: CompilerOptions;
typingOptions?: TypingOptions;
fileNames: string[];
errors: Diagnostic[];
}
Expand Down
Loading