mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into untyped_module_symbol
This commit is contained in:
+22
-39
@@ -283,8 +283,8 @@ namespace ts {
|
||||
// Parameters with names are handled at the top of this function. Parameters
|
||||
// without names can only come from JSDocFunctionTypes.
|
||||
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
|
||||
let functionType = <JSDocFunctionType>node.parent;
|
||||
let index = indexOf(functionType.parameters, node);
|
||||
const functionType = <JSDocFunctionType>node.parent;
|
||||
const index = indexOf(functionType.parameters, node);
|
||||
return "arg" + index;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
const parentNode = node.parent && node.parent.parent;
|
||||
@@ -349,17 +349,20 @@ namespace ts {
|
||||
// Otherwise, we'll be merging into a compatible existing symbol (for example when
|
||||
// you have multiple 'vars' with the same name in the same container). In this case
|
||||
// just add this node into the declarations list of the symbol.
|
||||
symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name));
|
||||
symbol = symbolTable.get(name);
|
||||
if (!symbol) {
|
||||
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
|
||||
}
|
||||
|
||||
if (name && (includes & SymbolFlags.Classifiable)) {
|
||||
classifiableNames[name] = name;
|
||||
classifiableNames.set(name, name);
|
||||
}
|
||||
|
||||
if (symbol.flags & excludes) {
|
||||
if (symbol.isReplaceableByMethod) {
|
||||
// Javascript constructor-declared symbols can be discarded in favor of
|
||||
// prototype symbols like methods.
|
||||
symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name);
|
||||
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
|
||||
}
|
||||
else {
|
||||
if (node.name) {
|
||||
@@ -518,7 +521,6 @@ namespace ts {
|
||||
hasExplicitReturn = false;
|
||||
bindChildren(node);
|
||||
// Reset all reachability check related flags on node (for incremental scenarios)
|
||||
// Reset all emit helper flags on node (for incremental scenarios)
|
||||
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
|
||||
node.flags |= NodeFlags.HasImplicitReturn;
|
||||
@@ -599,8 +601,8 @@ namespace ts {
|
||||
// Binding of JsDocComment should be done before the current block scope container changes.
|
||||
// because the scope of JsDocComment should not be affected by whether the current node is a
|
||||
// container or not.
|
||||
if (isInJavaScriptFile(node) && node.jsDocComments) {
|
||||
forEach(node.jsDocComments, bind);
|
||||
if (isInJavaScriptFile(node) && node.jsDoc) {
|
||||
forEach(node.jsDoc, bind);
|
||||
}
|
||||
if (checkUnreachable(node)) {
|
||||
bindEachChild(node);
|
||||
@@ -1571,7 +1573,7 @@ namespace ts {
|
||||
const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
|
||||
addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
|
||||
typeLiteralSymbol.members = createMap<Symbol>();
|
||||
typeLiteralSymbol.members[symbol.name] = symbol;
|
||||
typeLiteralSymbol.members.set(symbol.name, symbol);
|
||||
}
|
||||
|
||||
function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
|
||||
@@ -1602,9 +1604,9 @@ namespace ts {
|
||||
? ElementKind.Property
|
||||
: ElementKind.Accessor;
|
||||
|
||||
const existingKind = seen[identifier.text];
|
||||
const existingKind = seen.get(identifier.text);
|
||||
if (!existingKind) {
|
||||
seen[identifier.text] = currentKind;
|
||||
seen.set(identifier.text, currentKind);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1950,9 +1952,6 @@ namespace ts {
|
||||
return bindParameter(<ParameterDeclaration>node);
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.BindingElement:
|
||||
if ((node as BindingElement).dotDotDotToken && node.parent.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
emitFlags |= NodeFlags.HasRestAttribute;
|
||||
}
|
||||
return bindVariableDeclarationOrBindingElement(<VariableDeclaration | BindingElement>node);
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
@@ -1980,7 +1979,6 @@ namespace ts {
|
||||
}
|
||||
root = root.parent;
|
||||
}
|
||||
emitFlags |= hasRest ? NodeFlags.HasRestAttribute : NodeFlags.HasSpreadAttribute;
|
||||
return;
|
||||
|
||||
case SyntaxKind.CallSignature:
|
||||
@@ -2213,7 +2211,7 @@ namespace ts {
|
||||
constructorFunction.parent = classPrototype;
|
||||
classPrototype.parent = leftSideOfAssignment;
|
||||
|
||||
const funcSymbol = container.locals[constructorFunction.text];
|
||||
const funcSymbol = container.locals.get(constructorFunction.text);
|
||||
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
|
||||
return;
|
||||
}
|
||||
@@ -2236,15 +2234,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
|
||||
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
|
||||
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
|
||||
emitFlags |= NodeFlags.HasClassExtends;
|
||||
}
|
||||
if (nodeIsDecorated(node)) {
|
||||
emitFlags |= NodeFlags.HasDecorators;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.ClassDeclaration) {
|
||||
bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
|
||||
}
|
||||
@@ -2253,7 +2242,7 @@ namespace ts {
|
||||
bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName);
|
||||
// Add name of class expression into the map for semantic classifier
|
||||
if (node.name) {
|
||||
classifiableNames[node.name.text] = node.name.text;
|
||||
classifiableNames.set(node.name.text, node.name.text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2269,14 +2258,14 @@ namespace ts {
|
||||
// module might have an exported variable called 'prototype'. We can't allow that as
|
||||
// that would clash with the built-in 'prototype' for the class.
|
||||
const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype");
|
||||
if (symbol.exports[prototypeSymbol.name]) {
|
||||
const symbolExport = symbol.exports.get(prototypeSymbol.name);
|
||||
if (symbolExport) {
|
||||
if (node.name) {
|
||||
node.name.parent = node;
|
||||
}
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0],
|
||||
Diagnostics.Duplicate_identifier_0, prototypeSymbol.name));
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name));
|
||||
}
|
||||
symbol.exports[prototypeSymbol.name] = prototypeSymbol;
|
||||
symbol.exports.set(prototypeSymbol.name, prototypeSymbol);
|
||||
prototypeSymbol.parent = symbol;
|
||||
}
|
||||
|
||||
@@ -2314,12 +2303,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindParameter(node: ParameterDeclaration) {
|
||||
if (!isDeclarationFile(file) &&
|
||||
!isInAmbientContext(node) &&
|
||||
nodeIsDecorated(node)) {
|
||||
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
|
||||
}
|
||||
|
||||
if (inStrictMode) {
|
||||
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
|
||||
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
|
||||
@@ -2377,9 +2360,6 @@ namespace ts {
|
||||
if (isAsyncFunctionLike(node)) {
|
||||
emitFlags |= NodeFlags.HasAsyncFunctions;
|
||||
}
|
||||
if (nodeIsDecorated(node)) {
|
||||
emitFlags |= NodeFlags.HasDecorators;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFlow && isObjectLiteralOrClassExpressionMethod(node)) {
|
||||
@@ -3156,6 +3136,7 @@ namespace ts {
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.MetaProperty:
|
||||
// These nodes are ES6 syntax.
|
||||
transformFlags |= TransformFlags.AssertES2015;
|
||||
break;
|
||||
@@ -3168,6 +3149,7 @@ namespace ts {
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
@@ -3366,6 +3348,7 @@ namespace ts {
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
|
||||
+1520
-850
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/// <reference path="sys.ts"/>
|
||||
/// <reference path="sys.ts"/>
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="diagnosticInformationMap.generated.ts"/>
|
||||
@@ -65,12 +65,13 @@ namespace ts {
|
||||
},
|
||||
{
|
||||
name: "jsx",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
"preserve": JsxEmit.Preserve,
|
||||
"react-native": JsxEmit.ReactNative,
|
||||
"react": JsxEmit.React
|
||||
}),
|
||||
paramType: Diagnostics.KIND,
|
||||
description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react,
|
||||
description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_react_native_or_react,
|
||||
},
|
||||
{
|
||||
name: "reactNamespace",
|
||||
@@ -100,7 +101,7 @@ namespace ts {
|
||||
{
|
||||
name: "module",
|
||||
shortName: "m",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
"none": ModuleKind.None,
|
||||
"commonjs": ModuleKind.CommonJS,
|
||||
"amd": ModuleKind.AMD,
|
||||
@@ -114,7 +115,7 @@ namespace ts {
|
||||
},
|
||||
{
|
||||
name: "newLine",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
"crlf": NewLineKind.CarriageReturnLineFeed,
|
||||
"lf": NewLineKind.LineFeed
|
||||
}),
|
||||
@@ -212,8 +213,8 @@ namespace ts {
|
||||
shortName: "p",
|
||||
type: "string",
|
||||
isFilePath: true,
|
||||
description: Diagnostics.Compile_the_project_in_the_given_directory,
|
||||
paramType: Diagnostics.DIRECTORY
|
||||
description: Diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json,
|
||||
paramType: Diagnostics.FILE_OR_DIRECTORY
|
||||
},
|
||||
{
|
||||
name: "removeComments",
|
||||
@@ -263,7 +264,7 @@ namespace ts {
|
||||
{
|
||||
name: "target",
|
||||
shortName: "t",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
"es3": ScriptTarget.ES3,
|
||||
"es5": ScriptTarget.ES5,
|
||||
"es6": ScriptTarget.ES2015,
|
||||
@@ -272,7 +273,7 @@ namespace ts {
|
||||
"es2017": ScriptTarget.ES2017,
|
||||
"esnext": ScriptTarget.ESNext,
|
||||
}),
|
||||
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015,
|
||||
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_or_ESNEXT,
|
||||
paramType: Diagnostics.VERSION,
|
||||
},
|
||||
{
|
||||
@@ -300,7 +301,7 @@ namespace ts {
|
||||
},
|
||||
{
|
||||
name: "moduleResolution",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
"node": ModuleResolutionKind.NodeJs,
|
||||
"classic": ModuleResolutionKind.Classic,
|
||||
}),
|
||||
@@ -409,7 +410,7 @@ namespace ts {
|
||||
type: "list",
|
||||
element: {
|
||||
name: "lib",
|
||||
type: createMap({
|
||||
type: createMapFromTemplate({
|
||||
// JavaScript only
|
||||
"es5": "lib.es5.d.ts",
|
||||
"es6": "lib.es2015.d.ts",
|
||||
@@ -462,11 +463,18 @@ namespace ts {
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
export let typingOptionDeclarations: CommandLineOption[] = [
|
||||
export let typeAcquisitionDeclarations: CommandLineOption[] = [
|
||||
{
|
||||
/* @deprecated typingOptions.enableAutoDiscovery
|
||||
* Use typeAcquisition.enable instead.
|
||||
*/
|
||||
name: "enableAutoDiscovery",
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
name: "enable",
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
name: "include",
|
||||
type: "list",
|
||||
@@ -501,6 +509,20 @@ namespace ts {
|
||||
|
||||
let optionNameMapCache: OptionNameMap;
|
||||
|
||||
/* @internal */
|
||||
export function convertEnableAutoDiscoveryToEnable(typeAcquisition: TypeAcquisition): TypeAcquisition {
|
||||
// Convert deprecated typingOptions.enableAutoDiscovery to typeAcquisition.enable
|
||||
if (typeAcquisition && typeAcquisition.enableAutoDiscovery !== undefined && typeAcquisition.enable === undefined) {
|
||||
const result: TypeAcquisition = {
|
||||
enable: typeAcquisition.enableAutoDiscovery,
|
||||
include: typeAcquisition.include || [],
|
||||
exclude: typeAcquisition.exclude || []
|
||||
};
|
||||
return result;
|
||||
}
|
||||
return typeAcquisition;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getOptionNameMap(): OptionNameMap {
|
||||
if (optionNameMapCache) {
|
||||
@@ -510,9 +532,9 @@ namespace ts {
|
||||
const optionNameMap = createMap<CommandLineOption>();
|
||||
const shortOptionNames = createMap<string>();
|
||||
forEach(optionDeclarations, option => {
|
||||
optionNameMap[option.name.toLowerCase()] = option;
|
||||
optionNameMap.set(option.name.toLowerCase(), option);
|
||||
if (option.shortName) {
|
||||
shortOptionNames[option.shortName] = option.name;
|
||||
shortOptionNames.set(option.shortName, option.name);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -522,20 +544,13 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic {
|
||||
const namesOfType = Object.keys(opt.type).map(key => `'${key}'`).join(", ");
|
||||
const namesOfType = arrayFrom(opt.type.keys()).map(key => `'${key}'`).join(", ");
|
||||
return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
|
||||
const key = trimString((value || "")).toLowerCase();
|
||||
const map = opt.type;
|
||||
if (key in map) {
|
||||
return map[key];
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnosticForInvalidCustomType(opt));
|
||||
}
|
||||
return convertJsonOptionOfCustomType(opt, trimString(value || ""), errors);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -558,7 +573,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function parseCommandLine(commandLine: string[], readFile?: (path: string) => string): ParsedCommandLine {
|
||||
const options: CompilerOptions = {};
|
||||
const fileNames: string[] = [];
|
||||
@@ -584,13 +598,13 @@ namespace ts {
|
||||
s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
|
||||
|
||||
// Try to translate short option names to their full equivalents.
|
||||
if (s in shortOptionNames) {
|
||||
s = shortOptionNames[s];
|
||||
const short = shortOptionNames.get(s);
|
||||
if (short !== undefined) {
|
||||
s = short;
|
||||
}
|
||||
|
||||
if (s in optionNameMap) {
|
||||
const opt = optionNameMap[s];
|
||||
|
||||
const opt = optionNameMap.get(s);
|
||||
if (opt) {
|
||||
if (opt.isTSConfigOnly) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
|
||||
}
|
||||
@@ -607,7 +621,7 @@ namespace ts {
|
||||
break;
|
||||
case "boolean":
|
||||
// boolean flag has optional value true, false, others
|
||||
let optValue = args[i];
|
||||
const optValue = args[i];
|
||||
options[opt.name] = optValue !== "false";
|
||||
// consume next argument as boolean flag value
|
||||
if (optValue === "false" || optValue === "true") {
|
||||
@@ -713,7 +727,7 @@ namespace ts {
|
||||
* @param fileNames array of filenames to be generated into tsconfig.json
|
||||
*/
|
||||
/* @internal */
|
||||
export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map<CompilerOptionsValue> } {
|
||||
export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: MapLike<CompilerOptionsValue> } {
|
||||
const compilerOptions = extend(options, defaultInitCompilerOptions);
|
||||
const configurations: any = {
|
||||
compilerOptions: serializeCompilerOptions(compilerOptions)
|
||||
@@ -738,18 +752,17 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike<string | number>): string | undefined {
|
||||
function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map<string | number>): string | undefined {
|
||||
// There is a typeMap associated with this command-line option so use it to map value back to its name
|
||||
for (const key in customTypeMap) {
|
||||
if (customTypeMap[key] === value) {
|
||||
return forEachEntry(customTypeMap, (mapValue, key) => {
|
||||
if (mapValue === value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function serializeCompilerOptions(options: CompilerOptions): Map<CompilerOptionsValue> {
|
||||
const result = createMap<CompilerOptionsValue>();
|
||||
function serializeCompilerOptions(options: CompilerOptions): MapLike<CompilerOptionsValue> {
|
||||
const result: ts.MapLike<CompilerOptionsValue> = {};
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
|
||||
for (const name in options) {
|
||||
@@ -765,7 +778,7 @@ namespace ts {
|
||||
break;
|
||||
default:
|
||||
const value = options[name];
|
||||
let optionDefinition = optionsNameMap[name.toLowerCase()];
|
||||
const optionDefinition = optionsNameMap.get(name.toLowerCase());
|
||||
if (optionDefinition) {
|
||||
const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
|
||||
if (!customTypeMap) {
|
||||
@@ -827,15 +840,16 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine {
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], extraFileExtensions: FileExtensionInfo[] = []): ParsedCommandLine {
|
||||
const errors: Diagnostic[] = [];
|
||||
basePath = normalizeSlashes(basePath);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
|
||||
if (resolutionStack.indexOf(resolvedPath) >= 0) {
|
||||
return {
|
||||
options: {},
|
||||
fileNames: [],
|
||||
typingOptions: {},
|
||||
typeAcquisition: {},
|
||||
raw: json,
|
||||
errors: [createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))],
|
||||
wildcardDirectories: {}
|
||||
@@ -843,7 +857,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
let options: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
|
||||
const typingOptions: TypingOptions = convertTypingOptionsFromJsonWorker(json["typingOptions"], basePath, errors, configFileName);
|
||||
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
|
||||
// It should be removed in future releases - use typeAcquisition instead.
|
||||
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
|
||||
const typeAcquisition: TypeAcquisition = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName);
|
||||
|
||||
if (json["extends"]) {
|
||||
let [include, exclude, files, baseOptions]: [string[], string[], string[], CompilerOptions] = [undefined, undefined, undefined, {}];
|
||||
@@ -874,7 +891,7 @@ namespace ts {
|
||||
return {
|
||||
options,
|
||||
fileNames,
|
||||
typingOptions,
|
||||
typeAcquisition,
|
||||
raw: json,
|
||||
errors,
|
||||
wildcardDirectories,
|
||||
@@ -951,8 +968,8 @@ namespace ts {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
|
||||
}
|
||||
else {
|
||||
// By default, exclude common package folders and the outDir
|
||||
excludeSpecs = ["node_modules", "bower_components", "jspm_packages"];
|
||||
// If no includes were specified, exclude common package folders and the outDir
|
||||
excludeSpecs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
@@ -964,7 +981,7 @@ namespace ts {
|
||||
includeSpecs = ["**/*"];
|
||||
}
|
||||
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors);
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, extraFileExtensions);
|
||||
|
||||
if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) {
|
||||
errors.push(
|
||||
@@ -996,9 +1013,9 @@ namespace ts {
|
||||
return { options, errors };
|
||||
}
|
||||
|
||||
export function convertTypingOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypingOptions, errors: Diagnostic[] } {
|
||||
export function convertTypeAcquisitionFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypeAcquisition, errors: Diagnostic[] } {
|
||||
const errors: Diagnostic[] = [];
|
||||
const options = convertTypingOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
|
||||
const options = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName);
|
||||
return { options, errors };
|
||||
}
|
||||
|
||||
@@ -1012,16 +1029,18 @@ namespace ts {
|
||||
return options;
|
||||
}
|
||||
|
||||
function convertTypingOptionsFromJsonWorker(jsonOptions: any,
|
||||
basePath: string, errors: Diagnostic[], configFileName?: string): TypingOptions {
|
||||
function convertTypeAcquisitionFromJsonWorker(jsonOptions: any,
|
||||
basePath: string, errors: Diagnostic[], configFileName?: string): TypeAcquisition {
|
||||
|
||||
const options: TypeAcquisition = { enable: getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] };
|
||||
const typeAcquisition = convertEnableAutoDiscoveryToEnable(jsonOptions);
|
||||
convertOptionsFromJson(typeAcquisitionDeclarations, typeAcquisition, basePath, options, Diagnostics.Unknown_type_acquisition_option_0, errors);
|
||||
|
||||
const options: TypingOptions = { enableAutoDiscovery: getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] };
|
||||
convertOptionsFromJson(typingOptionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_typing_option_0, errors);
|
||||
return options;
|
||||
}
|
||||
|
||||
function convertOptionsFromJson(optionDeclarations: CommandLineOption[], jsonOptions: any, basePath: string,
|
||||
defaultOptions: CompilerOptions | TypingOptions, diagnosticMessage: DiagnosticMessage, errors: Diagnostic[]) {
|
||||
defaultOptions: CompilerOptions | TypeAcquisition, diagnosticMessage: DiagnosticMessage, errors: Diagnostic[]) {
|
||||
|
||||
if (!jsonOptions) {
|
||||
return;
|
||||
@@ -1030,8 +1049,8 @@ namespace ts {
|
||||
const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name);
|
||||
|
||||
for (const id in jsonOptions) {
|
||||
if (id in optionNameMap) {
|
||||
const opt = optionNameMap[id];
|
||||
const opt = optionNameMap.get(id);
|
||||
if (opt) {
|
||||
defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors);
|
||||
}
|
||||
else {
|
||||
@@ -1067,8 +1086,9 @@ namespace ts {
|
||||
|
||||
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
|
||||
const key = value.toLowerCase();
|
||||
if (key in opt.type) {
|
||||
return opt.type[key];
|
||||
const val = opt.type.get(key);
|
||||
if (val !== undefined) {
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnosticForInvalidCustomType(opt));
|
||||
@@ -1166,7 +1186,7 @@ namespace ts {
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
*/
|
||||
function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[]): ExpandResult {
|
||||
function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], extraFileExtensions: FileExtensionInfo[]): ExpandResult {
|
||||
basePath = normalizePath(basePath);
|
||||
|
||||
// The exclude spec list is converted into a regular expression, which allows us to quickly
|
||||
@@ -1196,18 +1216,18 @@ namespace ts {
|
||||
// file map that marks whether it was a regular wildcard match (with a `*` or `?` token),
|
||||
// or a recursive directory. This information is used by filesystem watchers to monitor for
|
||||
// new entries in these paths.
|
||||
const wildcardDirectories: Map<WatchDirectoryFlags> = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames);
|
||||
const wildcardDirectories = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames);
|
||||
|
||||
// Rather than requery this for each file and filespec, we query the supported extensions
|
||||
// once and store it on the expansion context.
|
||||
const supportedExtensions = getSupportedExtensions(options);
|
||||
const supportedExtensions = getSupportedExtensions(options, extraFileExtensions);
|
||||
|
||||
// Literal files are always included verbatim. An "include" or "exclude" specification cannot
|
||||
// remove a literal file.
|
||||
if (fileNames) {
|
||||
for (const fileName of fileNames) {
|
||||
const file = combinePaths(basePath, fileName);
|
||||
literalFileMap[keyMapper(file)] = file;
|
||||
literalFileMap.set(keyMapper(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,14 +1250,14 @@ namespace ts {
|
||||
removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper);
|
||||
|
||||
const key = keyMapper(file);
|
||||
if (!(key in literalFileMap) && !(key in wildcardFileMap)) {
|
||||
wildcardFileMap[key] = file;
|
||||
if (!literalFileMap.has(key) && !wildcardFileMap.has(key)) {
|
||||
wildcardFileMap.set(key, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []);
|
||||
const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []);
|
||||
const literalFiles = arrayFrom(literalFileMap.values());
|
||||
const wildcardFiles = arrayFrom(wildcardFileMap.values());
|
||||
wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive);
|
||||
return {
|
||||
fileNames: literalFiles.concat(wildcardFiles),
|
||||
@@ -1268,7 +1288,7 @@ namespace ts {
|
||||
/**
|
||||
* Gets directories in a set of include patterns that should be watched for changes.
|
||||
*/
|
||||
function getWildcardDirectories(include: string[], exclude: string[], path: string, useCaseSensitiveFileNames: boolean): Map<WatchDirectoryFlags> {
|
||||
function getWildcardDirectories(include: string[], exclude: string[], path: string, useCaseSensitiveFileNames: boolean): MapLike<WatchDirectoryFlags> {
|
||||
// We watch a directory recursively if it contains a wildcard anywhere in a directory segment
|
||||
// of the pattern:
|
||||
//
|
||||
@@ -1283,7 +1303,7 @@ namespace ts {
|
||||
// /a/b/a?z - Watch /a/b directly to catch any new file matching a?z
|
||||
const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude");
|
||||
const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i");
|
||||
const wildcardDirectories = createMap<WatchDirectoryFlags>();
|
||||
const wildcardDirectories: ts.MapLike<WatchDirectoryFlags> = {};
|
||||
if (include !== undefined) {
|
||||
const recursiveKeys: string[] = [];
|
||||
for (const file of include) {
|
||||
@@ -1306,13 +1326,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Remove any subpaths under an existing recursively watched directory.
|
||||
for (const key in wildcardDirectories) {
|
||||
for (const key in wildcardDirectories) if (hasProperty(wildcardDirectories, key)) {
|
||||
for (const recursiveKey of recursiveKeys) {
|
||||
if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) {
|
||||
delete wildcardDirectories[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return wildcardDirectories;
|
||||
@@ -1346,7 +1366,7 @@ namespace ts {
|
||||
for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
|
||||
const higherPriorityExtension = extensions[i];
|
||||
const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension));
|
||||
if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) {
|
||||
if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1368,21 +1388,10 @@ namespace ts {
|
||||
for (let i = nextExtensionPriority; i < extensions.length; i++) {
|
||||
const lowerPriorityExtension = extensions[i];
|
||||
const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension));
|
||||
delete wildcardFiles[lowerPriorityPath];
|
||||
wildcardFiles.delete(lowerPriorityPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to an array of files.
|
||||
*
|
||||
* @param output The output array.
|
||||
* @param file The file path.
|
||||
*/
|
||||
function addFileToOutput(output: string[], file: string) {
|
||||
output.push(file);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a case sensitive key.
|
||||
*
|
||||
|
||||
@@ -156,6 +156,9 @@ namespace ts {
|
||||
|
||||
if (!skipTrailingComments) {
|
||||
emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
|
||||
if (hasWrittenComment && !writer.isAtStartOfLine()) {
|
||||
writer.writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
|
||||
+218
-152
@@ -23,13 +23,14 @@ namespace ts {
|
||||
True = -1
|
||||
}
|
||||
|
||||
const createObject = Object.create;
|
||||
|
||||
// More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
|
||||
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator() : undefined;
|
||||
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
|
||||
// Intl is missing in Safari, and node 0.10 treats "a" as greater than "B".
|
||||
export const localeCompareIsCorrect = ts.collator && ts.collator.compare("a", "B") < 0;
|
||||
|
||||
export function createMap<T>(template?: MapLike<T>): Map<T> {
|
||||
const map: Map<T> = createObject(null); // tslint:disable-line:no-null-keyword
|
||||
/** Create a MapLike with good performance. */
|
||||
function createDictionaryObject<T>(): MapLike<T> {
|
||||
const map = Object.create(null); // tslint:disable-line:no-null-keyword
|
||||
|
||||
// Using 'delete' on an object causes V8 to put the object in dictionary mode.
|
||||
// This disables creation of hidden classes, which are expensive when an object is
|
||||
@@ -37,17 +38,113 @@ namespace ts {
|
||||
map["__"] = undefined;
|
||||
delete map["__"];
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/** Create a new map. If a template object is provided, the map will copy entries from it. */
|
||||
export function createMap<T>(): Map<T> {
|
||||
return new MapCtr<T>();
|
||||
}
|
||||
|
||||
export function createMapFromTemplate<T>(template?: MapLike<T>): Map<T> {
|
||||
const map: Map<T> = new MapCtr<T>();
|
||||
|
||||
// Copies keys/values from template. Note that for..in will not throw if
|
||||
// template is undefined, and instead will just exit the loop.
|
||||
for (const key in template) if (hasOwnProperty.call(template, key)) {
|
||||
map[key] = template[key];
|
||||
map.set(key, template[key]);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// The global Map object. This may not be available, so we must test for it.
|
||||
declare const Map: { new<T>(): Map<T> } | undefined;
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// tslint:disable-next-line:no-in-operator
|
||||
const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
|
||||
|
||||
// Keep the class inside a function so it doesn't get compiled if it's not used.
|
||||
function shimMap(): { new<T>(): Map<T> } {
|
||||
|
||||
class MapIterator<T, U extends (string | T | [string, T])> {
|
||||
private data: MapLike<T>;
|
||||
private keys: string[];
|
||||
private index = 0;
|
||||
private selector: (data: MapLike<T>, key: string) => U;
|
||||
constructor(data: MapLike<T>, selector: (data: MapLike<T>, key: string) => U) {
|
||||
this.data = data;
|
||||
this.selector = selector;
|
||||
this.keys = Object.keys(data);
|
||||
}
|
||||
|
||||
public next(): { value: U, done: false } | { value: never, done: true } {
|
||||
const index = this.index;
|
||||
if (index < this.keys.length) {
|
||||
this.index++;
|
||||
return { value: this.selector(this.data, this.keys[index]), done: false };
|
||||
}
|
||||
return { value: undefined as never, done: true }
|
||||
}
|
||||
}
|
||||
|
||||
return class<T> implements Map<T> {
|
||||
private data = createDictionaryObject<T>();
|
||||
public size = 0;
|
||||
|
||||
get(key: string): T {
|
||||
return this.data[key];
|
||||
}
|
||||
|
||||
set(key: string, value: T): this {
|
||||
if (!this.has(key)) {
|
||||
this.size++;
|
||||
}
|
||||
this.data[key] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
// tslint:disable-next-line:no-in-operator
|
||||
return key in this.data;
|
||||
}
|
||||
|
||||
delete(key: string): boolean {
|
||||
if (this.has(key)) {
|
||||
this.size--;
|
||||
delete this.data[key];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.data = createDictionaryObject<T>();
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return new MapIterator(this.data, (_data, key) => key);
|
||||
}
|
||||
|
||||
values() {
|
||||
return new MapIterator(this.data, (data, key) => data[key]);
|
||||
}
|
||||
|
||||
entries() {
|
||||
return new MapIterator(this.data, (data, key) => [key, data[key]] as [string, T]);
|
||||
}
|
||||
|
||||
forEach(action: (value: T, key: string) => void): void {
|
||||
for (const key in this.data) {
|
||||
action(this.data[key], key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileMap<T>(keyMapper?: (key: string) => string): FileMap<T> {
|
||||
let files = createMap<T>();
|
||||
const files = createMap<T>();
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
@@ -59,39 +156,34 @@ namespace ts {
|
||||
};
|
||||
|
||||
function forEachValueInMap(f: (key: Path, value: T) => void) {
|
||||
for (const key in files) {
|
||||
f(<Path>key, files[key]);
|
||||
}
|
||||
files.forEach((file, key) => {
|
||||
f(<Path>key, file);
|
||||
});
|
||||
}
|
||||
|
||||
function getKeys() {
|
||||
const keys: Path[] = [];
|
||||
for (const key in files) {
|
||||
keys.push(<Path>key);
|
||||
}
|
||||
return keys;
|
||||
return arrayFrom(files.keys()) as Path[];
|
||||
}
|
||||
|
||||
// path should already be well-formed so it does not need to be normalized
|
||||
function get(path: Path): T {
|
||||
return files[toKey(path)];
|
||||
return files.get(toKey(path));
|
||||
}
|
||||
|
||||
function set(path: Path, value: T) {
|
||||
files[toKey(path)] = value;
|
||||
files.set(toKey(path), value);
|
||||
}
|
||||
|
||||
function contains(path: Path) {
|
||||
return toKey(path) in files;
|
||||
return files.has(toKey(path));
|
||||
}
|
||||
|
||||
function remove(path: Path) {
|
||||
const key = toKey(path);
|
||||
delete files[key];
|
||||
files.delete(toKey(path));
|
||||
}
|
||||
|
||||
function clear() {
|
||||
files = createMap<T>();
|
||||
files.clear();
|
||||
}
|
||||
|
||||
function toKey(path: Path): string {
|
||||
@@ -112,6 +204,10 @@ namespace ts {
|
||||
GreaterThan = 1
|
||||
}
|
||||
|
||||
export function length(array: any[]) {
|
||||
return array ? array.length : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through 'array' by index and performs the callback on each element of array until the callback
|
||||
* returns a truthy value, then returns that value.
|
||||
@@ -119,7 +215,7 @@ namespace ts {
|
||||
*/
|
||||
export function forEach<T, U>(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
|
||||
if (array) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -143,7 +239,7 @@ namespace ts {
|
||||
*/
|
||||
export function every<T>(array: T[], callback: (element: T, index: number) => boolean): boolean {
|
||||
if (array) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!callback(array[i], i)) {
|
||||
return false;
|
||||
}
|
||||
@@ -155,7 +251,7 @@ namespace ts {
|
||||
|
||||
/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */
|
||||
export function find<T>(array: T[], predicate: (element: T, index: number) => boolean): T | undefined {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const value = array[i];
|
||||
if (predicate(value, i)) {
|
||||
return value;
|
||||
@@ -169,7 +265,7 @@ namespace ts {
|
||||
* This is like `forEach`, but never returns undefined.
|
||||
*/
|
||||
export function findMap<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -191,7 +287,7 @@ namespace ts {
|
||||
|
||||
export function indexOf<T>(array: T[], value: T): number {
|
||||
if (array) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i] === value) {
|
||||
return i;
|
||||
}
|
||||
@@ -201,7 +297,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number {
|
||||
for (let i = start || 0, len = text.length; i < len; i++) {
|
||||
for (let i = start || 0; i < text.length; i++) {
|
||||
if (contains(charCodes, text.charCodeAt(i))) {
|
||||
return i;
|
||||
}
|
||||
@@ -420,17 +516,16 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapObject<T, U>(object: MapLike<T>, f: (key: string, x: T) => [string, U]): MapLike<U> {
|
||||
let result: MapLike<U>;
|
||||
if (object) {
|
||||
result = {};
|
||||
for (const v of getOwnKeys(object)) {
|
||||
const [key, value]: [string, U] = f(v, object[v]) || [undefined, undefined];
|
||||
if (key !== undefined) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
export function mapEntries<T, U>(map: Map<T>, f: (key: string, value: T) => [string, U]): Map<U> {
|
||||
if (!map) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = createMap<U>();
|
||||
map.forEach((value, key) => {
|
||||
const [newKey, newValue] = f(key, value);
|
||||
result.set(newKey, newValue);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -748,9 +843,6 @@ namespace ts {
|
||||
/**
|
||||
* Indicates whether a map-like contains an own property with the specified key.
|
||||
*
|
||||
* NOTE: This is intended for use only with MapLike<T> objects. For Map<T> objects, use
|
||||
* the 'in' operator.
|
||||
*
|
||||
* @param map A map-like.
|
||||
* @param key A property key.
|
||||
*/
|
||||
@@ -761,9 +853,6 @@ namespace ts {
|
||||
/**
|
||||
* Gets the value of an owned property in a map-like.
|
||||
*
|
||||
* NOTE: This is intended for use only with MapLike<T> objects. For Map<T> objects, use
|
||||
* an indexer.
|
||||
*
|
||||
* @param map A map-like.
|
||||
* @param key A property key.
|
||||
*/
|
||||
@@ -787,50 +876,48 @@ namespace ts {
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates the properties of a Map<T>, invoking a callback and returning the first truthy result.
|
||||
*
|
||||
* @param map A map for which properties should be enumerated.
|
||||
* @param callback A callback to invoke for each property.
|
||||
*/
|
||||
export function forEachProperty<T, U>(map: Map<T>, callback: (value: T, key: string) => U): U {
|
||||
let result: U;
|
||||
for (const key in map) {
|
||||
if (result = callback(map[key], key)) break;
|
||||
/** Shims `Array.from`. */
|
||||
export function arrayFrom<T>(iterator: Iterator<T>): T[] {
|
||||
const result: T[] = [];
|
||||
for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) {
|
||||
result.push(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a Map<T> has some matching property.
|
||||
*
|
||||
* @param map A map whose properties should be tested.
|
||||
* @param predicate An optional callback used to test each property.
|
||||
* Calls `callback` for each entry in the map, returning the first truthy result.
|
||||
* Use `map.forEach` instead for normal iteration.
|
||||
*/
|
||||
export function someProperties<T>(map: Map<T>, predicate?: (value: T, key: string) => boolean) {
|
||||
for (const key in map) {
|
||||
if (!predicate || predicate(map[key], key)) return true;
|
||||
export function forEachEntry<T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined): U | undefined {
|
||||
const iterator = map.entries();
|
||||
for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) {
|
||||
const [key, value] = pair;
|
||||
const result = callback(value, key);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a shallow copy of the properties from a source Map<T> to a target MapLike<T>
|
||||
*
|
||||
* @param source A map from which properties should be copied.
|
||||
* @param target A map to which properties should be copied.
|
||||
*/
|
||||
export function copyProperties<T>(source: Map<T>, target: MapLike<T>): void {
|
||||
for (const key in source) {
|
||||
target[key] = source[key];
|
||||
/** `forEachEntry` for just keys. */
|
||||
export function forEachKey<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {
|
||||
const iterator = map.keys();
|
||||
for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) {
|
||||
const result = callback(key);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function appendProperty<T>(map: Map<T>, key: string | number, value: T): Map<T> {
|
||||
if (key === undefined || value === undefined) return map;
|
||||
if (map === undefined) map = createMap<T>();
|
||||
map[key] = value;
|
||||
return map;
|
||||
/** Copy entries from `source` to `target`. */
|
||||
export function copyEntries<T>(source: Map<T>, target: Map<T>): void {
|
||||
source.forEach((value, key) => {
|
||||
target.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
export function assign<T1 extends MapLike<{}>, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3;
|
||||
@@ -845,42 +932,6 @@ namespace ts {
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the properties of a map.
|
||||
*
|
||||
* NOTE: This is intended for use with Map<T> objects. For MapLike<T> objects, use
|
||||
* reduceOwnProperties instead as it offers better runtime safety.
|
||||
*
|
||||
* @param map The map to reduce
|
||||
* @param callback An aggregation function that is called for each entry in the map
|
||||
* @param initial The initial value for the reduction.
|
||||
*/
|
||||
export function reduceProperties<T, U>(map: Map<T>, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
|
||||
let result = initial;
|
||||
for (const key in map) {
|
||||
result = callback(result, map[key], String(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the properties defined on a map-like (but not from its prototype chain).
|
||||
*
|
||||
* NOTE: This is intended for use with MapLike<T> objects. For Map<T> objects, use
|
||||
* reduceProperties instead as it offers better performance.
|
||||
*
|
||||
* @param map The map-like to reduce
|
||||
* @param callback An aggregation function that is called for each entry in the map
|
||||
* @param initial The initial value for the reduction.
|
||||
*/
|
||||
export function reduceOwnProperties<T, U>(map: MapLike<T>, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
|
||||
let result = initial;
|
||||
for (const key in map) if (hasOwnProperty.call(map, key)) {
|
||||
result = callback(result, map[key], String(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a shallow equality comparison of the contents of two map-likes.
|
||||
*
|
||||
@@ -915,23 +966,14 @@ namespace ts {
|
||||
export function arrayToMap<T, U>(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map<T | U> {
|
||||
const result = createMap<T | U>();
|
||||
for (const value of array) {
|
||||
result[makeKey(value)] = makeValue ? makeValue(value) : value;
|
||||
result.set(makeKey(value), makeValue ? makeValue(value) : value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isEmpty<T>(map: Map<T>) {
|
||||
for (const id in map) {
|
||||
if (hasProperty(map, id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function cloneMap<T>(map: Map<T>) {
|
||||
const clone = createMap<T>();
|
||||
copyProperties(map, clone);
|
||||
copyEntries(map, clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -956,32 +998,43 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the value to an array of values associated with the key, and returns the array.
|
||||
* Creates the array if it does not already exist.
|
||||
*/
|
||||
export function multiMapAdd<V>(map: Map<V[]>, key: string | number, value: V): V[] {
|
||||
const values = map[key];
|
||||
if (values) {
|
||||
values.push(value);
|
||||
return values;
|
||||
}
|
||||
else {
|
||||
return map[key] = [value];
|
||||
}
|
||||
export interface MultiMap<T> extends Map<T[]> {
|
||||
/**
|
||||
* Adds the value to an array of values associated with the key, and returns the array.
|
||||
* Creates the array if it does not already exist.
|
||||
*/
|
||||
add(key: string, value: T): T[];
|
||||
/**
|
||||
* Removes a value from an array of values associated with the key.
|
||||
* Does not preserve the order of those values.
|
||||
* Does nothing if `key` is not in `map`, or `value` is not in `map[key]`.
|
||||
*/
|
||||
remove(key: string, value: T): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a value from an array of values associated with the key.
|
||||
* Does not preserve the order of those values.
|
||||
* Does nothing if `key` is not in `map`, or `value` is not in `map[key]`.
|
||||
*/
|
||||
export function multiMapRemove<V>(map: Map<V[]>, key: string, value: V): void {
|
||||
const values = map[key];
|
||||
export function createMultiMap<T>(): MultiMap<T> {
|
||||
const map = createMap<T[]>() as MultiMap<T>;
|
||||
map.add = multiMapAdd;
|
||||
map.remove = multiMapRemove;
|
||||
return map;
|
||||
}
|
||||
function multiMapAdd<T>(this: MultiMap<T>, key: string, value: T) {
|
||||
let values = this.get(key);
|
||||
if (values) {
|
||||
values.push(value);
|
||||
}
|
||||
else {
|
||||
this.set(key, values = [value]);
|
||||
}
|
||||
return values;
|
||||
|
||||
}
|
||||
function multiMapRemove<T>(this: MultiMap<T>, key: string, value: T) {
|
||||
const values = this.get(key);
|
||||
if (values) {
|
||||
unorderedRemoveItem(values, value);
|
||||
if (!values.length) {
|
||||
delete map[key];
|
||||
this.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1078,13 +1131,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function formatStringFromArgs(text: string, args: { [index: number]: string; }, baseIndex?: number): string {
|
||||
export function formatStringFromArgs(text: string, args: { [index: number]: string; }, baseIndex?: number): string {
|
||||
baseIndex = baseIndex || 0;
|
||||
|
||||
return text.replace(/{(\d+)}/g, (_match, index?) => args[+index + baseIndex]);
|
||||
}
|
||||
|
||||
export let localizedDiagnosticMessages: Map<string> = undefined;
|
||||
export let localizedDiagnosticMessages: MapLike<string> = undefined;
|
||||
|
||||
export function getLocaleSpecificMessage(message: DiagnosticMessage) {
|
||||
return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message;
|
||||
@@ -1200,9 +1253,12 @@ namespace ts {
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
if (ignoreCase) {
|
||||
if (collator && String.prototype.localeCompare) {
|
||||
// accent means a ≠ b, a ≠ á, a = A
|
||||
const result = a.localeCompare(b, /*locales*/ undefined, { usage: "sort", sensitivity: "accent" });
|
||||
// Checking if "collator exists indicates that Intl is available.
|
||||
// We still have to check if "collator.compare" is correct. If it is not, use "String.localeComapre"
|
||||
if (collator) {
|
||||
const result = localeCompareIsCorrect ?
|
||||
collator.compare(a, b) :
|
||||
a.localeCompare(b, /*locales*/ undefined, { usage: "sort", sensitivity: "accent" }); // accent means a ≠ b, a ≠ á, a = A
|
||||
return result < 0 ? Comparison.LessThan : result > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
|
||||
}
|
||||
|
||||
@@ -1942,8 +1998,18 @@ namespace ts {
|
||||
export const supportedJavascriptExtensions = [".js", ".jsx"];
|
||||
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
|
||||
|
||||
export function getSupportedExtensions(options?: CompilerOptions): string[] {
|
||||
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;
|
||||
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: FileExtensionInfo[]): string[] {
|
||||
const needAllExtensions = options && options.allowJs;
|
||||
if (!extraFileExtensions || extraFileExtensions.length === 0) {
|
||||
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
|
||||
}
|
||||
const extensions = (needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions).slice(0);
|
||||
for (const extInfo of extraFileExtensions) {
|
||||
if (needAllExtensions || extInfo.scriptKind === ScriptKind.TS) {
|
||||
extensions.push(extInfo.extension);
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
export function hasJavaScriptFileExtension(fileName: string) {
|
||||
@@ -1954,10 +2020,10 @@ namespace ts {
|
||||
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
|
||||
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions) {
|
||||
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: FileExtensionInfo[]) {
|
||||
if (!fileName) { return false; }
|
||||
|
||||
for (const extension of getSupportedExtensions(compilerOptions)) {
|
||||
for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) {
|
||||
if (fileExtensionIs(fileName, extension)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ts {
|
||||
|
||||
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, targetSourceFile: SourceFile): Diagnostic[] {
|
||||
const declarationDiagnostics = createDiagnosticCollection();
|
||||
forEachExpectedEmitFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
|
||||
forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
|
||||
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
|
||||
|
||||
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
|
||||
@@ -156,9 +156,9 @@ namespace ts {
|
||||
});
|
||||
|
||||
if (usedTypeDirectiveReferences) {
|
||||
for (const directive in usedTypeDirectiveReferences) {
|
||||
forEachKey(usedTypeDirectiveReferences, directive => {
|
||||
referencesOutput += `/// <reference types="${directive}" />${newLine}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -194,6 +194,7 @@ namespace ts {
|
||||
writer.writeSpace = writer.write;
|
||||
writer.writeStringLiteral = writer.writeLiteral;
|
||||
writer.writeParameter = writer.write;
|
||||
writer.writeProperty = writer.write;
|
||||
writer.writeSymbol = writer.write;
|
||||
setWriter(writer);
|
||||
}
|
||||
@@ -270,8 +271,8 @@ namespace ts {
|
||||
usedTypeDirectiveReferences = createMap<string>();
|
||||
}
|
||||
for (const directive of typeReferenceDirectives) {
|
||||
if (!(directive in usedTypeDirectiveReferences)) {
|
||||
usedTypeDirectiveReferences[directive] = directive;
|
||||
if (!usedTypeDirectiveReferences.has(directive)) {
|
||||
usedTypeDirectiveReferences.set(directive, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +372,7 @@ namespace ts {
|
||||
|
||||
function writeJsDocComments(declaration: Node) {
|
||||
if (declaration) {
|
||||
const jsDocComments = getJsDocCommentsFromText(declaration, currentText);
|
||||
const jsDocComments = getJSDocCommentRanges(declaration, currentText);
|
||||
emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments);
|
||||
// jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space
|
||||
emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange);
|
||||
@@ -389,6 +390,7 @@ namespace ts {
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
@@ -580,14 +582,14 @@ namespace ts {
|
||||
// do not need to keep track of created temp names.
|
||||
function getExportDefaultTempVariableName(): string {
|
||||
const baseName = "_default";
|
||||
if (!(baseName in currentIdentifiers)) {
|
||||
if (!currentIdentifiers.has(baseName)) {
|
||||
return baseName;
|
||||
}
|
||||
let count = 0;
|
||||
while (true) {
|
||||
count++;
|
||||
const name = baseName + "_" + count;
|
||||
if (!(name in currentIdentifiers)) {
|
||||
if (!currentIdentifiers.has(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1037,6 +1039,10 @@ namespace ts {
|
||||
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
|
||||
break;
|
||||
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.fail("This is unknown parent for type parameter: " + node.parent.kind);
|
||||
}
|
||||
@@ -1143,7 +1149,10 @@ namespace ts {
|
||||
const prevEnclosingDeclaration = enclosingDeclaration;
|
||||
enclosingDeclaration = node;
|
||||
emitTypeParameters(node.typeParameters);
|
||||
emitHeritageClause(getInterfaceBaseTypeNodes(node), /*isImplementsList*/ false);
|
||||
const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression));
|
||||
if (interfaceExtendsTypes && interfaceExtendsTypes.length) {
|
||||
emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false);
|
||||
}
|
||||
write(" {");
|
||||
writeLine();
|
||||
increaseIndent();
|
||||
@@ -1618,6 +1627,12 @@ namespace ts {
|
||||
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
|
||||
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
|
||||
|
||||
case SyntaxKind.IndexSignature:
|
||||
// Interfaces cannot have parameter types that cannot be named
|
||||
return symbolAccessibilityResult.errorModuleName ?
|
||||
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
|
||||
Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1;
|
||||
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
if (hasModifier(node.parent, ModifierFlags.Static)) {
|
||||
@@ -1773,7 +1788,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
// Get the declaration file path
|
||||
forEachExpectedEmitFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
|
||||
forEachEmittedFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
|
||||
}
|
||||
|
||||
if (declFileName) {
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
"category": "Error",
|
||||
"code": 1084
|
||||
},
|
||||
"Octal literals are not available when targeting ECMAScript 5 and higher.": {
|
||||
"Octal literals are not available when targeting ECMAScript 5 and higher. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 1085
|
||||
},
|
||||
@@ -851,6 +851,14 @@
|
||||
"category": "Error",
|
||||
"code": 1317
|
||||
},
|
||||
"An abstract accessor cannot have an implementation.": {
|
||||
"category": "Error",
|
||||
"code": 1318
|
||||
},
|
||||
"A default export can only be used in an ECMAScript-style module.": {
|
||||
"category": "Error",
|
||||
"code": 1319
|
||||
},
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2300
|
||||
@@ -1023,6 +1031,10 @@
|
||||
"category": "Error",
|
||||
"code": 2342
|
||||
},
|
||||
"This syntax requires an imported helper named '{1}', but module '{0}' has no exported member '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2343
|
||||
},
|
||||
"Type '{0}' does not satisfy the constraint '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2344
|
||||
@@ -1063,6 +1075,10 @@
|
||||
"category": "Error",
|
||||
"code": 2353
|
||||
},
|
||||
"This syntax requires an imported helper but module '{0}' cannot be found.": {
|
||||
"category": "Error",
|
||||
"code": 2354
|
||||
},
|
||||
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
|
||||
"category": "Error",
|
||||
"code": 2355
|
||||
@@ -1731,7 +1747,7 @@
|
||||
"category": "Error",
|
||||
"code": 2535
|
||||
},
|
||||
"Type '{0}' is not constrained to 'keyof {1}'.": {
|
||||
"Type '{0}' cannot be used to index type '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2536
|
||||
},
|
||||
@@ -1759,6 +1775,14 @@
|
||||
"category": "Error",
|
||||
"code": 2542
|
||||
},
|
||||
"Duplicate identifier '_newTarget'. Compiler uses variable declaration '_newTarget' to capture 'new.target' meta-property reference.": {
|
||||
"category": "Error",
|
||||
"code": 2543
|
||||
},
|
||||
"Expression resolves to variable declaration '_newTarget' that compiler uses to capture 'new.target' meta-property reference.": {
|
||||
"category": "Error",
|
||||
"code": 2544
|
||||
},
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
"code": 2600
|
||||
@@ -1795,6 +1819,10 @@
|
||||
"category": "Error",
|
||||
"code": 2608
|
||||
},
|
||||
"JSX spread child must be an array type.": {
|
||||
"category": "Error",
|
||||
"code": 2609
|
||||
},
|
||||
"Cannot emit namespaced JSX elements in React": {
|
||||
"category": "Error",
|
||||
"code": 2650
|
||||
@@ -1987,6 +2015,10 @@
|
||||
"category": "Error",
|
||||
"code": 2698
|
||||
},
|
||||
"Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2699
|
||||
},
|
||||
"Rest types may only be created from object types.": {
|
||||
"category": "Error",
|
||||
"code": 2700
|
||||
@@ -1995,6 +2027,18 @@
|
||||
"category": "Error",
|
||||
"code": 2701
|
||||
},
|
||||
"'{0}' only refers to a type, but is being used as a namespace here.": {
|
||||
"category": "Error",
|
||||
"code": 2702
|
||||
},
|
||||
"The operand of a delete operator must be a property reference": {
|
||||
"category": "Error",
|
||||
"code": 2703
|
||||
},
|
||||
"The operand of a delete operator cannot be a read-only property": {
|
||||
"category": "Error",
|
||||
"code": 2704
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@@ -2276,10 +2320,22 @@
|
||||
"category": "Error",
|
||||
"code": 4082
|
||||
},
|
||||
"Type parameter '{0}' of exported type alias has or is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4083
|
||||
},
|
||||
"Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": {
|
||||
"category": "Message",
|
||||
"code": 4090
|
||||
},
|
||||
"Parameter '{0}' of index signature from exported interface has or is using name '{1}' from private module '{2}'.": {
|
||||
"category": "Error",
|
||||
"code": 4091
|
||||
},
|
||||
"Parameter '{0}' of index signature from exported interface has or is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4092
|
||||
},
|
||||
|
||||
"The current host does not support the '{0}' option.": {
|
||||
"category": "Error",
|
||||
@@ -2445,7 +2501,7 @@
|
||||
"category": "Message",
|
||||
"code": 6012
|
||||
},
|
||||
"Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES2015'": {
|
||||
"Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'": {
|
||||
"category": "Message",
|
||||
"code": 6015
|
||||
},
|
||||
@@ -2461,7 +2517,7 @@
|
||||
"category": "Message",
|
||||
"code": 6019
|
||||
},
|
||||
"Compile the project in the given directory.": {
|
||||
"Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'": {
|
||||
"category": "Message",
|
||||
"code": 6020
|
||||
},
|
||||
@@ -2521,6 +2577,10 @@
|
||||
"category": "Message",
|
||||
"code": 6039
|
||||
},
|
||||
"FILE OR DIRECTORY": {
|
||||
"category": "Message",
|
||||
"code": 6040
|
||||
},
|
||||
"Compilation complete. Watching for file changes.": {
|
||||
"category": "Message",
|
||||
"code": 6042
|
||||
@@ -2653,10 +2713,14 @@
|
||||
"category": "Message",
|
||||
"code": 6079
|
||||
},
|
||||
"Specify JSX code generation: 'preserve' or 'react'": {
|
||||
"Specify JSX code generation: 'preserve', 'react-native', or 'react'": {
|
||||
"category": "Message",
|
||||
"code": 6080
|
||||
},
|
||||
"File '{0}' has an unsupported extension, so skipping it.": {
|
||||
"category": "Message",
|
||||
"code": 6081
|
||||
},
|
||||
"Only 'amd' and 'system' modules are supported alongside --{0}.": {
|
||||
"category": "Error",
|
||||
"code": 6082
|
||||
@@ -2709,7 +2773,7 @@
|
||||
"category": "Message",
|
||||
"code": 6094
|
||||
},
|
||||
"Loading module as file / folder, candidate module location '{0}'.": {
|
||||
"Loading module as file / folder, candidate module location '{0}', target file type '{1}'.": {
|
||||
"category": "Message",
|
||||
"code": 6095
|
||||
},
|
||||
@@ -2721,7 +2785,7 @@
|
||||
"category": "Message",
|
||||
"code": 6097
|
||||
},
|
||||
"Loading module '{0}' from 'node_modules' folder.": {
|
||||
"Loading module '{0}' from 'node_modules' folder, target file type '{1}'.": {
|
||||
"category": "Message",
|
||||
"code": 6098
|
||||
},
|
||||
@@ -2917,6 +2981,14 @@
|
||||
"category": "Message",
|
||||
"code": 6146
|
||||
},
|
||||
"Resolution for module '{0}' was found in cache.": {
|
||||
"category": "Message",
|
||||
"code": 6147
|
||||
},
|
||||
"Directory '{0}' does not exist, skipping all lookups in it.": {
|
||||
"category": "Message",
|
||||
"code": 6148
|
||||
},
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
@@ -3133,10 +3205,22 @@
|
||||
"category": "Error",
|
||||
"code": 17009
|
||||
},
|
||||
"Unknown typing option '{0}'.": {
|
||||
"Unknown type acquisition option '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 17010
|
||||
},
|
||||
"'super' must be called before accessing a property of 'super' in the constructor of a derived class.": {
|
||||
"category": "Error",
|
||||
"code": 17011
|
||||
},
|
||||
"'{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{0}'?": {
|
||||
"category": "Error",
|
||||
"code": 17012
|
||||
},
|
||||
"Meta-property '{0}' is only allowed in the body of a function declaration, function expression, or constructor.": {
|
||||
"category": "Error",
|
||||
"code": 17013
|
||||
},
|
||||
|
||||
"Circularity detected while resolving configuration: {0}": {
|
||||
"category": "Error",
|
||||
@@ -3163,23 +3247,19 @@
|
||||
"category": "Message",
|
||||
"code": 90002
|
||||
},
|
||||
"Change 'extends' to 'implements'": {
|
||||
"Change 'extends' to 'implements'.": {
|
||||
"category": "Message",
|
||||
"code": 90003
|
||||
},
|
||||
"Remove unused identifiers": {
|
||||
"Remove declaration for: {0}": {
|
||||
"category": "Message",
|
||||
"code": 90004
|
||||
},
|
||||
"Implement interface on reference": {
|
||||
"category": "Message",
|
||||
"code": 90005
|
||||
},
|
||||
"Implement interface on class": {
|
||||
"Implement interface '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90006
|
||||
},
|
||||
"Implement inherited abstract class": {
|
||||
"Implement inherited abstract class.": {
|
||||
"category": "Message",
|
||||
"code": 90007
|
||||
},
|
||||
@@ -3202,5 +3282,13 @@
|
||||
"Add {0} to existing import declaration from {1}": {
|
||||
"category": "Message",
|
||||
"code": 90015
|
||||
},
|
||||
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8017
|
||||
},
|
||||
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8018
|
||||
}
|
||||
}
|
||||
|
||||
+67
-29
@@ -73,7 +73,7 @@ namespace ts {
|
||||
|
||||
// Emit each output file
|
||||
performance.mark("beforePrint");
|
||||
forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles);
|
||||
forEachEmittedFile(host, emitFile, transformed, emitOnlyDtsFiles);
|
||||
performance.measure("printTime", "beforePrint");
|
||||
|
||||
// Clean up emit nodes on parse tree
|
||||
@@ -88,7 +88,7 @@ namespace ts {
|
||||
sourceMaps: sourceMapDataList
|
||||
};
|
||||
|
||||
function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
|
||||
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) {
|
||||
if (!emitOnlyDtsFiles) {
|
||||
@@ -143,7 +143,7 @@ namespace ts {
|
||||
|
||||
// Write the source map
|
||||
if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
|
||||
writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false);
|
||||
writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles);
|
||||
}
|
||||
|
||||
// Record source map data for the test harness.
|
||||
@@ -152,7 +152,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Write the output file
|
||||
writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM);
|
||||
writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM, sourceFiles);
|
||||
|
||||
// Reset state
|
||||
sourceMap.reset();
|
||||
@@ -660,6 +660,8 @@ namespace ts {
|
||||
return emitAsExpression(<AsExpression>node);
|
||||
case SyntaxKind.NonNullExpression:
|
||||
return emitNonNullExpression(<NonNullExpression>node);
|
||||
case SyntaxKind.MetaProperty:
|
||||
return emitMetaProperty(<MetaProperty>node);
|
||||
|
||||
// JSX
|
||||
case SyntaxKind.JsxElement:
|
||||
@@ -670,8 +672,6 @@ namespace ts {
|
||||
// Transformation nodes
|
||||
case SyntaxKind.PartiallyEmittedExpression:
|
||||
return emitPartiallyEmittedExpression(<PartiallyEmittedExpression>node);
|
||||
case SyntaxKind.RawExpression:
|
||||
return writeLines((<RawExpression>node).text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,6 +1249,12 @@ namespace ts {
|
||||
write("!");
|
||||
}
|
||||
|
||||
function emitMetaProperty(node: MetaProperty) {
|
||||
writeToken(node.keywordToken, node.pos);
|
||||
write(".");
|
||||
emit(node.name);
|
||||
}
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
@@ -1305,28 +1311,28 @@ namespace ts {
|
||||
writeToken(SyntaxKind.OpenParenToken, openParenPos, node);
|
||||
emitExpression(node.expression);
|
||||
writeToken(SyntaxKind.CloseParenToken, node.expression.end, node);
|
||||
emitEmbeddedStatement(node.thenStatement);
|
||||
emitEmbeddedStatement(node, node.thenStatement);
|
||||
if (node.elseStatement) {
|
||||
writeLine();
|
||||
writeLineOrSpace(node);
|
||||
writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node);
|
||||
if (node.elseStatement.kind === SyntaxKind.IfStatement) {
|
||||
write(" ");
|
||||
emit(node.elseStatement);
|
||||
}
|
||||
else {
|
||||
emitEmbeddedStatement(node.elseStatement);
|
||||
emitEmbeddedStatement(node, node.elseStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitDoStatement(node: DoStatement) {
|
||||
write("do");
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
if (isBlock(node.statement)) {
|
||||
write(" ");
|
||||
}
|
||||
else {
|
||||
writeLine();
|
||||
writeLineOrSpace(node);
|
||||
}
|
||||
|
||||
write("while (");
|
||||
@@ -1338,7 +1344,7 @@ namespace ts {
|
||||
write("while (");
|
||||
emitExpression(node.expression);
|
||||
write(")");
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
}
|
||||
|
||||
function emitForStatement(node: ForStatement) {
|
||||
@@ -1351,7 +1357,7 @@ namespace ts {
|
||||
write(";");
|
||||
emitExpressionWithPrefix(" ", node.incrementor);
|
||||
write(")");
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
}
|
||||
|
||||
function emitForInStatement(node: ForInStatement) {
|
||||
@@ -1362,7 +1368,7 @@ namespace ts {
|
||||
write(" in ");
|
||||
emitExpression(node.expression);
|
||||
writeToken(SyntaxKind.CloseParenToken, node.expression.end);
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
}
|
||||
|
||||
function emitForOfStatement(node: ForOfStatement) {
|
||||
@@ -1373,7 +1379,7 @@ namespace ts {
|
||||
write(" of ");
|
||||
emitExpression(node.expression);
|
||||
writeToken(SyntaxKind.CloseParenToken, node.expression.end);
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
}
|
||||
|
||||
function emitForBinding(node: VariableDeclarationList | Expression) {
|
||||
@@ -1409,7 +1415,7 @@ namespace ts {
|
||||
write("with (");
|
||||
emitExpression(node.expression);
|
||||
write(")");
|
||||
emitEmbeddedStatement(node.statement);
|
||||
emitEmbeddedStatement(node, node.statement);
|
||||
}
|
||||
|
||||
function emitSwitchStatement(node: SwitchStatement) {
|
||||
@@ -1437,9 +1443,12 @@ namespace ts {
|
||||
function emitTryStatement(node: TryStatement) {
|
||||
write("try ");
|
||||
emit(node.tryBlock);
|
||||
emit(node.catchClause);
|
||||
if (node.catchClause) {
|
||||
writeLineOrSpace(node);
|
||||
emit(node.catchClause);
|
||||
}
|
||||
if (node.finallyBlock) {
|
||||
writeLine();
|
||||
writeLineOrSpace(node);
|
||||
write("finally ");
|
||||
emit(node.finallyBlock);
|
||||
}
|
||||
@@ -1851,6 +1860,9 @@ namespace ts {
|
||||
function emitJsxExpression(node: JsxExpression) {
|
||||
if (node.expression) {
|
||||
write("{");
|
||||
if (node.dotDotDotToken) {
|
||||
write("...");
|
||||
}
|
||||
emitExpression(node.expression);
|
||||
write("}");
|
||||
}
|
||||
@@ -2031,11 +2043,11 @@ namespace ts {
|
||||
// Skip the helper if it can be bundled but hasn't already been emitted and we
|
||||
// are emitting a bundled module.
|
||||
if (shouldBundle) {
|
||||
if (bundledHelpers[helper.name]) {
|
||||
if (bundledHelpers.get(helper.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bundledHelpers[helper.name] = true;
|
||||
bundledHelpers.set(helper.name, true);
|
||||
}
|
||||
}
|
||||
else if (isBundle) {
|
||||
@@ -2125,8 +2137,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitEmbeddedStatement(node: Statement) {
|
||||
if (isBlock(node)) {
|
||||
function emitEmbeddedStatement(parent: Node, node: Statement) {
|
||||
if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) {
|
||||
write(" ");
|
||||
emit(node);
|
||||
}
|
||||
@@ -2291,6 +2303,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function writeLineOrSpace(node: Node) {
|
||||
if (getEmitFlags(node) & EmitFlags.SingleLine) {
|
||||
write(" ");
|
||||
}
|
||||
else {
|
||||
writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
function writeIfAny(nodes: NodeArray<Node>, text: string) {
|
||||
if (nodes && nodes.length > 0) {
|
||||
write(text);
|
||||
@@ -2487,15 +2508,16 @@ namespace ts {
|
||||
|
||||
function isUniqueName(name: string): boolean {
|
||||
return !resolver.hasGlobalName(name) &&
|
||||
!hasProperty(currentFileIdentifiers, name) &&
|
||||
!hasProperty(generatedNameSet, name);
|
||||
!currentFileIdentifiers.has(name) &&
|
||||
!generatedNameSet.has(name);
|
||||
}
|
||||
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals && hasProperty(node.locals, name)) {
|
||||
if (node.locals) {
|
||||
const local = node.locals.get(name);
|
||||
// We conservatively include alias symbols to cover cases where they're emitted as locals
|
||||
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2544,7 +2566,8 @@ namespace ts {
|
||||
while (true) {
|
||||
const generatedName = baseName + i;
|
||||
if (isUniqueName(generatedName)) {
|
||||
return generatedNameSet[generatedName] = generatedName;
|
||||
generatedNameSet.set(generatedName, generatedName);
|
||||
return generatedName;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -2571,6 +2594,13 @@ namespace ts {
|
||||
return makeUniqueName("class");
|
||||
}
|
||||
|
||||
function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) {
|
||||
if (isIdentifier(node.name)) {
|
||||
return generateNameForNodeCached(node.name);
|
||||
}
|
||||
return makeTempVariableName(TempFlags.Auto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique name from a node.
|
||||
*
|
||||
@@ -2592,6 +2622,10 @@ namespace ts {
|
||||
return generateNameForExportDefault();
|
||||
case SyntaxKind.ClassExpression:
|
||||
return generateNameForClassExpression();
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return generateNameForMethodOrAccessor(<MethodDeclaration | AccessorDeclaration>node);
|
||||
default:
|
||||
return makeTempVariableName(TempFlags.Auto);
|
||||
}
|
||||
@@ -2642,6 +2676,11 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
function generateNameForNodeCached(node: Node) {
|
||||
const nodeId = getNodeId(node);
|
||||
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generated identifier text from a generated identifier.
|
||||
*
|
||||
@@ -2652,8 +2691,7 @@ namespace ts {
|
||||
// Generated names generate unique names based on their original node
|
||||
// and are cached based on that node's id
|
||||
const node = getNodeForGeneratedName(name);
|
||||
const nodeId = getNodeId(node);
|
||||
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
|
||||
return generateNameForNodeCached(node);
|
||||
}
|
||||
else {
|
||||
// Auto, Loop, and Unique names are cached based on their unique
|
||||
|
||||
+75
-55
@@ -1,4 +1,4 @@
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="utilities.ts"/>
|
||||
|
||||
/* @internal */
|
||||
@@ -654,16 +654,16 @@ namespace ts {
|
||||
if (whenFalse) {
|
||||
// second overload
|
||||
node.questionToken = <QuestionToken>questionTokenOrWhenTrue;
|
||||
node.whenTrue = whenTrueOrWhenFalse;
|
||||
node.whenTrue = parenthesizeSubexpressionOfConditionalExpression(whenTrueOrWhenFalse);
|
||||
node.colonToken = <ColonToken>colonTokenOrLocation;
|
||||
node.whenFalse = whenFalse;
|
||||
node.whenFalse = parenthesizeSubexpressionOfConditionalExpression(whenFalse);
|
||||
}
|
||||
else {
|
||||
// first overload
|
||||
node.questionToken = createToken(SyntaxKind.QuestionToken);
|
||||
node.whenTrue = <Expression>questionTokenOrWhenTrue;
|
||||
node.whenTrue = parenthesizeSubexpressionOfConditionalExpression(<Expression>questionTokenOrWhenTrue);
|
||||
node.colonToken = createToken(SyntaxKind.ColonToken);
|
||||
node.whenFalse = whenTrueOrWhenFalse;
|
||||
node.whenFalse = parenthesizeSubexpressionOfConditionalExpression(whenTrueOrWhenFalse);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1317,15 +1317,16 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createJsxExpression(expression: Expression, location?: TextRange) {
|
||||
export function createJsxExpression(expression: Expression, dotDotDotToken: Token<SyntaxKind.DotDotDotToken>, location?: TextRange) {
|
||||
const node = <JsxExpression>createNode(SyntaxKind.JsxExpression, location);
|
||||
node.dotDotDotToken = dotDotDotToken;
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateJsxExpression(node: JsxExpression, expression: Expression) {
|
||||
if (node.expression !== expression) {
|
||||
return updateNode(createJsxExpression(expression, node), node);
|
||||
return updateNode(createJsxExpression(expression, node.dotDotDotToken, node), node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1529,19 +1530,6 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node that emits a string of raw text in an expression position. Raw text is never
|
||||
* transformed, should be ES3 compliant, and should have the same precedence as
|
||||
* PrimaryExpression.
|
||||
*
|
||||
* @param text The raw text of the node.
|
||||
*/
|
||||
export function createRawExpression(text: string) {
|
||||
const node = <RawExpression>createNode(SyntaxKind.RawExpression);
|
||||
node.text = text;
|
||||
return node;
|
||||
}
|
||||
|
||||
// Compound nodes
|
||||
|
||||
export function createComma(left: Expression, right: Expression) {
|
||||
@@ -1677,16 +1665,10 @@ namespace ts {
|
||||
|
||||
function createJsxFactoryExpressionFromEntityName(jsxFactory: EntityName, parent: JsxOpeningLikeElement): Expression {
|
||||
if (isQualifiedName(jsxFactory)) {
|
||||
return createPropertyAccess(
|
||||
createJsxFactoryExpressionFromEntityName(
|
||||
jsxFactory.left,
|
||||
parent
|
||||
),
|
||||
setEmitFlags(
|
||||
getMutableClone(jsxFactory.right),
|
||||
EmitFlags.NoSourceMap
|
||||
)
|
||||
);
|
||||
const left = createJsxFactoryExpressionFromEntityName(jsxFactory.left, parent);
|
||||
const right = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
|
||||
right.text = jsxFactory.right.text;
|
||||
return createPropertyAccess(left, right);
|
||||
}
|
||||
else {
|
||||
return createReactNamespace(jsxFactory.text, parent);
|
||||
@@ -1760,6 +1742,23 @@ namespace ts {
|
||||
|
||||
// Utilities
|
||||
|
||||
export function restoreEnclosingLabel(node: Statement, outermostLabeledStatement: LabeledStatement, afterRestoreLabelCallback?: (node: LabeledStatement) => void): Statement {
|
||||
if (!outermostLabeledStatement) {
|
||||
return node;
|
||||
}
|
||||
const updated = updateLabel(
|
||||
outermostLabeledStatement,
|
||||
outermostLabeledStatement.label,
|
||||
outermostLabeledStatement.statement.kind === SyntaxKind.LabeledStatement
|
||||
? restoreEnclosingLabel(node, <LabeledStatement>outermostLabeledStatement.statement)
|
||||
: node
|
||||
);
|
||||
if (afterRestoreLabelCallback) {
|
||||
afterRestoreLabelCallback(outermostLabeledStatement);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
export interface CallBinding {
|
||||
target: LeftHandSideExpression;
|
||||
thisArg: Expression;
|
||||
@@ -2381,6 +2380,15 @@ namespace ts {
|
||||
return condition;
|
||||
}
|
||||
|
||||
function parenthesizeSubexpressionOfConditionalExpression(e: Expression): Expression {
|
||||
// per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions
|
||||
// so in case when comma expression is introduced as a part of previous transformations
|
||||
// if should be wrapped in parens since comma operator has the lowest precedence
|
||||
return e.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>e).operatorToken.kind === SyntaxKind.CommaToken
|
||||
? createParen(e)
|
||||
: e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an expression in parentheses if it is needed in order to use the expression
|
||||
* as the expression of a NewExpression node.
|
||||
@@ -2638,9 +2646,11 @@ namespace ts {
|
||||
return destEmitNode;
|
||||
}
|
||||
|
||||
function mergeTokenSourceMapRanges(sourceRanges: Map<TextRange>, destRanges: Map<TextRange>) {
|
||||
if (!destRanges) destRanges = createMap<TextRange>();
|
||||
copyProperties(sourceRanges, destRanges);
|
||||
function mergeTokenSourceMapRanges(sourceRanges: TextRange[], destRanges: TextRange[]) {
|
||||
if (!destRanges) destRanges = [];
|
||||
for (const key in sourceRanges) {
|
||||
destRanges[key] = sourceRanges[key];
|
||||
}
|
||||
return destRanges;
|
||||
}
|
||||
|
||||
@@ -2754,7 +2764,7 @@ namespace ts {
|
||||
*/
|
||||
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
|
||||
const emitNode = getOrCreateEmitNode(node);
|
||||
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
|
||||
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = []);
|
||||
tokenSourceMapRanges[token] = range;
|
||||
return node;
|
||||
}
|
||||
@@ -2966,10 +2976,8 @@ namespace ts {
|
||||
* Here we check if alternative name was provided for a given moduleName and return it if possible.
|
||||
*/
|
||||
function tryRenameExternalModule(moduleName: LiteralExpression, sourceFile: SourceFile) {
|
||||
if (sourceFile.renamedDependencies && hasProperty(sourceFile.renamedDependencies, moduleName.text)) {
|
||||
return createLiteral(sourceFile.renamedDependencies[moduleName.text]);
|
||||
}
|
||||
return undefined;
|
||||
const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text);
|
||||
return rename && createLiteral(rename);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3269,7 +3277,7 @@ namespace ts {
|
||||
externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules
|
||||
externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers
|
||||
exportSpecifiers: Map<ExportSpecifier[]>; // export specifiers by name
|
||||
exportedBindings: Map<Identifier[]>; // exported names of local declarations
|
||||
exportedBindings: Identifier[][]; // exported names of local declarations
|
||||
exportedNames: Identifier[]; // all exported names local to module
|
||||
exportEquals: ExportAssignment | undefined; // an export= declaration if one was present
|
||||
hasExportStarsToExportValues: boolean; // whether this module contains export*
|
||||
@@ -3277,8 +3285,8 @@ namespace ts {
|
||||
|
||||
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo {
|
||||
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
|
||||
const exportSpecifiers = createMap<ExportSpecifier[]>();
|
||||
const exportedBindings = createMap<Identifier[]>();
|
||||
const exportSpecifiers = createMultiMap<ExportSpecifier>();
|
||||
const exportedBindings: Identifier[][] = [];
|
||||
const uniqueExports = createMap<boolean>();
|
||||
let exportedNames: Identifier[];
|
||||
let hasExportDefault = false;
|
||||
@@ -3329,18 +3337,18 @@ namespace ts {
|
||||
else {
|
||||
// export { x, y }
|
||||
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
|
||||
if (!uniqueExports[specifier.name.text]) {
|
||||
if (!uniqueExports.get(specifier.name.text)) {
|
||||
const name = specifier.propertyName || specifier.name;
|
||||
multiMapAdd(exportSpecifiers, name.text, specifier);
|
||||
exportSpecifiers.add(name.text, specifier);
|
||||
|
||||
const decl = resolver.getReferencedImportDeclaration(name)
|
||||
|| resolver.getReferencedValueDeclaration(name);
|
||||
|
||||
if (decl) {
|
||||
multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name);
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name);
|
||||
}
|
||||
|
||||
uniqueExports[specifier.name.text] = true;
|
||||
uniqueExports.set(specifier.name.text, true);
|
||||
exportedNames = append(exportedNames, specifier.name);
|
||||
}
|
||||
}
|
||||
@@ -3367,16 +3375,16 @@ namespace ts {
|
||||
if (hasModifier(node, ModifierFlags.Default)) {
|
||||
// export default function() { }
|
||||
if (!hasExportDefault) {
|
||||
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<FunctionDeclaration>node));
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<FunctionDeclaration>node));
|
||||
hasExportDefault = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export function x() { }
|
||||
const name = (<FunctionDeclaration>node).name;
|
||||
if (!uniqueExports[name.text]) {
|
||||
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports[name.text] = true;
|
||||
if (!uniqueExports.get(name.text)) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports.set(name.text, true);
|
||||
exportedNames = append(exportedNames, name);
|
||||
}
|
||||
}
|
||||
@@ -3388,16 +3396,16 @@ namespace ts {
|
||||
if (hasModifier(node, ModifierFlags.Default)) {
|
||||
// export default class { }
|
||||
if (!hasExportDefault) {
|
||||
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<ClassDeclaration>node));
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<ClassDeclaration>node));
|
||||
hasExportDefault = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export class x { }
|
||||
const name = (<ClassDeclaration>node).name;
|
||||
if (!uniqueExports[name.text]) {
|
||||
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports[name.text] = true;
|
||||
if (!uniqueExports.get(name.text)) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports.set(name.text, true);
|
||||
exportedNames = append(exportedNames, name);
|
||||
}
|
||||
}
|
||||
@@ -3418,11 +3426,23 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (!isGeneratedIdentifier(decl.name)) {
|
||||
if (!uniqueExports[decl.name.text]) {
|
||||
uniqueExports[decl.name.text] = true;
|
||||
if (!uniqueExports.get(decl.name.text)) {
|
||||
uniqueExports.set(decl.name.text, true);
|
||||
exportedNames = append(exportedNames, decl.name);
|
||||
}
|
||||
}
|
||||
return exportedNames;
|
||||
}
|
||||
|
||||
/** Use a sparse array as a multi-map. */
|
||||
function multiMapSparseArrayAdd<V>(map: V[][], key: number, value: V): V[] {
|
||||
let values = map[key];
|
||||
if (values) {
|
||||
values.push(value);
|
||||
}
|
||||
else {
|
||||
map[key] = values = [value];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="core.ts" />
|
||||
/// <reference path="core.ts" />
|
||||
/// <reference path="diagnosticInformationMap.generated.ts" />
|
||||
|
||||
namespace ts {
|
||||
@@ -15,7 +15,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Array that is only intended to be pushed to, never read. */
|
||||
interface Push<T> {
|
||||
export interface Push<T> {
|
||||
push(value: T): void;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ts {
|
||||
* Kinds of file that we are currently looking for.
|
||||
* Typically there is one pass with Extensions.TypeScript, then a second pass with Extensions.JavaScript.
|
||||
*/
|
||||
const enum Extensions {
|
||||
enum Extensions {
|
||||
TypeScript, /** '.ts', '.tsx', or '.d.ts' */
|
||||
JavaScript, /** '.js' or '.jsx' */
|
||||
DtsOnly /** Only '.d.ts' */
|
||||
@@ -47,11 +47,6 @@ namespace ts {
|
||||
return resolved.path;
|
||||
}
|
||||
|
||||
/** Create Resolved from a file with unknown extension. */
|
||||
function resolvedFromAnyFile(path: string): Resolved | undefined {
|
||||
return { path, extension: extensionFromPath(path) };
|
||||
}
|
||||
|
||||
/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
|
||||
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
|
||||
return { resolvedFileName: path, extension, isExternalLibraryImport };
|
||||
@@ -71,7 +66,8 @@ namespace ts {
|
||||
traceEnabled: boolean;
|
||||
}
|
||||
|
||||
function tryReadTypesSection(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
|
||||
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
|
||||
function tryReadPackageJsonMainOrTypes(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
|
||||
const jsonContent = readJson(packageJsonPath, state.host);
|
||||
|
||||
switch (extensions) {
|
||||
@@ -153,6 +149,7 @@ namespace ts {
|
||||
if (host.directoryExists(atTypes)) {
|
||||
(typeRoots || (typeRoots = [])).push(atTypes);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
return typeRoots;
|
||||
}
|
||||
@@ -220,9 +217,13 @@ namespace ts {
|
||||
return forEach(typeRoots, typeRoot => {
|
||||
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
|
||||
const candidateDirectory = getDirectoryPath(candidate);
|
||||
const directoryExists = directoryProbablyExists(candidateDirectory, host);
|
||||
if (!directoryExists && traceEnabled) {
|
||||
trace(host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidateDirectory);
|
||||
}
|
||||
return resolvedTypeScriptOnly(
|
||||
loadNodeModuleFromDirectory(Extensions.DtsOnly, candidate, failedLookupLocations,
|
||||
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState));
|
||||
!directoryExists, moduleResolutionState));
|
||||
});
|
||||
}
|
||||
else {
|
||||
@@ -241,7 +242,8 @@ namespace ts {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
|
||||
}
|
||||
resolvedFile = resolvedTypeScriptOnly(loadModuleFromNodeModules(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState));
|
||||
const result = loadModuleFromNodeModules(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState, /*cache*/ undefined);
|
||||
resolvedFile = resolvedTypeScriptOnly(result && result.value);
|
||||
if (!resolvedFile && traceEnabled) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
|
||||
}
|
||||
@@ -293,33 +295,172 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
/**
|
||||
* Cached module resolutions per containing directory.
|
||||
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
|
||||
*/
|
||||
export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache {
|
||||
getOrCreateCacheForDirectory(directoryName: string): Map<ResolvedModuleWithFailedLookupLocations>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stored map from non-relative module name to a table: directory -> result of module lookup in this directory
|
||||
* We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
|
||||
*/
|
||||
export interface NonRelativeModuleNameResolutionCache {
|
||||
getOrCreateCacheForModuleName(nonRelativeModuleName: string): PerModuleNameCache;
|
||||
}
|
||||
|
||||
export interface PerModuleNameCache {
|
||||
get(directory: string): ResolvedModuleWithFailedLookupLocations;
|
||||
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
|
||||
}
|
||||
|
||||
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
|
||||
const directoryToModuleNameMap = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
const moduleNameToDirectoryMap = createMap<PerModuleNameCache>();
|
||||
|
||||
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
|
||||
|
||||
function getOrCreateCacheForDirectory(directoryName: string) {
|
||||
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
|
||||
let perFolderCache = directoryToModuleNameMap.get(path);
|
||||
if (!perFolderCache) {
|
||||
perFolderCache = createMap<ResolvedModuleWithFailedLookupLocations>();
|
||||
directoryToModuleNameMap.set(path, perFolderCache);
|
||||
}
|
||||
return perFolderCache;
|
||||
}
|
||||
|
||||
function getOrCreateCacheForModuleName(nonRelativeModuleName: string) {
|
||||
if (!moduleHasNonRelativeName(nonRelativeModuleName)) {
|
||||
return undefined;
|
||||
}
|
||||
let perModuleNameCache = moduleNameToDirectoryMap.get(nonRelativeModuleName);
|
||||
if (!perModuleNameCache) {
|
||||
perModuleNameCache = createPerModuleNameCache();
|
||||
moduleNameToDirectoryMap.set(nonRelativeModuleName, perModuleNameCache);
|
||||
}
|
||||
return perModuleNameCache;
|
||||
}
|
||||
|
||||
function createPerModuleNameCache(): PerModuleNameCache {
|
||||
const directoryPathMap = createFileMap<ResolvedModuleWithFailedLookupLocations>();
|
||||
|
||||
return { get, set };
|
||||
|
||||
function get(directory: string): ResolvedModuleWithFailedLookupLocations {
|
||||
return directoryPathMap.get(toPath(directory, currentDirectory, getCanonicalFileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* At first this function add entry directory -> module resolution result to the table.
|
||||
* Then it computes the set of parent folders for 'directory' that should have the same module resolution result
|
||||
* and for every parent folder in set it adds entry: parent -> module resolution. .
|
||||
* Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts.
|
||||
* Set of parent folders that should have the same result will be:
|
||||
* [
|
||||
* /a/b/c/d, /a/b/c, /a/b
|
||||
* ]
|
||||
* this means that request for module resolution from file in any of these folder will be immediately found in cache.
|
||||
*/
|
||||
function set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void {
|
||||
const path = toPath(directory, currentDirectory, getCanonicalFileName);
|
||||
// if entry is already in cache do nothing
|
||||
if (directoryPathMap.contains(path)) {
|
||||
return;
|
||||
}
|
||||
directoryPathMap.set(path, result);
|
||||
|
||||
const resolvedFileName = result.resolvedModule && result.resolvedModule.resolvedFileName;
|
||||
// find common prefix between directory and resolved file name
|
||||
// this common prefix should be the shorted path that has the same resolution
|
||||
// directory: /a/b/c/d/e
|
||||
// resolvedFileName: /a/b/foo.d.ts
|
||||
const commonPrefix = getCommonPrefix(path, resolvedFileName);
|
||||
let current = path;
|
||||
while (true) {
|
||||
const parent = getDirectoryPath(current);
|
||||
if (parent === current || directoryPathMap.contains(parent)) {
|
||||
break;
|
||||
}
|
||||
directoryPathMap.set(parent, result);
|
||||
current = parent;
|
||||
|
||||
if (current == commonPrefix) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCommonPrefix(directory: Path, resolution: string) {
|
||||
if (resolution === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName);
|
||||
|
||||
// find first position where directory and resolution differs
|
||||
let i = 0;
|
||||
while (i < Math.min(directory.length, resolutionDirectory.length) && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
// find last directory separator before position i
|
||||
const sep = directory.lastIndexOf(directorySeparator, i);
|
||||
if (sep < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return directory.substr(0, sep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(compilerOptions, host);
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
|
||||
}
|
||||
const containingDirectory = getDirectoryPath(containingFile);
|
||||
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory);
|
||||
let result = perFolderCache && perFolderCache.get(moduleName);
|
||||
|
||||
let moduleResolution = compilerOptions.moduleResolution;
|
||||
if (moduleResolution === undefined) {
|
||||
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
|
||||
if (result) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
|
||||
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
|
||||
let moduleResolution = compilerOptions.moduleResolution;
|
||||
if (moduleResolution === undefined) {
|
||||
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result: ResolvedModuleWithFailedLookupLocations;
|
||||
switch (moduleResolution) {
|
||||
case ModuleResolutionKind.NodeJs:
|
||||
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
|
||||
break;
|
||||
case ModuleResolutionKind.Classic:
|
||||
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
|
||||
break;
|
||||
switch (moduleResolution) {
|
||||
case ModuleResolutionKind.NodeJs:
|
||||
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache);
|
||||
break;
|
||||
case ModuleResolutionKind.Classic:
|
||||
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache);
|
||||
break;
|
||||
}
|
||||
|
||||
if (perFolderCache) {
|
||||
perFolderCache.set(moduleName, result);
|
||||
// put result in per-module name cache
|
||||
const perModuleNameCache = cache.getOrCreateCacheForModuleName(moduleName);
|
||||
if (perModuleNameCache) {
|
||||
perModuleNameCache.set(containingDirectory, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (traceEnabled) {
|
||||
@@ -542,7 +683,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
|
||||
const containingDirectory = getDirectoryPath(containingFile);
|
||||
const traceEnabled = isTraceEnabled(compilerOptions, host);
|
||||
|
||||
@@ -550,30 +691,30 @@ namespace ts {
|
||||
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled };
|
||||
|
||||
const result = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript);
|
||||
if (result) {
|
||||
const { resolved, isExternalLibraryImport } = result;
|
||||
if (result && result.value) {
|
||||
const { resolved, isExternalLibraryImport } = result.value;
|
||||
return createResolvedModuleWithFailedLookupLocations(resolved, isExternalLibraryImport, failedLookupLocations);
|
||||
}
|
||||
return { resolvedModule: undefined, failedLookupLocations };
|
||||
|
||||
function tryResolve(extensions: Extensions): { resolved: Resolved, isExternalLibraryImport: boolean } | undefined {
|
||||
function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, isExternalLibraryImport: boolean }> {
|
||||
const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, nodeLoadModuleByRelativeName, failedLookupLocations, state);
|
||||
if (resolved) {
|
||||
return { resolved, isExternalLibraryImport: false };
|
||||
return toSearchResult({ resolved, isExternalLibraryImport: false });
|
||||
}
|
||||
|
||||
if (moduleHasNonRelativeName(moduleName)) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
|
||||
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
|
||||
}
|
||||
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state);
|
||||
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state, cache);
|
||||
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
|
||||
return resolved && { resolved: { path: realpath(resolved.path, host, traceEnabled), extension: resolved.extension }, isExternalLibraryImport: true };
|
||||
return resolved && { value: resolved.value && { resolved: { path: realpath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension }, isExternalLibraryImport: true } };
|
||||
}
|
||||
else {
|
||||
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
|
||||
const resolved = nodeLoadModuleByRelativeName(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
|
||||
return resolved && { resolved, isExternalLibraryImport: false };
|
||||
return resolved && toSearchResult({ resolved, isExternalLibraryImport: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,11 +733,33 @@ namespace ts {
|
||||
|
||||
function nodeLoadModuleByRelativeName(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
|
||||
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]);
|
||||
}
|
||||
|
||||
const resolvedFromFile = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
|
||||
return resolvedFromFile || loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (!pathEndsWithDirectorySeparator(candidate)) {
|
||||
if (!onlyRecordFailures) {
|
||||
const parentOfCandidate = getDirectoryPath(candidate);
|
||||
if (!directoryProbablyExists(parentOfCandidate, state.host)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, parentOfCandidate);
|
||||
}
|
||||
onlyRecordFailures = true;
|
||||
}
|
||||
}
|
||||
const resolvedFromFile = loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (resolvedFromFile) {
|
||||
return resolvedFromFile;
|
||||
}
|
||||
}
|
||||
if (!onlyRecordFailures) {
|
||||
const candidateExists = directoryProbablyExists(candidate, state.host);
|
||||
if (!candidateExists) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidate);
|
||||
}
|
||||
onlyRecordFailures = true;
|
||||
}
|
||||
}
|
||||
return loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -655,19 +818,21 @@ namespace ts {
|
||||
|
||||
/** Return the file if it exists. */
|
||||
function tryFile(fileName: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
|
||||
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
|
||||
if (!onlyRecordFailures) {
|
||||
if (state.host.fileExists(fileName)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
|
||||
}
|
||||
}
|
||||
failedLookupLocations.push(fileName);
|
||||
return undefined;
|
||||
}
|
||||
failedLookupLocations.push(fileName);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
|
||||
@@ -678,18 +843,23 @@ namespace ts {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
const typesFile = tryReadTypesSection(extensions, packageJsonPath, candidate, state);
|
||||
if (typesFile) {
|
||||
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
|
||||
const mainOrTypesFile = tryReadPackageJsonMainOrTypes(extensions, packageJsonPath, candidate, state);
|
||||
if (mainOrTypesFile) {
|
||||
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(mainOrTypesFile), state.host);
|
||||
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
|
||||
const fromFile = tryFile(typesFile, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (fromFile) {
|
||||
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
|
||||
return resolvedFromAnyFile(fromFile);
|
||||
const fromExactFile = tryFile(mainOrTypesFile, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (fromExactFile) {
|
||||
const resolved = fromExactFile && resolvedIfExtensionMatches(extensions, fromExactFile);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromExactFile);
|
||||
}
|
||||
}
|
||||
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (x) {
|
||||
return x;
|
||||
const resolved = tryAddingExtensions(mainOrTypesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -699,7 +869,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
if (directoryExists && state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
|
||||
}
|
||||
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
|
||||
@@ -709,66 +879,116 @@ namespace ts {
|
||||
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
|
||||
}
|
||||
|
||||
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
|
||||
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
|
||||
const extension = tryGetExtensionFromPath(path);
|
||||
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
|
||||
}
|
||||
|
||||
/** True if `extension` is one of the supported `extensions`. */
|
||||
function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
|
||||
switch (extensions) {
|
||||
case Extensions.JavaScript:
|
||||
return extension === Extension.Js || extension === Extension.Jsx;
|
||||
case Extensions.TypeScript:
|
||||
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts;
|
||||
case Extensions.DtsOnly:
|
||||
return extension === Extension.Dts;
|
||||
}
|
||||
}
|
||||
|
||||
function pathToPackageJson(directory: string): string {
|
||||
return combinePaths(directory, "package.json");
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
|
||||
const nodeModulesFolder = combinePaths(directory, "node_modules");
|
||||
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
|
||||
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
|
||||
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
|
||||
|
||||
return loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
|
||||
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
|
||||
return loadModuleFromNodeModulesWorker(extensions, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ false);
|
||||
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache): SearchResult<Resolved> {
|
||||
return loadModuleFromNodeModulesWorker(extensions, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ false, cache);
|
||||
}
|
||||
function loadModuleFromNodeModulesAtTypes(moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
|
||||
function loadModuleFromNodeModulesAtTypes(moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): SearchResult<Resolved> {
|
||||
// Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
|
||||
return loadModuleFromNodeModulesWorker(Extensions.DtsOnly, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ true);
|
||||
return loadModuleFromNodeModulesWorker(Extensions.DtsOnly, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ true, /*cache*/ undefined);
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModulesWorker(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, typesOnly: boolean): Resolved | undefined {
|
||||
function loadModuleFromNodeModulesWorker(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, typesOnly: boolean, cache: NonRelativeModuleNameResolutionCache): SearchResult<Resolved> {
|
||||
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
|
||||
return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
|
||||
if (getBaseFileName(ancestorDirectory) !== "node_modules") {
|
||||
return loadModuleFromNodeModulesOneLevel(extensions, moduleName, ancestorDirectory, failedLookupLocations, state, typesOnly);
|
||||
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state.traceEnabled, state.host);
|
||||
if (resolutionFromCache) {
|
||||
return resolutionFromCache;
|
||||
}
|
||||
return toSearchResult(loadModuleFromNodeModulesOneLevel(extensions, moduleName, ancestorDirectory, failedLookupLocations, state, typesOnly));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Load a module from a single node_modules directory, but not from any ancestors' node_modules directories. */
|
||||
function loadModuleFromNodeModulesOneLevel(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push<string>, state: ModuleResolutionState, typesOnly = false): Resolved | undefined {
|
||||
const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder(extensions, moduleName, directory, failedLookupLocations, state);
|
||||
const nodeModulesFolder = combinePaths(directory, "node_modules");
|
||||
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
|
||||
if (!nodeModulesFolderExists && state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesFolder);
|
||||
}
|
||||
|
||||
const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder(extensions, moduleName, nodeModulesFolder, nodeModulesFolderExists, failedLookupLocations, state);
|
||||
if (packageResult) {
|
||||
return packageResult;
|
||||
}
|
||||
if (extensions !== Extensions.JavaScript) {
|
||||
return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, combinePaths("@types", moduleName), directory, failedLookupLocations, state);
|
||||
const nodeModulesAtTypes = combinePaths(nodeModulesFolder, "@types");
|
||||
let nodeModulesAtTypesExists = nodeModulesFolderExists;
|
||||
if (nodeModulesFolderExists && !directoryProbablyExists(nodeModulesAtTypes, state.host)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesAtTypes);
|
||||
}
|
||||
nodeModulesAtTypesExists = false;
|
||||
}
|
||||
return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, moduleName, nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state);
|
||||
}
|
||||
}
|
||||
|
||||
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, traceEnabled: boolean, host: ModuleResolutionHost): SearchResult<Resolved> {
|
||||
const result = cache && cache.get(containingDirectory);
|
||||
if (result) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName)
|
||||
}
|
||||
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension } };
|
||||
}
|
||||
}
|
||||
|
||||
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache): ResolvedModuleWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(compilerOptions, host);
|
||||
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled };
|
||||
const failedLookupLocations: string[] = [];
|
||||
const containingDirectory = getDirectoryPath(containingFile);
|
||||
|
||||
const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript);
|
||||
return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ false, failedLookupLocations);
|
||||
return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations);
|
||||
|
||||
function tryResolve(extensions: Extensions): Resolved | undefined {
|
||||
function tryResolve(extensions: Extensions): SearchResult<Resolved> {
|
||||
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, state);
|
||||
if (resolvedUsingSettings) {
|
||||
return resolvedUsingSettings;
|
||||
return { value: resolvedUsingSettings };
|
||||
}
|
||||
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
|
||||
|
||||
if (moduleHasNonRelativeName(moduleName)) {
|
||||
// Climb up parent directories looking for a module.
|
||||
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
|
||||
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, traceEnabled, host);
|
||||
if (resolutionFromCache) {
|
||||
return resolutionFromCache;
|
||||
}
|
||||
const searchName = normalizePath(combinePaths(directory, moduleName));
|
||||
return loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state);
|
||||
return toSearchResult(loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state));
|
||||
});
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
@@ -780,7 +1000,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
|
||||
return loadModuleFromFile(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
|
||||
return toSearchResult(loadModuleFromFile(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -801,8 +1021,28 @@ namespace ts {
|
||||
return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator
|
||||
* that search fails and we should try another option.
|
||||
* However this does not allow us to represent final result that should be used instead of further searching (i.e. a final result that was found in cache).
|
||||
* SearchResult is used to deal with this issue, its values represents following outcomes:
|
||||
* - undefined - not found, continue searching
|
||||
* - { value: undefined } - not found - stop searching
|
||||
* - { value: <some-value> } - found - stop searching
|
||||
*/
|
||||
type SearchResult<T> = { value: T | undefined } | undefined;
|
||||
|
||||
/**
|
||||
* Wraps value to SearchResult.
|
||||
* @returns undefined if value is undefined or { value } otherwise
|
||||
*/
|
||||
function toSearchResult<T>(value: T | undefined): SearchResult<T> {
|
||||
return value !== undefined ? { value } : undefined;
|
||||
}
|
||||
|
||||
|
||||
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
|
||||
function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T | undefined): T | undefined {
|
||||
function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => SearchResult<T>): SearchResult<T> {
|
||||
while (true) {
|
||||
const result = callback(directory);
|
||||
if (result !== undefined) {
|
||||
|
||||
+62
-21
@@ -198,6 +198,8 @@ namespace ts {
|
||||
visitNode(cbNode, (<AsExpression>node).type);
|
||||
case SyntaxKind.NonNullExpression:
|
||||
return visitNode(cbNode, (<NonNullExpression>node).expression);
|
||||
case SyntaxKind.MetaProperty:
|
||||
return visitNode(cbNode, (<MetaProperty>node).name);
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return visitNode(cbNode, (<ConditionalExpression>node).condition) ||
|
||||
visitNode(cbNode, (<ConditionalExpression>node).questionToken) ||
|
||||
@@ -373,7 +375,8 @@ namespace ts {
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
return visitNode(cbNode, (<JsxSpreadAttribute>node).expression);
|
||||
case SyntaxKind.JsxExpression:
|
||||
return visitNode(cbNode, (<JsxExpression>node).expression);
|
||||
return visitNode(cbNode, (node as JsxExpression).dotDotDotToken) ||
|
||||
visitNode(cbNode, (node as JsxExpression).expression);
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
return visitNode(cbNode, (<JsxClosingElement>node).tagName);
|
||||
|
||||
@@ -418,6 +421,8 @@ namespace ts {
|
||||
return visitNode(cbNode, (<JSDocReturnTag>node).typeExpression);
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return visitNode(cbNode, (<JSDocAugmentsTag>node).typeExpression);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return visitNodes(cbNodes, (<JSDocTemplateTag>node).typeParameters);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
@@ -681,7 +686,7 @@ namespace ts {
|
||||
|
||||
|
||||
function addJSDocComment<T extends Node>(node: T): T {
|
||||
const comments = getJsDocCommentsFromText(node, sourceFile.text);
|
||||
const comments = getJSDocCommentRanges(node, sourceFile.text);
|
||||
if (comments) {
|
||||
for (const comment of comments) {
|
||||
const jsDoc = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
|
||||
@@ -689,10 +694,10 @@ namespace ts {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!node.jsDocComments) {
|
||||
node.jsDocComments = [];
|
||||
if (!node.jsDoc) {
|
||||
node.jsDoc = [];
|
||||
}
|
||||
node.jsDocComments.push(jsDoc);
|
||||
node.jsDoc.push(jsDoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,11 +724,11 @@ namespace ts {
|
||||
const saveParent = parent;
|
||||
parent = n;
|
||||
forEachChild(n, visitNode);
|
||||
if (n.jsDocComments) {
|
||||
for (const jsDocComment of n.jsDocComments) {
|
||||
jsDocComment.parent = n;
|
||||
parent = jsDocComment;
|
||||
forEachChild(jsDocComment, visitNode);
|
||||
if (n.jsDoc) {
|
||||
for (const jsDoc of n.jsDoc) {
|
||||
jsDoc.parent = n;
|
||||
parent = jsDoc;
|
||||
forEachChild(jsDoc, visitNode);
|
||||
}
|
||||
}
|
||||
parent = saveParent;
|
||||
@@ -1129,7 +1134,11 @@ namespace ts {
|
||||
|
||||
function internIdentifier(text: string): string {
|
||||
text = escapeIdentifier(text);
|
||||
return identifiers[text] || (identifiers[text] = text);
|
||||
let identifier = identifiers.get(text);
|
||||
if (identifier === undefined) {
|
||||
identifiers.set(text, identifier = text);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
// An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues
|
||||
@@ -1676,8 +1685,8 @@ namespace ts {
|
||||
// Method declarations are not necessarily reusable. An object-literal
|
||||
// may have a method calls "constructor(...)" and we must reparse that
|
||||
// into an actual .ConstructorDeclaration.
|
||||
let methodDeclaration = <MethodDeclaration>node;
|
||||
let nameIsConstructor = methodDeclaration.name.kind === SyntaxKind.Identifier &&
|
||||
const methodDeclaration = <MethodDeclaration>node;
|
||||
const nameIsConstructor = methodDeclaration.name.kind === SyntaxKind.Identifier &&
|
||||
(<Identifier>methodDeclaration.name).originalKeywordKind === SyntaxKind.ConstructorKeyword;
|
||||
|
||||
return !nameIsConstructor;
|
||||
@@ -2505,6 +2514,7 @@ namespace ts {
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
// If these are followed by a dot, then parse these out as a dotted type reference instead.
|
||||
const node = tryParse(parseKeywordAndNoDot);
|
||||
return node || parseTypeReference();
|
||||
@@ -2556,11 +2566,14 @@ namespace ts {
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.BarToken:
|
||||
case SyntaxKind.AmpersandToken:
|
||||
case SyntaxKind.NewKeyword:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
return true;
|
||||
case SyntaxKind.MinusToken:
|
||||
return lookAhead(nextTokenIsNumericLiteral);
|
||||
@@ -2615,6 +2628,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseUnionOrIntersectionType(kind: SyntaxKind, parseConstituentType: () => TypeNode, operator: SyntaxKind): TypeNode {
|
||||
parseOptional(operator);
|
||||
let type = parseConstituentType();
|
||||
if (token() === operator) {
|
||||
const types = createNodeArray<TypeNode>([type], type.pos);
|
||||
@@ -3909,6 +3923,7 @@ namespace ts {
|
||||
|
||||
parseExpected(SyntaxKind.OpenBraceToken);
|
||||
if (token() !== SyntaxKind.CloseBraceToken) {
|
||||
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
node.expression = parseAssignmentExpressionOrHigher();
|
||||
}
|
||||
if (inExpressionContext) {
|
||||
@@ -4324,15 +4339,22 @@ namespace ts {
|
||||
return isIdentifier() ? parseIdentifier() : undefined;
|
||||
}
|
||||
|
||||
function parseNewExpression(): NewExpression {
|
||||
const node = <NewExpression>createNode(SyntaxKind.NewExpression);
|
||||
function parseNewExpression(): NewExpression | MetaProperty {
|
||||
const fullStart = scanner.getStartPos();
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
if (parseOptional(SyntaxKind.DotToken)) {
|
||||
const node = <MetaProperty>createNode(SyntaxKind.MetaProperty, fullStart);
|
||||
node.keywordToken = SyntaxKind.NewKeyword;
|
||||
node.name = parseIdentifierName();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
const node = <NewExpression>createNode(SyntaxKind.NewExpression, fullStart);
|
||||
node.expression = parseMemberExpressionOrHigher();
|
||||
node.typeArguments = tryParse(parseTypeArgumentsInExpression);
|
||||
if (node.typeArguments || token() === SyntaxKind.OpenParenToken) {
|
||||
node.arguments = parseArgumentList();
|
||||
}
|
||||
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
@@ -6021,6 +6043,7 @@ namespace ts {
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
return parseTokenNode<JSDocType>();
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
@@ -6332,7 +6355,7 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
const asterisk = scanner.getTokenText();
|
||||
if (state === JSDocState.SawAsterisk) {
|
||||
if (state === JSDocState.SawAsterisk || state === JSDocState.SavingComments) {
|
||||
// If we've already seen an asterisk, then we can no longer parse a tag on this line
|
||||
state = JSDocState.SavingComments;
|
||||
pushComment(asterisk);
|
||||
@@ -6353,7 +6376,10 @@ namespace ts {
|
||||
case SyntaxKind.WhitespaceTrivia:
|
||||
// only collect whitespace if we're already saving comments or have just crossed the comment indent margin
|
||||
const whitespace = scanner.getTokenText();
|
||||
if (state === JSDocState.SavingComments || margin !== undefined && indent + whitespace.length > margin) {
|
||||
if (state === JSDocState.SavingComments) {
|
||||
comments.push(whitespace);
|
||||
}
|
||||
else if (margin !== undefined && indent + whitespace.length > margin) {
|
||||
comments.push(whitespace.slice(margin - indent - 1));
|
||||
}
|
||||
indent += whitespace.length;
|
||||
@@ -6361,6 +6387,8 @@ namespace ts {
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
break;
|
||||
default:
|
||||
// anything other than whitespace or asterisk at the beginning of the line starts the comment text
|
||||
state = JSDocState.SavingComments;
|
||||
pushComment(scanner.getTokenText());
|
||||
break;
|
||||
}
|
||||
@@ -6426,6 +6454,9 @@ namespace ts {
|
||||
let tag: JSDocTag;
|
||||
if (tagName) {
|
||||
switch (tagName.text) {
|
||||
case "augments":
|
||||
tag = parseAugmentsTag(atToken, tagName);
|
||||
break;
|
||||
case "param":
|
||||
tag = parseParamTag(atToken, tagName);
|
||||
break;
|
||||
@@ -6642,6 +6673,16 @@ namespace ts {
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
|
||||
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos);
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
result.typeExpression = typeExpression;
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
skipWhitespace();
|
||||
@@ -6954,8 +6995,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
forEachChild(node, visitNode, visitArray);
|
||||
if (node.jsDocComments) {
|
||||
for (const jsDocComment of node.jsDocComments) {
|
||||
if (node.jsDoc) {
|
||||
for (const jsDocComment of node.jsDoc) {
|
||||
forEachChild(jsDocComment, visitNode, visitArray);
|
||||
}
|
||||
}
|
||||
@@ -7381,7 +7422,7 @@ namespace ts {
|
||||
if (position >= array.pos && position < array.end) {
|
||||
// position was in this array. Search through this array to see if we find a
|
||||
// viable element.
|
||||
for (let i = 0, n = array.length; i < n; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const child = array[i];
|
||||
if (child) {
|
||||
if (child.pos === position) {
|
||||
|
||||
+10
-10
@@ -27,8 +27,8 @@ namespace ts.performance {
|
||||
*/
|
||||
export function mark(markName: string) {
|
||||
if (enabled) {
|
||||
marks[markName] = timestamp();
|
||||
counts[markName] = (counts[markName] || 0) + 1;
|
||||
marks.set(markName, timestamp());
|
||||
counts.set(markName, (counts.get(markName) || 0) + 1);
|
||||
profilerEvent(markName);
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,9 @@ namespace ts.performance {
|
||||
*/
|
||||
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
|
||||
if (enabled) {
|
||||
const end = endMarkName && marks[endMarkName] || timestamp();
|
||||
const start = startMarkName && marks[startMarkName] || profilerStart;
|
||||
measures[measureName] = (measures[measureName] || 0) + (end - start);
|
||||
const end = endMarkName && marks.get(endMarkName) || timestamp();
|
||||
const start = startMarkName && marks.get(startMarkName) || profilerStart;
|
||||
measures.set(measureName, (measures.get(measureName) || 0) + (end - start));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace ts.performance {
|
||||
* @param markName The name of the mark.
|
||||
*/
|
||||
export function getCount(markName: string) {
|
||||
return counts && counts[markName] || 0;
|
||||
return counts && counts.get(markName) || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ namespace ts.performance {
|
||||
* @param measureName The name of the measure whose durations should be accumulated.
|
||||
*/
|
||||
export function getDuration(measureName: string) {
|
||||
return measures && measures[measureName] || 0;
|
||||
return measures && measures.get(measureName) || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,9 +74,9 @@ namespace ts.performance {
|
||||
* @param cb The action to perform for each measure
|
||||
*/
|
||||
export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
|
||||
for (const key in measures) {
|
||||
cb(key, measures[key]);
|
||||
}
|
||||
measures.forEach((measure, key) => {
|
||||
cb(key, measure);
|
||||
});
|
||||
}
|
||||
|
||||
/** Enables (and resets) performance measurements for the compiler. */
|
||||
|
||||
+42
-31
@@ -40,7 +40,8 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
|
||||
const n = Math.min(commonPathComponents.length, sourcePathComponents.length);
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
|
||||
if (i === 0) {
|
||||
// Failed to find any common path component
|
||||
@@ -110,11 +111,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function directoryExists(directoryPath: string): boolean {
|
||||
if (directoryPath in existingDirectories) {
|
||||
if (existingDirectories.has(directoryPath)) {
|
||||
return true;
|
||||
}
|
||||
if (sys.directoryExists(directoryPath)) {
|
||||
existingDirectories[directoryPath] = true;
|
||||
existingDirectories.set(directoryPath, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -138,11 +139,11 @@ namespace ts {
|
||||
const hash = sys.createHash(data);
|
||||
const mtimeBefore = sys.getModifiedTime(fileName);
|
||||
|
||||
if (mtimeBefore && fileName in outputFingerprints) {
|
||||
const fingerprint = outputFingerprints[fileName];
|
||||
|
||||
if (mtimeBefore) {
|
||||
const fingerprint = outputFingerprints.get(fileName);
|
||||
// If output has not been changed, and the file has no external modification
|
||||
if (fingerprint.byteOrderMark === writeByteOrderMark &&
|
||||
if (fingerprint &&
|
||||
fingerprint.byteOrderMark === writeByteOrderMark &&
|
||||
fingerprint.hash === hash &&
|
||||
fingerprint.mtime.getTime() === mtimeBefore.getTime()) {
|
||||
return;
|
||||
@@ -153,11 +154,11 @@ namespace ts {
|
||||
|
||||
const mtimeAfter = sys.getModifiedTime(fileName);
|
||||
|
||||
outputFingerprints[fileName] = {
|
||||
outputFingerprints.set(fileName, {
|
||||
hash,
|
||||
byteOrderMark: writeByteOrderMark,
|
||||
mtime: mtimeAfter
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
|
||||
@@ -277,9 +278,13 @@ namespace ts {
|
||||
const resolutions: T[] = [];
|
||||
const cache = createMap<T>();
|
||||
for (const name of names) {
|
||||
const result = name in cache
|
||||
? cache[name]
|
||||
: cache[name] = loader(name, containingFile);
|
||||
let result: T;
|
||||
if (cache.has(name)) {
|
||||
result = cache.get(name);
|
||||
}
|
||||
else {
|
||||
cache.set(name, result = loader(name, containingFile));
|
||||
}
|
||||
resolutions.push(result);
|
||||
}
|
||||
return resolutions;
|
||||
@@ -325,6 +330,7 @@ namespace ts {
|
||||
// Map storing if there is emit blocking diagnostics for given input
|
||||
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
|
||||
|
||||
let moduleResolutionCache: ModuleResolutionCache;
|
||||
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
|
||||
if (host.resolveModuleNames) {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
|
||||
@@ -338,7 +344,8 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
else {
|
||||
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
|
||||
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
|
||||
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
|
||||
}
|
||||
|
||||
@@ -364,7 +371,8 @@ namespace ts {
|
||||
|
||||
if (typeReferences.length) {
|
||||
// This containingFilename needs to match with the one used in managed-side
|
||||
const containingFilename = combinePaths(host.getCurrentDirectory(), "__inferred type names__.ts");
|
||||
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
|
||||
const containingFilename = combinePaths(containingDirectory, "__inferred type names__.ts");
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
|
||||
for (let i = 0; i < typeReferences.length; i++) {
|
||||
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
|
||||
@@ -390,6 +398,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
|
||||
moduleResolutionCache = undefined;
|
||||
|
||||
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
|
||||
oldProgram = undefined;
|
||||
|
||||
@@ -428,7 +439,7 @@ namespace ts {
|
||||
|
||||
function getCommonSourceDirectory() {
|
||||
if (commonSourceDirectory === undefined) {
|
||||
const emittedFiles = filterSourceFilesInDirectory(files, isSourceFileFromExternalLibrary);
|
||||
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary));
|
||||
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
|
||||
// If a rootDir is specified and is valid use it as the commonSourceDirectory
|
||||
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
|
||||
@@ -453,7 +464,7 @@ namespace ts {
|
||||
classifiableNames = createMap<string>();
|
||||
|
||||
for (const sourceFile of files) {
|
||||
copyProperties(sourceFile.classifiableNames, classifiableNames);
|
||||
copyEntries(sourceFile.classifiableNames, classifiableNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,7 +705,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// update fileName -> file mapping
|
||||
for (let i = 0, len = newSourceFiles.length; i < len; i++) {
|
||||
for (let i = 0; i < newSourceFiles.length; i++) {
|
||||
filesByName.set(filePaths[i], newSourceFiles[i]);
|
||||
}
|
||||
|
||||
@@ -728,7 +739,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
|
||||
return sourceFilesFoundSearchingNodeModules[file.path];
|
||||
return sourceFilesFoundSearchingNodeModules.get(file.path);
|
||||
}
|
||||
|
||||
function getDiagnosticsProducingTypeChecker() {
|
||||
@@ -951,7 +962,7 @@ namespace ts {
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.HeritageClause:
|
||||
let heritageClause = <HeritageClause>node;
|
||||
const heritageClause = <HeritageClause>node;
|
||||
if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
@@ -970,7 +981,7 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.enum_declarations_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
let typeAssertionExpression = <TypeAssertion>node;
|
||||
const typeAssertionExpression = <TypeAssertion>node;
|
||||
diagnostics.push(createDiagnosticForNode(typeAssertionExpression.type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
}
|
||||
@@ -1169,7 +1180,7 @@ namespace ts {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
let moduleNameExpr = getExternalModuleName(node);
|
||||
const moduleNameExpr = getExternalModuleName(node);
|
||||
if (!moduleNameExpr || moduleNameExpr.kind !== SyntaxKind.StringLiteral) {
|
||||
break;
|
||||
}
|
||||
@@ -1291,20 +1302,20 @@ namespace ts {
|
||||
|
||||
// If the file was previously found via a node_modules search, but is now being processed as a root file,
|
||||
// then everything it sucks in may also be marked incorrectly, and needs to be checked again.
|
||||
if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) {
|
||||
sourceFilesFoundSearchingNodeModules[file.path] = false;
|
||||
if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth == 0) {
|
||||
sourceFilesFoundSearchingNodeModules.set(file.path, false);
|
||||
if (!options.noResolve) {
|
||||
processReferencedFiles(file, isDefaultLib);
|
||||
processTypeReferenceDirectives(file);
|
||||
}
|
||||
|
||||
modulesWithElidedImports[file.path] = false;
|
||||
modulesWithElidedImports.set(file.path, false);
|
||||
processImportedModules(file);
|
||||
}
|
||||
// See if we need to reprocess the imports due to prior skipped imports
|
||||
else if (file && modulesWithElidedImports[file.path]) {
|
||||
else if (file && modulesWithElidedImports.get(file.path)) {
|
||||
if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
|
||||
modulesWithElidedImports[file.path] = false;
|
||||
modulesWithElidedImports.set(file.path, false);
|
||||
processImportedModules(file);
|
||||
}
|
||||
}
|
||||
@@ -1325,7 +1336,7 @@ namespace ts {
|
||||
|
||||
filesByName.set(path, file);
|
||||
if (file) {
|
||||
sourceFilesFoundSearchingNodeModules[path] = (currentNodeModulesDepth > 0);
|
||||
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
|
||||
file.path = path;
|
||||
|
||||
if (host.useCaseSensitiveFileNames()) {
|
||||
@@ -1386,7 +1397,7 @@ namespace ts {
|
||||
refFile?: SourceFile, refPos?: number, refEnd?: number): void {
|
||||
|
||||
// If we already found this library as a primary reference - nothing to do
|
||||
const previousResolution = resolvedTypeReferenceDirectives[typeReferenceDirective];
|
||||
const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
|
||||
if (previousResolution && previousResolution.primary) {
|
||||
return;
|
||||
}
|
||||
@@ -1426,7 +1437,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (saveResolution) {
|
||||
resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective;
|
||||
resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,7 +1490,7 @@ namespace ts {
|
||||
const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport;
|
||||
|
||||
if (elideImport) {
|
||||
modulesWithElidedImports[file.path] = true;
|
||||
modulesWithElidedImports.set(file.path, true);
|
||||
}
|
||||
else if (shouldAddFile) {
|
||||
const path = toPath(resolvedFileName, currentDirectory, getCanonicalFileName);
|
||||
@@ -1696,7 +1707,7 @@ namespace ts {
|
||||
if (!options.noEmit && !options.suppressOutputPathCheck) {
|
||||
const emitHost = getEmitHost();
|
||||
const emitFilesSeen = createFileMap<boolean>(!host.useCaseSensitiveFileNames() ? key => key.toLocaleLowerCase() : undefined);
|
||||
forEachExpectedEmitFile(emitHost, (emitFileNames) => {
|
||||
forEachEmittedFile(emitHost, (emitFileNames) => {
|
||||
verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
|
||||
verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
|
||||
});
|
||||
|
||||
+17
-15
@@ -56,7 +56,7 @@ namespace ts {
|
||||
tryScan<T>(callback: () => T): T;
|
||||
}
|
||||
|
||||
const textToToken = createMap({
|
||||
const textToToken = createMapFromTemplate({
|
||||
"abstract": SyntaxKind.AbstractKeyword,
|
||||
"any": SyntaxKind.AnyKeyword,
|
||||
"as": SyntaxKind.AsKeyword,
|
||||
@@ -98,6 +98,7 @@ namespace ts {
|
||||
"new": SyntaxKind.NewKeyword,
|
||||
"null": SyntaxKind.NullKeyword,
|
||||
"number": SyntaxKind.NumberKeyword,
|
||||
"object": SyntaxKind.ObjectKeyword,
|
||||
"package": SyntaxKind.PackageKeyword,
|
||||
"private": SyntaxKind.PrivateKeyword,
|
||||
"protected": SyntaxKind.ProtectedKeyword,
|
||||
@@ -275,9 +276,9 @@ namespace ts {
|
||||
|
||||
function makeReverseMap(source: Map<number>): string[] {
|
||||
const result: string[] = [];
|
||||
for (const name in source) {
|
||||
result[source[name]] = name;
|
||||
}
|
||||
source.forEach((value, name) => {
|
||||
result[value] = name;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -289,7 +290,7 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export function stringToToken(s: string): SyntaxKind {
|
||||
return textToToken[s];
|
||||
return textToToken.get(s);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -363,8 +364,6 @@ namespace ts {
|
||||
return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position);
|
||||
}
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
export function isWhiteSpace(ch: number): boolean {
|
||||
return isWhiteSpaceSingleLine(ch) || isLineBreak(ch);
|
||||
}
|
||||
@@ -531,7 +530,7 @@ namespace ts {
|
||||
const ch = text.charCodeAt(pos);
|
||||
|
||||
if ((pos + mergeConflictMarkerLength) < text.length) {
|
||||
for (let i = 0, n = mergeConflictMarkerLength; i < n; i++) {
|
||||
for (let i = 0; i < mergeConflictMarkerLength; i++) {
|
||||
if (text.charCodeAt(pos + i) !== ch) {
|
||||
return false;
|
||||
}
|
||||
@@ -643,7 +642,7 @@ namespace ts {
|
||||
pos++;
|
||||
continue;
|
||||
case CharacterCodes.slash:
|
||||
let nextChar = text.charCodeAt(pos + 1);
|
||||
const nextChar = text.charCodeAt(pos + 1);
|
||||
let hasTrailingNewLine = false;
|
||||
if (nextChar === CharacterCodes.slash || nextChar === CharacterCodes.asterisk) {
|
||||
const kind = nextChar === CharacterCodes.slash ? SyntaxKind.SingleLineCommentTrivia : SyntaxKind.MultiLineCommentTrivia;
|
||||
@@ -733,11 +732,11 @@ namespace ts {
|
||||
return comments;
|
||||
}
|
||||
|
||||
export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] {
|
||||
export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] | undefined {
|
||||
return reduceEachLeadingCommentRange(text, pos, appendCommentRange, undefined, undefined);
|
||||
}
|
||||
|
||||
export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] {
|
||||
export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] | undefined {
|
||||
return reduceEachTrailingCommentRange(text, pos, appendCommentRange, undefined, undefined);
|
||||
}
|
||||
|
||||
@@ -766,7 +765,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 1, n = name.length; i < n; i++) {
|
||||
for (let i = 1; i < name.length; i++) {
|
||||
if (!isIdentifierPart(name.charCodeAt(i), languageVersion)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1183,8 +1182,11 @@ namespace ts {
|
||||
const len = tokenValue.length;
|
||||
if (len >= 2 && len <= 11) {
|
||||
const ch = tokenValue.charCodeAt(0);
|
||||
if (ch >= CharacterCodes.a && ch <= CharacterCodes.z && hasOwnProperty.call(textToToken, tokenValue)) {
|
||||
return token = textToToken[tokenValue];
|
||||
if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) {
|
||||
token = textToToken.get(tokenValue);
|
||||
if (token !== undefined) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
return token = SyntaxKind.Identifier;
|
||||
@@ -1563,7 +1565,7 @@ namespace ts {
|
||||
pos++;
|
||||
return token = SyntaxKind.AtToken;
|
||||
case CharacterCodes.backslash:
|
||||
let cookedChar = peekUnicodeEscape();
|
||||
const cookedChar = peekUnicodeEscape();
|
||||
if (cookedChar >= 0 && isIdentifierStart(cookedChar, languageVersion)) {
|
||||
pos += 6;
|
||||
tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts();
|
||||
|
||||
@@ -427,7 +427,7 @@ namespace ts {
|
||||
|
||||
encodeLastRecordedSourceMapSpan();
|
||||
|
||||
return stringify({
|
||||
return JSON.stringify({
|
||||
version: 3,
|
||||
file: sourceMapData.sourceMapFile,
|
||||
sourceRoot: sourceMapData.sourceMapSourceRoot,
|
||||
|
||||
+19
-13
@@ -1,4 +1,7 @@
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
|
||||
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
|
||||
declare function clearTimeout(handle: any): void;
|
||||
|
||||
namespace ts {
|
||||
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
|
||||
@@ -18,7 +21,7 @@ namespace ts {
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
/**
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
*/
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
@@ -160,7 +163,7 @@ namespace ts {
|
||||
|
||||
function getNames(collection: any): string[] {
|
||||
const result: string[] = [];
|
||||
for (let e = new Enumerator(collection); !e.atEnd(); e.moveNext()) {
|
||||
for (const e = new Enumerator(collection); !e.atEnd(); e.moveNext()) {
|
||||
result.push(e.item().Name);
|
||||
}
|
||||
return result.sort();
|
||||
@@ -243,23 +246,23 @@ namespace ts {
|
||||
function createWatchedFileSet() {
|
||||
const dirWatchers = createMap<DirectoryWatcher>();
|
||||
// One file can have multiple watchers
|
||||
const fileWatcherCallbacks = createMap<FileWatcherCallback[]>();
|
||||
const fileWatcherCallbacks = createMultiMap<FileWatcherCallback>();
|
||||
return { addFile, removeFile };
|
||||
|
||||
function reduceDirWatcherRefCountForFile(fileName: string) {
|
||||
const dirName = getDirectoryPath(fileName);
|
||||
const watcher = dirWatchers[dirName];
|
||||
const watcher = dirWatchers.get(dirName);
|
||||
if (watcher) {
|
||||
watcher.referenceCount -= 1;
|
||||
if (watcher.referenceCount <= 0) {
|
||||
watcher.close();
|
||||
delete dirWatchers[dirName];
|
||||
dirWatchers.delete(dirName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addDirWatcher(dirPath: string): void {
|
||||
let watcher = dirWatchers[dirPath];
|
||||
let watcher = dirWatchers.get(dirPath);
|
||||
if (watcher) {
|
||||
watcher.referenceCount += 1;
|
||||
return;
|
||||
@@ -270,12 +273,12 @@ namespace ts {
|
||||
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
|
||||
);
|
||||
watcher.referenceCount = 1;
|
||||
dirWatchers[dirPath] = watcher;
|
||||
dirWatchers.set(dirPath, watcher);
|
||||
return;
|
||||
}
|
||||
|
||||
function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void {
|
||||
multiMapAdd(fileWatcherCallbacks, filePath, callback);
|
||||
fileWatcherCallbacks.add(filePath, callback);
|
||||
}
|
||||
|
||||
function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile {
|
||||
@@ -291,7 +294,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) {
|
||||
multiMapRemove(fileWatcherCallbacks, filePath, callback);
|
||||
fileWatcherCallbacks.remove(filePath, callback);
|
||||
}
|
||||
|
||||
function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) {
|
||||
@@ -300,9 +303,12 @@ namespace ts {
|
||||
? undefined
|
||||
: ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath);
|
||||
// Some applications save a working file via rename operations
|
||||
if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) {
|
||||
for (const fileCallback of fileWatcherCallbacks[fileName]) {
|
||||
fileCallback(fileName);
|
||||
if ((eventName === "change" || eventName === "rename")) {
|
||||
const callbacks = fileWatcherCallbacks.get(fileName);
|
||||
if (callbacks) {
|
||||
for (const fileCallback of callbacks) {
|
||||
fileCallback(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-10
@@ -1,4 +1,4 @@
|
||||
/// <reference path="visitor.ts" />
|
||||
/// <reference path="visitor.ts" />
|
||||
/// <reference path="transformers/ts.ts" />
|
||||
/// <reference path="transformers/jsx.ts" />
|
||||
/// <reference path="transformers/esnext.ts" />
|
||||
@@ -13,14 +13,16 @@
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
const moduleTransformerMap = createMap<Transformer>({
|
||||
[ModuleKind.ES2015]: transformES2015Module,
|
||||
[ModuleKind.System]: transformSystemModule,
|
||||
[ModuleKind.AMD]: transformModule,
|
||||
[ModuleKind.CommonJS]: transformModule,
|
||||
[ModuleKind.UMD]: transformModule,
|
||||
[ModuleKind.None]: transformModule,
|
||||
});
|
||||
function getModuleTransformer(moduleKind: ModuleKind): Transformer {
|
||||
switch (moduleKind) {
|
||||
case ModuleKind.ES2015:
|
||||
return transformES2015Module;
|
||||
case ModuleKind.System:
|
||||
return transformSystemModule;
|
||||
default:
|
||||
return transformModule;
|
||||
}
|
||||
}
|
||||
|
||||
const enum SyntaxKindFeatureFlags {
|
||||
Substitution = 1 << 0,
|
||||
@@ -56,7 +58,7 @@ namespace ts {
|
||||
transformers.push(transformGenerators);
|
||||
}
|
||||
|
||||
transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]);
|
||||
transformers.push(getModuleTransformer(moduleKind));
|
||||
|
||||
// The ES5 transformer is last so that it can substitute expressions like `exports.default`
|
||||
// for ES3.
|
||||
|
||||
@@ -459,7 +459,7 @@ namespace ts {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (typeof Object.getOwnPropertySymbols === "function")
|
||||
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]];
|
||||
return t;
|
||||
|
||||
+745
-438
File diff suppressed because it is too large
Load Diff
@@ -494,7 +494,7 @@ namespace ts {
|
||||
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); }
|
||||
step((generator = generator.apply(thisArg, _arguments)).next());
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};`
|
||||
};
|
||||
|
||||
@@ -9,6 +9,20 @@ namespace ts {
|
||||
* @param context Context and state information for the transformation.
|
||||
*/
|
||||
export function transformES5(context: TransformationContext) {
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
|
||||
// enable emit notification only if using --jsx preserve or react-native
|
||||
let previousOnEmitNode: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
|
||||
let noSubstitution: boolean[];
|
||||
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
|
||||
previousOnEmitNode = context.onEmitNode;
|
||||
context.onEmitNode = onEmitNode;
|
||||
context.enableEmitNotification(SyntaxKind.JsxOpeningElement);
|
||||
context.enableEmitNotification(SyntaxKind.JsxClosingElement);
|
||||
context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement);
|
||||
noSubstitution = [];
|
||||
}
|
||||
|
||||
const previousOnSubstituteNode = context.onSubstituteNode;
|
||||
context.onSubstituteNode = onSubstituteNode;
|
||||
context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
|
||||
@@ -24,6 +38,24 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the printer just before a node is printed.
|
||||
*
|
||||
* @param node The node to be printed.
|
||||
*/
|
||||
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
const tagName = (<JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement>node).tagName;
|
||||
noSubstitution[getOriginalNodeId(tagName)] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks node substitutions.
|
||||
*
|
||||
@@ -31,6 +63,10 @@ namespace ts {
|
||||
* @param node The node to substitute.
|
||||
*/
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node) {
|
||||
if (node.id && noSubstitution && noSubstitution[node.id]) {
|
||||
return previousOnSubstituteNode(emitContext, node);
|
||||
}
|
||||
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
if (isPropertyAccessExpression(node)) {
|
||||
return substitutePropertyAccessExpression(node);
|
||||
@@ -80,4 +116,4 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="../factory.ts" />
|
||||
/// <reference path="../factory.ts" />
|
||||
/// <reference path="../visitor.ts" />
|
||||
|
||||
// Transforms generator functions into a compatible ES5 representation with similar runtime
|
||||
@@ -217,13 +217,15 @@ namespace ts {
|
||||
Endfinally = 7,
|
||||
}
|
||||
|
||||
const instructionNames = createMap<string>({
|
||||
[Instruction.Return]: "return",
|
||||
[Instruction.Break]: "break",
|
||||
[Instruction.Yield]: "yield",
|
||||
[Instruction.YieldStar]: "yield*",
|
||||
[Instruction.Endfinally]: "endfinally",
|
||||
});
|
||||
function getInstructionName(instruction: Instruction): string {
|
||||
switch (instruction) {
|
||||
case Instruction.Return: return "return";
|
||||
case Instruction.Break: return "break";
|
||||
case Instruction.Yield: return "yield";
|
||||
case Instruction.YieldStar: return "yield*";
|
||||
case Instruction.Endfinally: return "endfinally";
|
||||
}
|
||||
}
|
||||
|
||||
export function transformGenerators(context: TransformationContext) {
|
||||
const {
|
||||
@@ -241,7 +243,7 @@ namespace ts {
|
||||
|
||||
let currentSourceFile: SourceFile;
|
||||
let renamedCatchVariables: Map<boolean>;
|
||||
let renamedCatchVariableDeclarations: Map<Identifier>;
|
||||
let renamedCatchVariableDeclarations: Identifier[];
|
||||
|
||||
let inGeneratorFunctionBody: boolean;
|
||||
let inStatementContainingYield: boolean;
|
||||
@@ -938,7 +940,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
markLabel(resumeLabel);
|
||||
return createGeneratorResume();
|
||||
return createGeneratorResume(/*location*/ node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1234,7 +1236,9 @@ namespace ts {
|
||||
|
||||
function transformAndEmitVariableDeclarationList(node: VariableDeclarationList): VariableDeclarationList {
|
||||
for (const variable of node.declarations) {
|
||||
hoistVariableDeclaration(<Identifier>variable.name);
|
||||
const name = getSynthesizedClone(<Identifier>variable.name);
|
||||
setCommentRange(name, variable.name);
|
||||
hoistVariableDeclaration(name);
|
||||
}
|
||||
|
||||
const variables = getInitializedVariables(node);
|
||||
@@ -1287,7 +1291,7 @@ namespace ts {
|
||||
if (containsYield(node.thenStatement) || containsYield(node.elseStatement)) {
|
||||
const endLabel = defineLabel();
|
||||
const elseLabel = node.elseStatement ? defineLabel() : undefined;
|
||||
emitBreakWhenFalse(node.elseStatement ? elseLabel : endLabel, visitNode(node.expression, visitor, isExpression));
|
||||
emitBreakWhenFalse(node.elseStatement ? elseLabel : endLabel, visitNode(node.expression, visitor, isExpression), /*location*/ node.expression);
|
||||
transformAndEmitEmbeddedStatement(node.thenStatement);
|
||||
if (node.elseStatement) {
|
||||
emitBreak(endLabel);
|
||||
@@ -1921,12 +1925,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function substituteExpressionIdentifier(node: Identifier) {
|
||||
if (renamedCatchVariables && hasProperty(renamedCatchVariables, node.text)) {
|
||||
if (renamedCatchVariables && renamedCatchVariables.has(node.text)) {
|
||||
const original = getOriginalNode(node);
|
||||
if (isIdentifier(original) && original.parent) {
|
||||
const declaration = resolver.getReferencedValueDeclaration(original);
|
||||
if (declaration) {
|
||||
const name = getProperty(renamedCatchVariableDeclarations, String(getOriginalNodeId(declaration)));
|
||||
const name = renamedCatchVariableDeclarations[getOriginalNodeId(declaration)];
|
||||
if (name) {
|
||||
const clone = getMutableClone(name);
|
||||
setSourceMapRange(clone, node);
|
||||
@@ -2092,11 +2096,11 @@ namespace ts {
|
||||
|
||||
if (!renamedCatchVariables) {
|
||||
renamedCatchVariables = createMap<boolean>();
|
||||
renamedCatchVariableDeclarations = createMap<Identifier>();
|
||||
renamedCatchVariableDeclarations = [];
|
||||
context.enableSubstitution(SyntaxKind.Identifier);
|
||||
}
|
||||
|
||||
renamedCatchVariables[text] = true;
|
||||
renamedCatchVariables.set(text, true);
|
||||
renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name;
|
||||
|
||||
const exception = <ExceptionBlock>peekBlock();
|
||||
@@ -2401,7 +2405,7 @@ namespace ts {
|
||||
*/
|
||||
function createInstruction(instruction: Instruction): NumericLiteral {
|
||||
const literal = createLiteral(instruction);
|
||||
literal.trailingComment = instructionNames[instruction];
|
||||
literal.trailingComment = getInstructionName(instruction);
|
||||
return literal;
|
||||
}
|
||||
|
||||
@@ -2965,12 +2969,15 @@ namespace ts {
|
||||
lastOperationWasAbrupt = true;
|
||||
lastOperationWasCompletion = true;
|
||||
writeStatement(
|
||||
createReturn(
|
||||
createArrayLiteral(expression
|
||||
? [createInstruction(Instruction.Return), expression]
|
||||
: [createInstruction(Instruction.Return)]
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral(expression
|
||||
? [createInstruction(Instruction.Return), expression]
|
||||
: [createInstruction(Instruction.Return)]
|
||||
),
|
||||
operationLocation
|
||||
),
|
||||
operationLocation
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -2984,12 +2991,15 @@ namespace ts {
|
||||
function writeBreak(label: Label, operationLocation: TextRange): void {
|
||||
lastOperationWasAbrupt = true;
|
||||
writeStatement(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
),
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -3003,15 +3013,21 @@ namespace ts {
|
||||
*/
|
||||
function writeBreakWhenTrue(label: Label, condition: Expression, operationLocation: TextRange): void {
|
||||
writeStatement(
|
||||
createIf(
|
||||
condition,
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
)
|
||||
setEmitFlags(
|
||||
createIf(
|
||||
condition,
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
),
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
),
|
||||
EmitFlags.SingleLine
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -3025,15 +3041,21 @@ namespace ts {
|
||||
*/
|
||||
function writeBreakWhenFalse(label: Label, condition: Expression, operationLocation: TextRange): void {
|
||||
writeStatement(
|
||||
createIf(
|
||||
createLogicalNot(condition),
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
)
|
||||
setEmitFlags(
|
||||
createIf(
|
||||
createLogicalNot(condition),
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.Break),
|
||||
createLabel(label)
|
||||
]),
|
||||
operationLocation
|
||||
),
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
),
|
||||
EmitFlags.SingleLine
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -3047,13 +3069,16 @@ namespace ts {
|
||||
function writeYield(expression: Expression, operationLocation: TextRange): void {
|
||||
lastOperationWasAbrupt = true;
|
||||
writeStatement(
|
||||
createReturn(
|
||||
createArrayLiteral(
|
||||
expression
|
||||
? [createInstruction(Instruction.Yield), expression]
|
||||
: [createInstruction(Instruction.Yield)]
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral(
|
||||
expression
|
||||
? [createInstruction(Instruction.Yield), expression]
|
||||
: [createInstruction(Instruction.Yield)]
|
||||
),
|
||||
operationLocation
|
||||
),
|
||||
operationLocation
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -3067,12 +3092,15 @@ namespace ts {
|
||||
function writeYieldStar(expression: Expression, operationLocation: TextRange): void {
|
||||
lastOperationWasAbrupt = true;
|
||||
writeStatement(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.YieldStar),
|
||||
expression
|
||||
]),
|
||||
operationLocation
|
||||
setEmitFlags(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
createInstruction(Instruction.YieldStar),
|
||||
expression
|
||||
]),
|
||||
operationLocation
|
||||
),
|
||||
EmitFlags.NoTokenSourceMaps
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ namespace ts {
|
||||
return decoded ? createLiteral(decoded, /*location*/ node) : node;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.JsxExpression) {
|
||||
if (node.expression === undefined) {
|
||||
return createLiteral(true);
|
||||
}
|
||||
return visitJsxExpression(<JsxExpression>node);
|
||||
}
|
||||
else {
|
||||
@@ -157,33 +160,50 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function visitJsxText(node: JsxText) {
|
||||
const text = getTextOfNode(node, /*includeTrivia*/ true);
|
||||
let parts: Expression[];
|
||||
let firstNonWhitespace = 0;
|
||||
let lastNonWhitespace = -1;
|
||||
function visitJsxText(node: JsxText): StringLiteral | undefined {
|
||||
const fixed = fixupWhitespaceAndDecodeEntities(getTextOfNode(node, /*includeTrivia*/ true));
|
||||
return fixed === undefined ? undefined : createLiteral(fixed);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSX trims whitespace at the end and beginning of lines, except that the
|
||||
* start/end of a tag is considered a start/end of a line only if that line is
|
||||
* on the same line as the closing tag. See examples in
|
||||
* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
|
||||
* See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model
|
||||
*
|
||||
* An equivalent algorithm would be:
|
||||
* - If there is only one line, return it.
|
||||
* - If there is only whitespace (but multiple lines), return `undefined`.
|
||||
* - Split the text into lines.
|
||||
* - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines.
|
||||
* - Decode entities on each line (individually).
|
||||
* - Remove empty lines and join the rest with " ".
|
||||
*/
|
||||
function fixupWhitespaceAndDecodeEntities(text: string): string | undefined {
|
||||
let acc: string | undefined;
|
||||
// First non-whitespace character on this line.
|
||||
let firstNonWhitespace = 0;
|
||||
// Last non-whitespace character on this line.
|
||||
let lastNonWhitespace = -1;
|
||||
// These initial values are special because the first line is:
|
||||
// firstNonWhitespace = 0 to indicate that we want leading whitsepace,
|
||||
// but lastNonWhitespace = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
|
||||
|
||||
// JSX trims whitespace at the end and beginning of lines, except that the
|
||||
// start/end of a tag is considered a start/end of a line only if that line is
|
||||
// on the same line as the closing tag. See examples in
|
||||
// tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const c = text.charCodeAt(i);
|
||||
if (isLineBreak(c)) {
|
||||
if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) {
|
||||
const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1);
|
||||
if (!parts) {
|
||||
parts = [];
|
||||
}
|
||||
|
||||
// We do not escape the string here as that is handled by the printer
|
||||
// when it emits the literal. We do, however, need to decode JSX entities.
|
||||
parts.push(createLiteral(decodeEntities(part)));
|
||||
// If we've seen any non-whitespace characters on this line, add the 'trim' of the line.
|
||||
// (lastNonWhitespace === -1 is a special flag to detect whether the first line is all whitespace.)
|
||||
if (firstNonWhitespace !== -1 && lastNonWhitespace !== -1) {
|
||||
acc = addLineOfJsxText(acc, text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1));
|
||||
}
|
||||
|
||||
// Reset firstNonWhitespace for the next line.
|
||||
// Don't bother to reset lastNonWhitespace because we ignore it if firstNonWhitespace = -1.
|
||||
firstNonWhitespace = -1;
|
||||
}
|
||||
else if (!isWhiteSpace(c)) {
|
||||
else if (!isWhiteSpaceSingleLine(c)) {
|
||||
lastNonWhitespace = i;
|
||||
if (firstNonWhitespace === -1) {
|
||||
firstNonWhitespace = i;
|
||||
@@ -191,29 +211,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
if (firstNonWhitespace !== -1) {
|
||||
const part = text.substr(firstNonWhitespace);
|
||||
if (!parts) {
|
||||
parts = [];
|
||||
}
|
||||
|
||||
// We do not escape the string here as that is handled by the printer
|
||||
// when it emits the literal. We do, however, need to decode JSX entities.
|
||||
parts.push(createLiteral(decodeEntities(part)));
|
||||
}
|
||||
|
||||
if (parts) {
|
||||
return reduceLeft(parts, aggregateJsxTextParts);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return firstNonWhitespace !== -1
|
||||
// Last line had a non-whitespace character. Emit the 'trimLeft', meaning keep trailing whitespace.
|
||||
? addLineOfJsxText(acc, text.substr(firstNonWhitespace))
|
||||
// Last line was all whitespace, so ignore it
|
||||
: acc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates two expressions by interpolating them with a whitespace literal.
|
||||
*/
|
||||
function aggregateJsxTextParts(left: Expression, right: Expression) {
|
||||
return createAdd(createAdd(left, createLiteral(" ")), right);
|
||||
function addLineOfJsxText(acc: string | undefined, trimmedLine: string): string {
|
||||
// We do not escape the string here as that is handled by the printer
|
||||
// when it emits the literal. We do, however, need to decode JSX entities.
|
||||
const decoded = decodeEntities(trimmedLine);
|
||||
return acc === undefined ? decoded : acc + " " + decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +238,7 @@ namespace ts {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
else {
|
||||
const ch = entities[word];
|
||||
const ch = entities.get(word);
|
||||
// If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace)
|
||||
return ch ? String.fromCharCode(ch) : match;
|
||||
}
|
||||
@@ -277,7 +286,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const entities = createMap<number>({
|
||||
const entities = createMapFromTemplate<number>({
|
||||
"quot": 0x0022,
|
||||
"amp": 0x0026,
|
||||
"apos": 0x0027,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="../../factory.ts" />
|
||||
/// <reference path="../../factory.ts" />
|
||||
/// <reference path="../../visitor.ts" />
|
||||
|
||||
/*@internal*/
|
||||
@@ -10,12 +10,13 @@ namespace ts {
|
||||
importAliasNames: ParameterDeclaration[];
|
||||
}
|
||||
|
||||
const transformModuleDelegates = createMap<(node: SourceFile) => SourceFile>({
|
||||
[ModuleKind.None]: transformCommonJSModule,
|
||||
[ModuleKind.CommonJS]: transformCommonJSModule,
|
||||
[ModuleKind.AMD]: transformAMDModule,
|
||||
[ModuleKind.UMD]: transformUMDModule,
|
||||
});
|
||||
function getTransformModuleDelegate(moduleKind: ModuleKind): (node: SourceFile) => SourceFile {
|
||||
switch (moduleKind) {
|
||||
case ModuleKind.AMD: return transformAMDModule;
|
||||
case ModuleKind.UMD: return transformUMDModule;
|
||||
default: return transformCommonJSModule;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
startLexicalEnvironment,
|
||||
@@ -38,12 +39,12 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols.
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
const moduleInfoMap = createMap<ExternalModuleInfo>(); // The ExternalModuleInfo for each file.
|
||||
const deferredExports = createMap<Statement[]>(); // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file.
|
||||
const deferredExports: Statement[][] = []; // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
|
||||
let currentSourceFile: SourceFile; // The current file.
|
||||
let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file.
|
||||
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
|
||||
let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored.
|
||||
|
||||
return transformSourceFile;
|
||||
|
||||
@@ -60,10 +61,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
currentSourceFile = node;
|
||||
currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver, compilerOptions);
|
||||
currentModuleInfo = collectExternalModuleInfo(node, resolver, compilerOptions);
|
||||
moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo;
|
||||
|
||||
// Perform the transformation.
|
||||
const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None];
|
||||
const transformModule = getTransformModuleDelegate(moduleKind);
|
||||
const updated = transformModule(node);
|
||||
|
||||
currentSourceFile = undefined;
|
||||
@@ -102,28 +104,7 @@ namespace ts {
|
||||
function transformAMDModule(node: SourceFile) {
|
||||
const define = createIdentifier("define");
|
||||
const moduleName = tryGetModuleNameFromFile(node, host, compilerOptions);
|
||||
return transformAsynchronousModule(node, define, moduleName, /*includeNonAmdDependencies*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a SourceFile into a UMD module.
|
||||
*
|
||||
* @param node The SourceFile node.
|
||||
*/
|
||||
function transformUMDModule(node: SourceFile) {
|
||||
const define = createRawExpression(umdHelper);
|
||||
return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a SourceFile into an AMD or UMD module.
|
||||
*
|
||||
* @param node The SourceFile node.
|
||||
* @param define The expression used to define the module.
|
||||
* @param moduleName An expression for the module name, if available.
|
||||
* @param includeNonAmdDependencies A value indicating whether to incldue any non-AMD dependencies.
|
||||
*/
|
||||
function transformAsynchronousModule(node: SourceFile, define: Expression, moduleName: Expression, includeNonAmdDependencies: boolean) {
|
||||
// An AMD define function has the following shape:
|
||||
//
|
||||
// define(id?, dependencies?, factory);
|
||||
@@ -145,7 +126,7 @@ namespace ts {
|
||||
//
|
||||
// we need to add modules without alias names to the end of the dependencies list
|
||||
|
||||
const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, includeNonAmdDependencies);
|
||||
const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ true);
|
||||
|
||||
// Create an updated SourceFile:
|
||||
//
|
||||
@@ -194,6 +175,137 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a SourceFile into a UMD module.
|
||||
*
|
||||
* @param node The SourceFile node.
|
||||
*/
|
||||
function transformUMDModule(node: SourceFile) {
|
||||
const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ false);
|
||||
const umdHeader = createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "factory")],
|
||||
/*type*/ undefined,
|
||||
createBlock(
|
||||
[
|
||||
createIf(
|
||||
createLogicalAnd(
|
||||
createTypeCheck(createIdentifier("module"), "object"),
|
||||
createTypeCheck(createPropertyAccess(createIdentifier("module"), "exports"), "object")
|
||||
),
|
||||
createBlock([
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
[
|
||||
createVariableDeclaration(
|
||||
"v",
|
||||
/*type*/ undefined,
|
||||
createCall(
|
||||
createIdentifier("factory"),
|
||||
/*typeArguments*/ undefined,
|
||||
[
|
||||
createIdentifier("require"),
|
||||
createIdentifier("exports")
|
||||
]
|
||||
)
|
||||
)
|
||||
]
|
||||
),
|
||||
setEmitFlags(
|
||||
createIf(
|
||||
createStrictInequality(
|
||||
createIdentifier("v"),
|
||||
createIdentifier("undefined")
|
||||
),
|
||||
createStatement(
|
||||
createAssignment(
|
||||
createPropertyAccess(createIdentifier("module"), "exports"),
|
||||
createIdentifier("v")
|
||||
)
|
||||
)
|
||||
),
|
||||
EmitFlags.SingleLine
|
||||
)
|
||||
]),
|
||||
createIf(
|
||||
createLogicalAnd(
|
||||
createTypeCheck(createIdentifier("define"), "function"),
|
||||
createPropertyAccess(createIdentifier("define"), "amd")
|
||||
),
|
||||
createBlock([
|
||||
createStatement(
|
||||
createCall(
|
||||
createIdentifier("define"),
|
||||
/*typeArguments*/ undefined,
|
||||
[
|
||||
createArrayLiteral([
|
||||
createLiteral("require"),
|
||||
createLiteral("exports"),
|
||||
...aliasedModuleNames,
|
||||
...unaliasedModuleNames
|
||||
]),
|
||||
createIdentifier("factory")
|
||||
]
|
||||
)
|
||||
)
|
||||
])
|
||||
)
|
||||
)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
/*multiLine*/ true
|
||||
)
|
||||
);
|
||||
|
||||
// Create an updated SourceFile:
|
||||
//
|
||||
// (function (factory) {
|
||||
// if (typeof module === "object" && typeof module.exports === "object") {
|
||||
// var v = factory(require, exports);
|
||||
// if (v !== undefined) module.exports = v;
|
||||
// }
|
||||
// else if (typeof define === 'function' && define.amd) {
|
||||
// define(["require", "exports"], factory);
|
||||
// }
|
||||
// })(function ...)
|
||||
|
||||
return updateSourceFileNode(
|
||||
node,
|
||||
createNodeArray(
|
||||
[
|
||||
createStatement(
|
||||
createCall(
|
||||
umdHeader,
|
||||
/*typeArguments*/ undefined,
|
||||
[
|
||||
// Add the module body function argument:
|
||||
//
|
||||
// function (require, exports) ...
|
||||
createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[
|
||||
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "require"),
|
||||
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "exports"),
|
||||
...importAliasNames
|
||||
],
|
||||
/*type*/ undefined,
|
||||
transformAsynchronousModuleBody(node)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
],
|
||||
/*location*/ node.statements
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the additional asynchronous dependencies for the module.
|
||||
*
|
||||
@@ -973,7 +1085,7 @@ namespace ts {
|
||||
*/
|
||||
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined {
|
||||
const name = getDeclarationName(decl);
|
||||
const exportSpecifiers = currentModuleInfo.exportSpecifiers[name.text];
|
||||
const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(name.text);
|
||||
if (exportSpecifiers) {
|
||||
for (const exportSpecifier of exportSpecifiers) {
|
||||
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name);
|
||||
@@ -997,7 +1109,7 @@ namespace ts {
|
||||
function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean): Statement[] | undefined {
|
||||
if (exportName.text === "default") {
|
||||
const sourceFile = getOriginalNode(currentSourceFile, isSourceFile);
|
||||
if (sourceFile && !sourceFile.symbol.exports["___esModule"]) {
|
||||
if (sourceFile && !sourceFile.symbol.exports.get("___esModule")) {
|
||||
if (languageVersion === ScriptTarget.ES3) {
|
||||
statements = append(statements,
|
||||
createStatement(
|
||||
@@ -1103,7 +1215,7 @@ namespace ts {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
currentSourceFile = <SourceFile>node;
|
||||
currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)];
|
||||
noSubstitution = createMap<boolean>();
|
||||
noSubstitution = [];
|
||||
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
|
||||
@@ -1333,15 +1445,4 @@ namespace ts {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}`
|
||||
};
|
||||
|
||||
// emit output for the UMD helper function.
|
||||
const umdHelper = `
|
||||
(function (dependencies, factory) {
|
||||
if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
var v = factory(require, exports); if (v !== undefined) module.exports = v;
|
||||
}
|
||||
else if (typeof define === 'function' && define.amd) {
|
||||
define(dependencies, factory);
|
||||
}
|
||||
})`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="../../factory.ts" />
|
||||
/// <reference path="../../factory.ts" />
|
||||
/// <reference path="../../visitor.ts" />
|
||||
|
||||
/*@internal*/
|
||||
@@ -28,10 +28,10 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols.
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
const moduleInfoMap = createMap<ExternalModuleInfo>(); // The ExternalModuleInfo for each file.
|
||||
const deferredExports = createMap<Statement[]>(); // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
const exportFunctionsMap = createMap<Identifier>(); // The export function associated with a source file.
|
||||
const noSubstitutionMap = createMap<Map<boolean>>(); // Set of nodes for which substitution rules should be ignored for each file.
|
||||
const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file.
|
||||
const deferredExports: Statement[][] = []; // Exports to defer until an EndOfDeclarationMarker is found.
|
||||
const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file.
|
||||
const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file.
|
||||
|
||||
let currentSourceFile: SourceFile; // The current file.
|
||||
let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file.
|
||||
@@ -39,7 +39,7 @@ namespace ts {
|
||||
let contextObject: Identifier; // The context object for the current file.
|
||||
let hoistedStatements: Statement[];
|
||||
let enclosingBlockScopedContainer: Node;
|
||||
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
|
||||
let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored.
|
||||
|
||||
return transformSourceFile;
|
||||
|
||||
@@ -77,7 +77,8 @@ namespace ts {
|
||||
|
||||
// Make sure that the name of the 'exports' function does not conflict with
|
||||
// existing identifiers.
|
||||
exportFunction = exportFunctionsMap[id] = createUniqueName("exports");
|
||||
exportFunction = createUniqueName("exports");
|
||||
exportFunctionsMap[id] = exportFunction;
|
||||
contextObject = createUniqueName("context");
|
||||
|
||||
// Add the body of the module.
|
||||
@@ -101,20 +102,21 @@ namespace ts {
|
||||
// So the helper will be emit at the correct position instead of at the top of the source-file
|
||||
const moduleName = tryGetModuleNameFromFile(node, host, compilerOptions);
|
||||
const dependencies = createArrayLiteral(map(dependencyGroups, dependencyGroup => dependencyGroup.name));
|
||||
const updated = updateSourceFileNode(
|
||||
node,
|
||||
createNodeArray([
|
||||
createStatement(
|
||||
createCall(
|
||||
createPropertyAccess(createIdentifier("System"), "register"),
|
||||
const updated = setEmitFlags(
|
||||
updateSourceFileNode(
|
||||
node,
|
||||
createNodeArray([
|
||||
createStatement(
|
||||
createCall(
|
||||
createPropertyAccess(createIdentifier("System"), "register"),
|
||||
/*typeArguments*/ undefined,
|
||||
moduleName
|
||||
? [moduleName, dependencies, moduleBodyFunction]
|
||||
: [dependencies, moduleBodyFunction]
|
||||
moduleName
|
||||
? [moduleName, dependencies, moduleBodyFunction]
|
||||
: [dependencies, moduleBodyFunction]
|
||||
)
|
||||
)
|
||||
)
|
||||
], node.statements)
|
||||
);
|
||||
], node.statements)
|
||||
), EmitFlags.NoTrailingComments);
|
||||
|
||||
if (!(compilerOptions.outFile || compilerOptions.out)) {
|
||||
moveEmitHelpers(updated, moduleBodyBlock, helper => !helper.scoped);
|
||||
@@ -147,13 +149,13 @@ namespace ts {
|
||||
const externalImport = externalImports[i];
|
||||
const externalModuleName = getExternalModuleNameLiteral(externalImport, currentSourceFile, host, resolver, compilerOptions);
|
||||
const text = externalModuleName.text;
|
||||
if (hasProperty(groupIndices, text)) {
|
||||
const groupIndex = groupIndices.get(text);
|
||||
if (groupIndex !== undefined) {
|
||||
// deduplicate/group entries in dependency list by the dependency name
|
||||
const groupIndex = groupIndices[text];
|
||||
dependencyGroups[groupIndex].externalImports.push(externalImport);
|
||||
}
|
||||
else {
|
||||
groupIndices[text] = dependencyGroups.length;
|
||||
groupIndices.set(text, dependencyGroups.length);
|
||||
dependencyGroups.push({
|
||||
name: externalModuleName,
|
||||
externalImports: [externalImport]
|
||||
@@ -305,7 +307,7 @@ namespace ts {
|
||||
// this set is used to filter names brought by star expors.
|
||||
|
||||
// local names set should only be added if we have anything exported
|
||||
if (!moduleInfo.exportedNames && isEmpty(moduleInfo.exportSpecifiers)) {
|
||||
if (!moduleInfo.exportedNames && moduleInfo.exportSpecifiers.size === 0) {
|
||||
// no exported declarations (export var ...) or export specifiers (export {x})
|
||||
// check if we have any non star export declarations.
|
||||
let hasExportDeclarationWithExportClause = false;
|
||||
@@ -1080,7 +1082,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const name = getDeclarationName(decl);
|
||||
const exportSpecifiers = moduleInfo.exportSpecifiers[name.text];
|
||||
const exportSpecifiers = moduleInfo.exportSpecifiers.get(name.text);
|
||||
if (exportSpecifiers) {
|
||||
for (const exportSpecifier of exportSpecifiers) {
|
||||
if (exportSpecifier.name.text !== excludeName) {
|
||||
@@ -1769,7 +1771,7 @@ namespace ts {
|
||||
* @param node The node which should not be substituted.
|
||||
*/
|
||||
function preventSubstitution<T extends Node>(node: T): T {
|
||||
if (noSubstitution === undefined) noSubstitution = createMap<boolean>();
|
||||
if (noSubstitution === undefined) noSubstitution = [];
|
||||
noSubstitution[getNodeId(node)] = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="../factory.ts" />
|
||||
/// <reference path="../factory.ts" />
|
||||
/// <reference path="../visitor.ts" />
|
||||
/// <reference path="./destructuring.ts" />
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace ts {
|
||||
* A map that keeps track of aliases created for classes with decorators to avoid issues
|
||||
* with the double-binding behavior of classes.
|
||||
*/
|
||||
let classAliases: Map<Identifier>;
|
||||
let classAliases: Identifier[];
|
||||
|
||||
/**
|
||||
* Keeps track of whether we are within any containing namespaces when performing
|
||||
@@ -345,6 +345,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
// TypeScript property declarations are elided.
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.Constructor:
|
||||
return visitConstructor(<ConstructorDeclaration>node);
|
||||
@@ -1222,11 +1223,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
const { firstAccessor, secondAccessor, setAccessor } = getAllAccessorDeclarations(node.members, accessor);
|
||||
if (accessor !== firstAccessor) {
|
||||
const firstAccessorWithDecorators = firstAccessor.decorators ? firstAccessor : secondAccessor && secondAccessor.decorators ? secondAccessor : undefined;
|
||||
if (!firstAccessorWithDecorators || accessor !== firstAccessorWithDecorators) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const decorators = firstAccessor.decorators || (secondAccessor && secondAccessor.decorators);
|
||||
const decorators = firstAccessorWithDecorators.decorators;
|
||||
const parameters = getDecoratorsOfParameters(setAccessor);
|
||||
if (!decorators && !parameters) {
|
||||
return undefined;
|
||||
@@ -1275,7 +1277,7 @@ namespace ts {
|
||||
* @param node The declaration node.
|
||||
* @param allDecorators An object containing all of the decorators for the declaration.
|
||||
*/
|
||||
function transformAllDecoratorsOfDeclaration(node: Declaration, allDecorators: AllDecorators) {
|
||||
function transformAllDecoratorsOfDeclaration(node: Declaration, container: ClassLikeDeclaration, allDecorators: AllDecorators) {
|
||||
if (!allDecorators) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1283,7 +1285,7 @@ namespace ts {
|
||||
const decoratorExpressions: Expression[] = [];
|
||||
addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator));
|
||||
addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter));
|
||||
addTypeMetadata(node, decoratorExpressions);
|
||||
addTypeMetadata(node, container, decoratorExpressions);
|
||||
return decoratorExpressions;
|
||||
}
|
||||
|
||||
@@ -1332,7 +1334,7 @@ namespace ts {
|
||||
*/
|
||||
function generateClassElementDecorationExpression(node: ClassExpression | ClassDeclaration, member: ClassElement) {
|
||||
const allDecorators = getAllDecoratorsOfClassElement(node, member);
|
||||
const decoratorExpressions = transformAllDecoratorsOfDeclaration(member, allDecorators);
|
||||
const decoratorExpressions = transformAllDecoratorsOfDeclaration(member, node, allDecorators);
|
||||
if (!decoratorExpressions) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1353,13 +1355,13 @@ namespace ts {
|
||||
// __metadata("design:type", Function),
|
||||
// __metadata("design:paramtypes", [Object]),
|
||||
// __metadata("design:returntype", void 0)
|
||||
// ], C.prototype, "method", undefined);
|
||||
// ], C.prototype, "method", null);
|
||||
//
|
||||
// The emit for an accessor is:
|
||||
//
|
||||
// __decorate([
|
||||
// dec
|
||||
// ], C.prototype, "accessor", undefined);
|
||||
// ], C.prototype, "accessor", null);
|
||||
//
|
||||
// The emit for a property is:
|
||||
//
|
||||
@@ -1413,7 +1415,7 @@ namespace ts {
|
||||
*/
|
||||
function generateConstructorDecorationExpression(node: ClassExpression | ClassDeclaration) {
|
||||
const allDecorators = getAllDecoratorsOfConstructor(node);
|
||||
const decoratorExpressions = transformAllDecoratorsOfDeclaration(node, allDecorators);
|
||||
const decoratorExpressions = transformAllDecoratorsOfDeclaration(node, node, allDecorators);
|
||||
if (!decoratorExpressions) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1466,22 +1468,22 @@ namespace ts {
|
||||
* @param node The declaration node.
|
||||
* @param decoratorExpressions The destination array to which to add new decorator expressions.
|
||||
*/
|
||||
function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
|
||||
function addTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) {
|
||||
if (USE_NEW_TYPE_METADATA_FORMAT) {
|
||||
addNewTypeMetadata(node, decoratorExpressions);
|
||||
addNewTypeMetadata(node, container, decoratorExpressions);
|
||||
}
|
||||
else {
|
||||
addOldTypeMetadata(node, decoratorExpressions);
|
||||
addOldTypeMetadata(node, container, decoratorExpressions);
|
||||
}
|
||||
}
|
||||
|
||||
function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
|
||||
function addOldTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) {
|
||||
if (compilerOptions.emitDecoratorMetadata) {
|
||||
if (shouldAddTypeMetadata(node)) {
|
||||
decoratorExpressions.push(createMetadataHelper(context, "design:type", serializeTypeOfNode(node)));
|
||||
}
|
||||
if (shouldAddParamTypesMetadata(node)) {
|
||||
decoratorExpressions.push(createMetadataHelper(context, "design:paramtypes", serializeParameterTypesOfNode(node)));
|
||||
decoratorExpressions.push(createMetadataHelper(context, "design:paramtypes", serializeParameterTypesOfNode(node, container)));
|
||||
}
|
||||
if (shouldAddReturnTypeMetadata(node)) {
|
||||
decoratorExpressions.push(createMetadataHelper(context, "design:returntype", serializeReturnTypeOfNode(node)));
|
||||
@@ -1489,14 +1491,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function addNewTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
|
||||
function addNewTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) {
|
||||
if (compilerOptions.emitDecoratorMetadata) {
|
||||
let properties: ObjectLiteralElementLike[];
|
||||
if (shouldAddTypeMetadata(node)) {
|
||||
(properties || (properties = [])).push(createPropertyAssignment("type", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeTypeOfNode(node))));
|
||||
}
|
||||
if (shouldAddParamTypesMetadata(node)) {
|
||||
(properties || (properties = [])).push(createPropertyAssignment("paramTypes", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeParameterTypesOfNode(node))));
|
||||
(properties || (properties = [])).push(createPropertyAssignment("paramTypes", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeParameterTypesOfNode(node, container))));
|
||||
}
|
||||
if (shouldAddReturnTypeMetadata(node)) {
|
||||
(properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node))));
|
||||
@@ -1541,20 +1543,27 @@ namespace ts {
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function shouldAddParamTypesMetadata(node: Declaration): boolean {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.ClassDeclaration
|
||||
|| kind === SyntaxKind.ClassExpression
|
||||
|| kind === SyntaxKind.MethodDeclaration
|
||||
|| kind === SyntaxKind.GetAccessor
|
||||
|| kind === SyntaxKind.SetAccessor;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
return getFirstConstructorWithBody(<ClassLikeDeclaration>node) !== undefined;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
type SerializedEntityNameAsExpression = Identifier | BinaryExpression | PropertyAccessExpression;
|
||||
type SerializedTypeNode = SerializedEntityNameAsExpression | VoidExpression | ConditionalExpression;
|
||||
|
||||
/**
|
||||
* Serializes the type of a node for use with decorator type metadata.
|
||||
*
|
||||
* @param node The node that should have its type serialized.
|
||||
*/
|
||||
function serializeTypeOfNode(node: Node): Expression {
|
||||
function serializeTypeOfNode(node: Node): SerializedTypeNode {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
@@ -1571,30 +1580,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most likely element type for a TypeNode. This is not an exhaustive test
|
||||
* as it assumes a rest argument can only be an array type (either T[], or Array<T>).
|
||||
*
|
||||
* @param node The type node.
|
||||
*/
|
||||
function getRestParameterElementType(node: TypeNode) {
|
||||
if (node && node.kind === SyntaxKind.ArrayType) {
|
||||
return (<ArrayTypeNode>node).elementType;
|
||||
}
|
||||
else if (node && node.kind === SyntaxKind.TypeReference) {
|
||||
return singleOrUndefined((<TypeReferenceNode>node).typeArguments);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the types of the parameters of a node for use with decorator type metadata.
|
||||
*
|
||||
* @param node The node that should have its parameter types serialized.
|
||||
*/
|
||||
function serializeParameterTypesOfNode(node: Node): Expression {
|
||||
function serializeParameterTypesOfNode(node: Node, container: ClassLikeDeclaration): ArrayLiteralExpression {
|
||||
const valueDeclaration =
|
||||
isClassLike(node)
|
||||
? getFirstConstructorWithBody(node)
|
||||
@@ -1602,9 +1593,9 @@ namespace ts {
|
||||
? node
|
||||
: undefined;
|
||||
|
||||
const expressions: Expression[] = [];
|
||||
const expressions: SerializedTypeNode[] = [];
|
||||
if (valueDeclaration) {
|
||||
const parameters = valueDeclaration.parameters;
|
||||
const parameters = getParametersOfDecoratedDeclaration(valueDeclaration, container);
|
||||
const numParameters = parameters.length;
|
||||
for (let i = 0; i < numParameters; i++) {
|
||||
const parameter = parameters[i];
|
||||
@@ -1623,12 +1614,22 @@ namespace ts {
|
||||
return createArrayLiteral(expressions);
|
||||
}
|
||||
|
||||
function getParametersOfDecoratedDeclaration(node: FunctionLikeDeclaration, container: ClassLikeDeclaration) {
|
||||
if (container && node.kind === SyntaxKind.GetAccessor) {
|
||||
const { setAccessor } = getAllAccessorDeclarations(container.members, <AccessorDeclaration>node);
|
||||
if (setAccessor) {
|
||||
return setAccessor.parameters;
|
||||
}
|
||||
}
|
||||
return node.parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the return type of a node for use with decorator type metadata.
|
||||
*
|
||||
* @param node The node that should have its return type serialized.
|
||||
*/
|
||||
function serializeReturnTypeOfNode(node: Node): Expression {
|
||||
function serializeReturnTypeOfNode(node: Node): SerializedTypeNode {
|
||||
if (isFunctionLike(node) && node.type) {
|
||||
return serializeTypeNode(node.type);
|
||||
}
|
||||
@@ -1657,13 +1658,16 @@ namespace ts {
|
||||
*
|
||||
* @param node The type node to serialize.
|
||||
*/
|
||||
function serializeTypeNode(node: TypeNode): Expression {
|
||||
function serializeTypeNode(node: TypeNode): SerializedTypeNode {
|
||||
if (node === undefined) {
|
||||
return createIdentifier("Object");
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
return createVoidZero();
|
||||
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
@@ -1715,37 +1719,8 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.UnionType:
|
||||
{
|
||||
const unionOrIntersection = <UnionOrIntersectionTypeNode>node;
|
||||
let serializedUnion: Identifier;
|
||||
for (const typeNode of unionOrIntersection.types) {
|
||||
const serializedIndividual = serializeTypeNode(typeNode) as Identifier;
|
||||
// Non identifier
|
||||
if (serializedIndividual.kind !== SyntaxKind.Identifier) {
|
||||
serializedUnion = undefined;
|
||||
break;
|
||||
}
|
||||
return serializeUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
|
||||
|
||||
// One of the individual is global object, return immediately
|
||||
if (serializedIndividual.text === "Object") {
|
||||
return serializedIndividual;
|
||||
}
|
||||
|
||||
// Different types
|
||||
if (serializedUnion && serializedUnion.text !== serializedIndividual.text) {
|
||||
serializedUnion = undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
serializedUnion = serializedIndividual;
|
||||
}
|
||||
|
||||
// If we were able to find common type
|
||||
if (serializedUnion) {
|
||||
return serializedUnion;
|
||||
}
|
||||
}
|
||||
// Fallthrough
|
||||
case SyntaxKind.TypeQuery:
|
||||
case SyntaxKind.TypeOperator:
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
@@ -1763,13 +1738,48 @@ namespace ts {
|
||||
return createIdentifier("Object");
|
||||
}
|
||||
|
||||
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
|
||||
let serializedUnion: SerializedTypeNode;
|
||||
for (const typeNode of node.types) {
|
||||
const serializedIndividual = serializeTypeNode(typeNode);
|
||||
|
||||
if (isVoidExpression(serializedIndividual)) {
|
||||
// If we dont have any other type already set, set the initial type
|
||||
if (!serializedUnion) {
|
||||
serializedUnion = serializedIndividual;
|
||||
}
|
||||
}
|
||||
else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
|
||||
// One of the individual is global object, return immediately
|
||||
return serializedIndividual;
|
||||
}
|
||||
// If there exists union that is not void 0 expression, check if the the common type is identifier.
|
||||
// anything more complex and we will just default to Object
|
||||
else if (serializedUnion && !isVoidExpression(serializedUnion)) {
|
||||
// Different types
|
||||
if (!isIdentifier(serializedUnion) ||
|
||||
!isIdentifier(serializedIndividual) ||
|
||||
serializedUnion.text !== serializedIndividual.text) {
|
||||
return createIdentifier("Object");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Initialize the union type
|
||||
serializedUnion = serializedIndividual;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were able to find common type, use it
|
||||
return serializedUnion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a TypeReferenceNode to an appropriate JS constructor value for use with
|
||||
* decorator type metadata.
|
||||
*
|
||||
* @param node The type reference node.
|
||||
*/
|
||||
function serializeTypeReferenceNode(node: TypeReferenceNode) {
|
||||
function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode {
|
||||
switch (resolver.getTypeReferenceSerializationKind(node.typeName, currentScope)) {
|
||||
case TypeReferenceSerializationKind.Unknown:
|
||||
const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true);
|
||||
@@ -1824,7 +1834,7 @@ namespace ts {
|
||||
* @param useFallback A value indicating whether to use logical operators to test for the
|
||||
* entity name at runtime.
|
||||
*/
|
||||
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression {
|
||||
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): SerializedEntityNameAsExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
// Create a clone of the name with a new parent, and treat it as if it were
|
||||
@@ -1857,8 +1867,8 @@ namespace ts {
|
||||
* @param useFallback A value indicating whether to use logical operators to test for the
|
||||
* qualified name at runtime.
|
||||
*/
|
||||
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression {
|
||||
let left: Expression;
|
||||
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): PropertyAccessExpression {
|
||||
let left: SerializedEntityNameAsExpression;
|
||||
if (node.left.kind === SyntaxKind.Identifier) {
|
||||
left = serializeEntityNameAsExpression(node.left, useFallback);
|
||||
}
|
||||
@@ -1883,7 +1893,7 @@ namespace ts {
|
||||
* Gets an expression that points to the global "Symbol" constructor at runtime if it is
|
||||
* available.
|
||||
*/
|
||||
function getGlobalSymbolNameWithFallback(): Expression {
|
||||
function getGlobalSymbolNameWithFallback(): ConditionalExpression {
|
||||
return createConditional(
|
||||
createTypeCheck(createIdentifier("Symbol"), "function"),
|
||||
createIdentifier("Symbol"),
|
||||
@@ -1905,7 +1915,7 @@ namespace ts {
|
||||
: (<ComputedPropertyName>name).expression;
|
||||
}
|
||||
else if (isIdentifier(name)) {
|
||||
return createLiteral(name.text);
|
||||
return createLiteral(unescapeIdentifier(name.text));
|
||||
}
|
||||
else {
|
||||
return getSynthesizedClone(name);
|
||||
@@ -2528,8 +2538,8 @@ namespace ts {
|
||||
currentScopeFirstDeclarationsOfName = createMap<Node>();
|
||||
}
|
||||
|
||||
if (!(name in currentScopeFirstDeclarationsOfName)) {
|
||||
currentScopeFirstDeclarationsOfName[name] = node;
|
||||
if (!currentScopeFirstDeclarationsOfName.has(name)) {
|
||||
currentScopeFirstDeclarationsOfName.set(name, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2542,7 +2552,7 @@ namespace ts {
|
||||
if (currentScopeFirstDeclarationsOfName) {
|
||||
const name = node.symbol && node.symbol.name;
|
||||
if (name) {
|
||||
return currentScopeFirstDeclarationsOfName[name] === node;
|
||||
return currentScopeFirstDeclarationsOfName.get(name) === node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2721,7 +2731,7 @@ namespace ts {
|
||||
let blockLocation: TextRange;
|
||||
const body = node.body;
|
||||
if (body.kind === SyntaxKind.ModuleBlock) {
|
||||
addRange(statements, visitNodes((<ModuleBlock>body).statements, namespaceElementVisitor, isStatement));
|
||||
saveStateAndInvoke(body, body => addRange(statements, visitNodes((<ModuleBlock>body).statements, namespaceElementVisitor, isStatement)));
|
||||
statementsLocation = (<ModuleBlock>body).statements;
|
||||
blockLocation = body;
|
||||
}
|
||||
@@ -3115,7 +3125,7 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.Identifier);
|
||||
|
||||
// Keep track of class aliases.
|
||||
classAliases = createMap<Identifier>();
|
||||
classAliases = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-78
@@ -1,4 +1,4 @@
|
||||
/// <reference path="program.ts"/>
|
||||
/// <reference path="program.ts"/>
|
||||
/// <reference path="commandLineParser.ts"/>
|
||||
|
||||
namespace ts {
|
||||
@@ -43,66 +43,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the locale is in the appropriate format,
|
||||
* and if it is, attempts to set the appropriate language.
|
||||
*/
|
||||
function validateLocaleAndSetLanguage(locale: string, errors: Diagnostic[]): boolean {
|
||||
const matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.exec(locale.toLowerCase());
|
||||
|
||||
if (!matchResult) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, "en", "ja-jp"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const language = matchResult[1];
|
||||
const territory = matchResult[3];
|
||||
|
||||
// First try the entire locale, then fall back to just language if that's all we have.
|
||||
// Either ways do not fail, and fallback to the English diagnostic strings.
|
||||
if (!trySetLanguageAndTerritory(language, territory, errors)) {
|
||||
trySetLanguageAndTerritory(language, undefined, errors);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function trySetLanguageAndTerritory(language: string, territory: string, errors: Diagnostic[]): boolean {
|
||||
const compilerFilePath = normalizePath(sys.getExecutingFilePath());
|
||||
const containingDirectoryPath = getDirectoryPath(compilerFilePath);
|
||||
|
||||
let filePath = combinePaths(containingDirectoryPath, language);
|
||||
|
||||
if (territory) {
|
||||
filePath = filePath + "-" + territory;
|
||||
}
|
||||
|
||||
filePath = sys.resolvePath(combinePaths(filePath, "diagnosticMessages.generated.json"));
|
||||
|
||||
if (!sys.fileExists(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Add codePage support for readFile?
|
||||
let fileContents = "";
|
||||
try {
|
||||
fileContents = sys.readFile(filePath);
|
||||
}
|
||||
catch (e) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath));
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ts.localizedDiagnosticMessages = JSON.parse(fileContents);
|
||||
}
|
||||
catch (e) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function countLines(program: Program): number {
|
||||
let count = 0;
|
||||
forEach(program.getSourceFiles(), file => {
|
||||
@@ -127,11 +67,13 @@ namespace ts {
|
||||
const gutterSeparator = " ";
|
||||
const resetEscapeSequence = "\u001b[0m";
|
||||
const ellipsis = "...";
|
||||
const categoryFormatMap = createMap<string>({
|
||||
[DiagnosticCategory.Warning]: yellowForegroundEscapeSequence,
|
||||
[DiagnosticCategory.Error]: redForegroundEscapeSequence,
|
||||
[DiagnosticCategory.Message]: blueForegroundEscapeSequence,
|
||||
});
|
||||
function getCategoryFormat(category: DiagnosticCategory): string {
|
||||
switch (category) {
|
||||
case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence;
|
||||
case DiagnosticCategory.Error: return redForegroundEscapeSequence;
|
||||
case DiagnosticCategory.Message: return blueForegroundEscapeSequence;
|
||||
}
|
||||
}
|
||||
|
||||
function formatAndReset(text: string, formatStyle: string) {
|
||||
return formatStyle + text + resetEscapeSequence;
|
||||
@@ -199,7 +141,7 @@ namespace ts {
|
||||
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
|
||||
}
|
||||
|
||||
const categoryColor = categoryFormatMap[diagnostic.category];
|
||||
const categoryColor = getCategoryFormat(diagnostic.category);
|
||||
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
|
||||
output += sys.newLine + sys.newLine;
|
||||
@@ -215,7 +157,7 @@ namespace ts {
|
||||
output += `${ diagnostic.file.fileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
|
||||
}
|
||||
|
||||
output += `${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
|
||||
output += `${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine + sys.newLine + sys.newLine }`;
|
||||
|
||||
sys.write(output);
|
||||
}
|
||||
@@ -263,7 +205,7 @@ namespace ts {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"), /* host */ undefined);
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
validateLocaleAndSetLanguage(commandLine.options.locale, commandLine.errors);
|
||||
validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors);
|
||||
}
|
||||
|
||||
// If there are any errors due to command line parsing and/or
|
||||
@@ -438,9 +380,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function cachedFileExists(fileName: string): boolean {
|
||||
return fileName in cachedExistingFiles
|
||||
? cachedExistingFiles[fileName]
|
||||
: cachedExistingFiles[fileName] = hostFileExists(fileName);
|
||||
let fileExists = cachedExistingFiles.get(fileName);
|
||||
if (fileExists === undefined) {
|
||||
cachedExistingFiles.set(fileName, fileExists = hostFileExists(fileName));
|
||||
}
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) {
|
||||
@@ -734,13 +678,9 @@ namespace ts {
|
||||
|
||||
if (option.name === "lib") {
|
||||
description = getDiagnosticText(option.description);
|
||||
const options: string[] = [];
|
||||
const element = (<CommandLineOptionOfListType>option).element;
|
||||
const typeMap = <Map<number | string>>element.type;
|
||||
for (const key in typeMap) {
|
||||
options.push(`'${key}'`);
|
||||
}
|
||||
optionsDescriptionMap[description] = options;
|
||||
optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`));
|
||||
}
|
||||
else {
|
||||
description = getDiagnosticText(option.description);
|
||||
@@ -762,7 +702,7 @@ namespace ts {
|
||||
for (let i = 0; i < usageColumn.length; i++) {
|
||||
const usage = usageColumn[i];
|
||||
const description = descriptionColumn[i];
|
||||
const kindsList = optionsDescriptionMap[description];
|
||||
const kindsList = optionsDescriptionMap.get(description);
|
||||
output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
|
||||
|
||||
if (kindsList) {
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"outFile": "../../built/local/tsc.js",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
"declaration": true
|
||||
},
|
||||
"files": [
|
||||
"core.ts",
|
||||
|
||||
+166
-75
@@ -1,11 +1,30 @@
|
||||
namespace ts {
|
||||
|
||||
namespace ts {
|
||||
/**
|
||||
* Type of objects whose values are all of the same type.
|
||||
* The `in` and `for-in` operators can *not* be safely used,
|
||||
* since `Object.prototype` may be modified by outside code.
|
||||
*/
|
||||
export interface MapLike<T> {
|
||||
[index: string]: T;
|
||||
}
|
||||
|
||||
export interface Map<T> extends MapLike<T> {
|
||||
__mapBrand: any;
|
||||
/** ES6 Map interface. */
|
||||
export interface Map<T> {
|
||||
get(key: string): T;
|
||||
has(key: string): boolean;
|
||||
set(key: string, value: T): this;
|
||||
delete(key: string): boolean;
|
||||
clear(): void;
|
||||
forEach(action: (value: T, key: string) => void): void;
|
||||
readonly size: number;
|
||||
keys(): Iterator<string>;
|
||||
values(): Iterator<T>;
|
||||
entries(): Iterator<[string, T]>;
|
||||
}
|
||||
|
||||
/** ES6 Iterator type. */
|
||||
export interface Iterator<T> {
|
||||
next(): { value: T, done: false } | { value: never, done: true };
|
||||
}
|
||||
|
||||
// branded string type used to store absolute, normalized and canonicalized paths
|
||||
@@ -175,6 +194,7 @@ namespace ts {
|
||||
ReadonlyKeyword,
|
||||
RequireKeyword,
|
||||
NumberKeyword,
|
||||
ObjectKeyword,
|
||||
SetKeyword,
|
||||
StringKeyword,
|
||||
SymbolKeyword,
|
||||
@@ -253,6 +273,7 @@ namespace ts {
|
||||
ExpressionWithTypeArguments,
|
||||
AsExpression,
|
||||
NonNullExpression,
|
||||
MetaProperty,
|
||||
|
||||
// Misc
|
||||
TemplateSpan,
|
||||
@@ -349,6 +370,7 @@ namespace ts {
|
||||
JSDocThisType,
|
||||
JSDocComment,
|
||||
JSDocTag,
|
||||
JSDocAugmentsTag,
|
||||
JSDocParameterTag,
|
||||
JSDocReturnTag,
|
||||
JSDocTypeTag,
|
||||
@@ -369,7 +391,6 @@ namespace ts {
|
||||
PartiallyEmittedExpression,
|
||||
MergeDeclarationMarker,
|
||||
EndOfDeclarationMarker,
|
||||
RawExpression,
|
||||
|
||||
// Enum value count
|
||||
Count,
|
||||
@@ -400,7 +421,7 @@ namespace ts {
|
||||
LastBinaryOperator = CaretEqualsToken,
|
||||
FirstNode = QualifiedName,
|
||||
FirstJSDocNode = JSDocTypeExpression,
|
||||
LastJSDocNode = JSDocLiteralType,
|
||||
LastJSDocNode = JSDocNeverKeyword,
|
||||
FirstJSDocTagNode = JSDocComment,
|
||||
LastJSDocTagNode = JSDocNeverKeyword
|
||||
}
|
||||
@@ -417,26 +438,20 @@ namespace ts {
|
||||
HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding)
|
||||
HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding)
|
||||
GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope
|
||||
HasClassExtends = 1 << 10, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
|
||||
HasDecorators = 1 << 11, // If the file has decorators (initialized by binding)
|
||||
HasParamDecorators = 1 << 12, // If the file has parameter decorators (initialized by binding)
|
||||
HasAsyncFunctions = 1 << 13, // If the file has async functions (initialized by binding)
|
||||
HasSpreadAttribute = 1 << 14, // If the file as JSX spread attributes (initialized by binding)
|
||||
HasRestAttribute = 1 << 15, // If the file has object destructure elements
|
||||
DisallowInContext = 1 << 16, // If node was parsed in a context where 'in-expressions' are not allowed
|
||||
YieldContext = 1 << 17, // If node was parsed in the 'yield' context created when parsing a generator
|
||||
DecoratorContext = 1 << 18, // If node was parsed as part of a decorator
|
||||
AwaitContext = 1 << 19, // If node was parsed in the 'await' context created when parsing an async function
|
||||
ThisNodeHasError = 1 << 20, // If the parser encountered an error when parsing the code that created this node
|
||||
JavaScriptFile = 1 << 21, // If node was parsed in a JavaScript
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 22, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 23, // If we've computed data from children and cached it in this node
|
||||
HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding)
|
||||
DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed
|
||||
YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator
|
||||
DecoratorContext = 1 << 13, // If node was parsed as part of a decorator
|
||||
AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function
|
||||
ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node
|
||||
JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
|
||||
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
|
||||
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions | HasSpreadAttribute | HasRestAttribute,
|
||||
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
|
||||
ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions,
|
||||
|
||||
// Parsing context flags
|
||||
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
|
||||
@@ -497,7 +512,8 @@ namespace ts {
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
|
||||
/* @internal */ jsDocComments?: JSDoc[]; // JSDoc for the node, if it has any.
|
||||
/* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
|
||||
/* @internal */ jsDocCache?: (JSDoc | JSDocTag)[]; // All JSDoc that applies to the node, including parent docs and @param tags
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
|
||||
@@ -644,9 +660,9 @@ namespace ts {
|
||||
|
||||
export interface ParameterDeclaration extends Declaration {
|
||||
kind: SyntaxKind.Parameter;
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
name: BindingName; // Declared parameter name
|
||||
questionToken?: QuestionToken; // Present on optional parameter
|
||||
questionToken?: QuestionToken; // Present on optional parameter
|
||||
type?: TypeNode; // Optional type annotation
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
@@ -662,14 +678,14 @@ namespace ts {
|
||||
export interface PropertySignature extends TypeElement {
|
||||
kind: SyntaxKind.PropertySignature | SyntaxKind.JSDocRecordMember;
|
||||
name: PropertyName; // Declared property name
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
type?: TypeNode; // Optional type annotation
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
|
||||
export interface PropertyDeclaration extends ClassElement {
|
||||
kind: SyntaxKind.PropertyDeclaration;
|
||||
questionToken?: QuestionToken; // Present for use with reporting a grammar error
|
||||
questionToken?: QuestionToken; // Present for use with reporting a grammar error
|
||||
name: PropertyName;
|
||||
type?: TypeNode;
|
||||
initializer?: Expression; // Optional initializer
|
||||
@@ -820,6 +836,7 @@ namespace ts {
|
||||
export interface KeywordTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.AnyKeyword
|
||||
| SyntaxKind.NumberKeyword
|
||||
| SyntaxKind.ObjectKeyword
|
||||
| SyntaxKind.BooleanKeyword
|
||||
| SyntaxKind.StringKeyword
|
||||
| SyntaxKind.SymbolKeyword
|
||||
@@ -1448,6 +1465,14 @@ namespace ts {
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
// NOTE: MetaProperty is really a MemberExpression, but we consider it a PrimaryExpression
|
||||
// for the same reasons we treat NewExpression as a PrimaryExpression.
|
||||
export interface MetaProperty extends PrimaryExpression {
|
||||
kind: SyntaxKind.MetaProperty;
|
||||
keywordToken: SyntaxKind;
|
||||
name: Identifier;
|
||||
}
|
||||
|
||||
/// A JSX expression of the form <TagName attrs>...</TagName>
|
||||
export interface JsxElement extends PrimaryExpression {
|
||||
kind: SyntaxKind.JsxElement;
|
||||
@@ -1496,6 +1521,7 @@ namespace ts {
|
||||
|
||||
export interface JsxExpression extends Expression {
|
||||
kind: SyntaxKind.JsxExpression;
|
||||
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
|
||||
expression?: Expression;
|
||||
}
|
||||
|
||||
@@ -1524,16 +1550,6 @@ namespace ts {
|
||||
kind: SyntaxKind.EndOfDeclarationMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a string of raw text in an expression position. Raw text is never transformed, should
|
||||
* be ES3 compliant, and should have the same precedence as PrimaryExpression.
|
||||
*/
|
||||
/* @internal */
|
||||
export interface RawExpression extends PrimaryExpression {
|
||||
kind: SyntaxKind.RawExpression;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the beginning of a merged transformed declaration.
|
||||
*/
|
||||
@@ -1555,7 +1571,7 @@ namespace ts {
|
||||
name?: Identifier;
|
||||
}
|
||||
|
||||
export type BlockLike = SourceFile | Block | ModuleBlock | CaseClause;
|
||||
export type BlockLike = SourceFile | Block | ModuleBlock | CaseOrDefaultClause;
|
||||
|
||||
export interface Block extends Statement {
|
||||
kind: SyntaxKind.Block;
|
||||
@@ -1988,6 +2004,11 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocTag;
|
||||
}
|
||||
|
||||
export interface JSDocAugmentsTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocAugmentsTag;
|
||||
typeExpression: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
export interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
@@ -2334,10 +2355,16 @@ namespace ts {
|
||||
getDeclaredTypeOfSymbol(symbol: Symbol): Type;
|
||||
getPropertiesOfType(type: Type): Symbol[];
|
||||
getPropertyOfType(type: Type, propertyName: string): Symbol;
|
||||
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo;
|
||||
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getBaseTypes(type: InterfaceType): ObjectType[];
|
||||
getBaseTypes(type: InterfaceType): BaseType[];
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
/**
|
||||
* Gets the type of a parameter at a given position in a signature.
|
||||
* Returns `any` if the index is not valid.
|
||||
*/
|
||||
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
@@ -2347,6 +2374,8 @@ namespace ts {
|
||||
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol;
|
||||
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol;
|
||||
getTypeAtLocation(node: Node): Type;
|
||||
getTypeFromTypeNode(node: TypeNode): Type;
|
||||
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
|
||||
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
|
||||
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
|
||||
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
|
||||
@@ -2365,6 +2394,8 @@ namespace ts {
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfModule(moduleSymbol: Symbol): Symbol[];
|
||||
/** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */
|
||||
/* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[];
|
||||
|
||||
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
|
||||
getJsxIntrinsicTagNames(): Symbol[];
|
||||
@@ -2372,6 +2403,7 @@ namespace ts {
|
||||
getAmbientModules(): Symbol[];
|
||||
|
||||
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
|
||||
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol;
|
||||
|
||||
@@ -2390,6 +2422,7 @@ namespace ts {
|
||||
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void;
|
||||
buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
|
||||
buildIndexSignatureDisplay(info: IndexInfo, writer: SymbolWriter, kind: IndexKind, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]): void;
|
||||
buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
@@ -2406,6 +2439,7 @@ namespace ts {
|
||||
writeSpace(text: string): void;
|
||||
writeStringLiteral(text: string): void;
|
||||
writeParameter(text: string): void;
|
||||
writeProperty(text: string): void;
|
||||
writeSymbol(text: string, symbol: Symbol): void;
|
||||
writeLine(): void;
|
||||
increaseIndent(): void;
|
||||
@@ -2432,6 +2466,7 @@ namespace ts {
|
||||
InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type
|
||||
InTypeAlias = 0x00000200, // Writing type in type alias declaration
|
||||
UseTypeAliasValue = 0x00000400, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
|
||||
SuppressAnyReturnType = 0x00000800, // If the return type is any-like, don't offer a return type.
|
||||
}
|
||||
|
||||
export const enum SymbolFormatFlags {
|
||||
@@ -2677,6 +2712,7 @@ namespace ts {
|
||||
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
|
||||
leftSpread?: Symbol; // Left source for synthetic spread property
|
||||
rightSpread?: Symbol; // Right source for synthetic spread property
|
||||
mappedTypeOrigin?: Symbol; // For a property on a mapped type, points back to the orignal 'T' from 'keyof T'.
|
||||
hasNonUniformType?: boolean; // True if constituents have non-uniform types
|
||||
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
|
||||
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
|
||||
@@ -2711,6 +2747,7 @@ namespace ts {
|
||||
TypeChecked = 0x00000001, // Node has been type checked
|
||||
LexicalThis = 0x00000002, // Lexical 'this' reference
|
||||
CaptureThis = 0x00000004, // Lexical 'this' used in body
|
||||
CaptureNewTarget = 0x00000008, // Lexical 'new.target' used in body
|
||||
SuperInstance = 0x00000100, // Instance 'super' reference
|
||||
SuperStatic = 0x00000200, // Static 'super' reference
|
||||
ContextChecked = 0x00000400, // Contextual types have been assigned
|
||||
@@ -2777,6 +2814,7 @@ namespace ts {
|
||||
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
|
||||
NonPrimitive = 1 << 24, // intrinsic object type
|
||||
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
@@ -2786,21 +2824,22 @@ namespace ts {
|
||||
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
|
||||
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
|
||||
/* @internal */
|
||||
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never,
|
||||
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
|
||||
/* @internal */
|
||||
Primitive = String | Number | Boolean | Enum | ESSymbol | Void | Undefined | Null | Literal,
|
||||
StringLike = String | StringLiteral,
|
||||
StringLike = String | StringLiteral | Index,
|
||||
NumberLike = Number | NumberLiteral | Enum | EnumLiteral,
|
||||
BooleanLike = Boolean | BooleanLiteral,
|
||||
EnumLike = Enum | EnumLiteral,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = Object | Union | Intersection,
|
||||
StructuredOrTypeParameter = StructuredType | TypeParameter | Index,
|
||||
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
|
||||
TypeVariable = TypeParameter | IndexedAccess,
|
||||
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol,
|
||||
NotUnionOrUnit = Any | ESSymbol | Object,
|
||||
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
|
||||
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
|
||||
/* @internal */
|
||||
@@ -2835,7 +2874,7 @@ namespace ts {
|
||||
|
||||
// Enum types (TypeFlags.Enum)
|
||||
export interface EnumType extends Type {
|
||||
memberTypes: Map<EnumLiteralType>;
|
||||
memberTypes: EnumLiteralType[];
|
||||
}
|
||||
|
||||
// Enum types (TypeFlags.EnumLiteral)
|
||||
@@ -2854,6 +2893,7 @@ namespace ts {
|
||||
ObjectLiteral = 1 << 7, // Originates in an object literal
|
||||
EvolvingArray = 1 << 8, // Evolving array type
|
||||
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
|
||||
NonPrimitive = 1 << 10, // NonPrimitive object type
|
||||
ClassOrInterface = Class | Interface
|
||||
}
|
||||
|
||||
@@ -2862,7 +2902,7 @@ namespace ts {
|
||||
objectFlags: ObjectFlags;
|
||||
}
|
||||
|
||||
// Class and interface types (TypeFlags.Class and TypeFlags.Interface)
|
||||
/** Class and interface types (TypeFlags.Class and TypeFlags.Interface). */
|
||||
export interface InterfaceType extends ObjectType {
|
||||
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
|
||||
outerTypeParameters: TypeParameter[]; // Outer type parameters (undefined if none)
|
||||
@@ -2871,9 +2911,12 @@ namespace ts {
|
||||
/* @internal */
|
||||
resolvedBaseConstructorType?: Type; // Resolved base constructor type of class
|
||||
/* @internal */
|
||||
resolvedBaseTypes: ObjectType[]; // Resolved base types
|
||||
resolvedBaseTypes: BaseType[]; // Resolved base types
|
||||
}
|
||||
|
||||
// Object type or intersection of object types
|
||||
export type BaseType = ObjectType | IntersectionType;
|
||||
|
||||
export interface InterfaceTypeWithDeclaredMembers extends InterfaceType {
|
||||
declaredProperties: Symbol[]; // Declared members
|
||||
declaredCallSignatures: Signature[]; // Declared call signatures
|
||||
@@ -2882,14 +2925,16 @@ namespace ts {
|
||||
declaredNumberIndexInfo: IndexInfo; // Declared numeric indexing info
|
||||
}
|
||||
|
||||
// Type references (TypeFlags.Reference). When a class or interface has type parameters or
|
||||
// a "this" type, references to the class or interface are made using type references. The
|
||||
// typeArguments property specifies the types to substitute for the type parameters of the
|
||||
// class or interface and optionally includes an extra element that specifies the type to
|
||||
// substitute for "this" in the resulting instantiation. When no extra argument is present,
|
||||
// the type reference itself is substituted for "this". The typeArguments property is undefined
|
||||
// if the class or interface has no type parameters and the reference isn't specifying an
|
||||
// explicit "this" argument.
|
||||
/**
|
||||
* Type references (TypeFlags.Reference). When a class or interface has type parameters or
|
||||
* a "this" type, references to the class or interface are made using type references. The
|
||||
* typeArguments property specifies the types to substitute for the type parameters of the
|
||||
* class or interface and optionally includes an extra element that specifies the type to
|
||||
* substitute for "this" in the resulting instantiation. When no extra argument is present,
|
||||
* the type reference itself is substituted for "this". The typeArguments property is undefined
|
||||
* if the class or interface has no type parameters and the reference isn't specifying an
|
||||
* explicit "this" argument.
|
||||
*/
|
||||
export interface TypeReference extends ObjectType {
|
||||
target: GenericType; // Type reference target
|
||||
typeArguments: Type[]; // Type reference type arguments (undefined if none)
|
||||
@@ -2904,14 +2949,23 @@ namespace ts {
|
||||
export interface UnionOrIntersectionType extends Type {
|
||||
types: Type[]; // Constituent types
|
||||
/* @internal */
|
||||
resolvedProperties: SymbolTable; // Cache of resolved properties
|
||||
propertyCache: SymbolTable; // Cache of resolved properties
|
||||
/* @internal */
|
||||
couldContainTypeParameters: boolean;
|
||||
resolvedProperties: Symbol[];
|
||||
/* @internal */
|
||||
resolvedIndexType: IndexType;
|
||||
/* @internal */
|
||||
resolvedBaseConstraint: Type;
|
||||
/* @internal */
|
||||
couldContainTypeVariables: boolean;
|
||||
}
|
||||
|
||||
export interface UnionType extends UnionOrIntersectionType { }
|
||||
|
||||
export interface IntersectionType extends UnionOrIntersectionType { }
|
||||
export interface IntersectionType extends UnionOrIntersectionType {
|
||||
/* @internal */
|
||||
resolvedApparentType: Type;
|
||||
}
|
||||
|
||||
export type StructuredType = ObjectType | UnionType | IntersectionType;
|
||||
|
||||
@@ -2928,6 +2982,7 @@ namespace ts {
|
||||
typeParameter?: TypeParameter;
|
||||
constraintType?: Type;
|
||||
templateType?: Type;
|
||||
modifiersType?: Type;
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
}
|
||||
|
||||
@@ -2962,30 +3017,35 @@ namespace ts {
|
||||
iteratorElementType?: Type;
|
||||
}
|
||||
|
||||
export interface TypeVariable extends Type {
|
||||
/* @internal */
|
||||
resolvedBaseConstraint: Type;
|
||||
/* @internal */
|
||||
resolvedIndexType: IndexType;
|
||||
}
|
||||
|
||||
// Type parameters (TypeFlags.TypeParameter)
|
||||
export interface TypeParameter extends Type {
|
||||
export interface TypeParameter extends TypeVariable {
|
||||
constraint: Type; // Constraint
|
||||
/* @internal */
|
||||
target?: TypeParameter; // Instantiation target
|
||||
/* @internal */
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
/* @internal */
|
||||
resolvedApparentType: Type;
|
||||
/* @internal */
|
||||
resolvedIndexType: IndexType;
|
||||
/* @internal */
|
||||
resolvedIndexedAccessTypes: IndexedAccessType[];
|
||||
/* @internal */
|
||||
isThisType?: boolean;
|
||||
}
|
||||
|
||||
export interface IndexType extends Type {
|
||||
type: TypeParameter;
|
||||
// Indexed access types (TypeFlags.IndexedAccess)
|
||||
// Possible forms are T[xxx], xxx[T], or xxx[keyof T], where T is a type variable
|
||||
export interface IndexedAccessType extends TypeVariable {
|
||||
objectType: Type;
|
||||
indexType: Type;
|
||||
constraint?: Type;
|
||||
}
|
||||
|
||||
export interface IndexedAccessType extends Type {
|
||||
objectType: Type;
|
||||
indexType: TypeParameter;
|
||||
// keyof T types (TypeFlags.Index)
|
||||
export interface IndexType extends Type {
|
||||
type: TypeVariable | UnionOrIntersectionType;
|
||||
}
|
||||
|
||||
export const enum SignatureKind {
|
||||
@@ -3077,6 +3137,12 @@ namespace ts {
|
||||
ThisProperty
|
||||
}
|
||||
|
||||
export interface FileExtensionInfo {
|
||||
extension: string;
|
||||
scriptKind: ScriptKind;
|
||||
isMixedContent: boolean;
|
||||
}
|
||||
|
||||
export interface DiagnosticMessage {
|
||||
key: string;
|
||||
category: DiagnosticCategory;
|
||||
@@ -3200,8 +3266,12 @@ namespace ts {
|
||||
[option: string]: CompilerOptionsValue | undefined;
|
||||
}
|
||||
|
||||
export interface TypingOptions {
|
||||
export interface TypeAcquisition {
|
||||
/* @deprecated typingOptions.enableAutoDiscovery
|
||||
* Use typeAcquisition.enable instead.
|
||||
*/
|
||||
enableAutoDiscovery?: boolean;
|
||||
enable?: boolean;
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
[option: string]: string[] | boolean | undefined;
|
||||
@@ -3212,7 +3282,7 @@ namespace ts {
|
||||
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
|
||||
typeAcquisition: TypeAcquisition; // Used to customize the type acquisition process
|
||||
compilerOptions: CompilerOptions; // Used as a source for typing inference
|
||||
unresolvedImports: ReadonlyArray<string>; // List of unresolved module ids from imports
|
||||
}
|
||||
@@ -3229,7 +3299,8 @@ namespace ts {
|
||||
export const enum JsxEmit {
|
||||
None = 0,
|
||||
Preserve = 1,
|
||||
React = 2
|
||||
React = 2,
|
||||
ReactNative = 3
|
||||
}
|
||||
|
||||
export const enum NewLineKind {
|
||||
@@ -3277,7 +3348,7 @@ namespace ts {
|
||||
/** Either a parsed command line or a parsed tsconfig.json */
|
||||
export interface ParsedCommandLine {
|
||||
options: CompilerOptions;
|
||||
typingOptions?: TypingOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
fileNames: string[];
|
||||
raw?: any;
|
||||
errors: Diagnostic[];
|
||||
@@ -3522,6 +3593,7 @@ namespace ts {
|
||||
|
||||
export interface ResolvedModuleWithFailedLookupLocations {
|
||||
resolvedModule: ResolvedModuleFull | undefined;
|
||||
/* @internal */
|
||||
failedLookupLocations: string[];
|
||||
}
|
||||
|
||||
@@ -3646,7 +3718,7 @@ namespace ts {
|
||||
flags?: EmitFlags; // Flags that customize emit
|
||||
commentRange?: TextRange; // The text range to use when emitting leading or trailing comments
|
||||
sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings
|
||||
tokenSourceMapRanges?: Map<TextRange>; // The text range to use when emitting source mappings for tokens
|
||||
tokenSourceMapRanges?: TextRange[]; // The text range to use when emitting source mappings for tokens
|
||||
constantValue?: number; // The constant value of an expression
|
||||
externalHelpersModuleName?: Identifier; // The local name for an imported helpers module
|
||||
helpers?: EmitHelper[]; // Emit helpers for the node
|
||||
@@ -3689,6 +3761,25 @@ namespace ts {
|
||||
readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node.
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the checker, this enum keeps track of external emit helpers that should be type
|
||||
* checked.
|
||||
*/
|
||||
/* @internal */
|
||||
export const enum ExternalEmitHelpers {
|
||||
Extends = 1 << 0, // __extends (used by the ES2015 class transformation)
|
||||
Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations)
|
||||
Rest = 1 << 2, // __rest (used by ESNext object rest transformation)
|
||||
Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation)
|
||||
Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation)
|
||||
Param = 1 << 5, // __param (used by TypeScript decorators transformation)
|
||||
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
|
||||
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
|
||||
|
||||
FirstEmitHelper = Extends,
|
||||
LastEmitHelper = Generator
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum EmitContext {
|
||||
SourceFile, // Emitting a SourceFile
|
||||
|
||||
+311
-374
File diff suppressed because it is too large
Load Diff
+55
-53
@@ -1,4 +1,4 @@
|
||||
/// <reference path="checker.ts" />
|
||||
/// <reference path="checker.ts" />
|
||||
/// <reference path="factory.ts" />
|
||||
/// <reference path="utilities.ts" />
|
||||
|
||||
@@ -46,54 +46,56 @@ namespace ts {
|
||||
* supplant the existing `forEachChild` implementation if performance is not
|
||||
* significantly impacted.
|
||||
*/
|
||||
const nodeEdgeTraversalMap = createMap<NodeTraversalPath>({
|
||||
[SyntaxKind.QualifiedName]: [
|
||||
{ name: "left", test: isEntityName },
|
||||
{ name: "right", test: isIdentifier }
|
||||
],
|
||||
[SyntaxKind.Decorator]: [
|
||||
{ name: "expression", test: isLeftHandSideExpression }
|
||||
],
|
||||
[SyntaxKind.TypeAssertionExpression]: [
|
||||
{ name: "type", test: isTypeNode },
|
||||
{ name: "expression", test: isUnaryExpression }
|
||||
],
|
||||
[SyntaxKind.AsExpression]: [
|
||||
{ name: "expression", test: isExpression },
|
||||
{ name: "type", test: isTypeNode }
|
||||
],
|
||||
[SyntaxKind.NonNullExpression]: [
|
||||
{ name: "expression", test: isLeftHandSideExpression }
|
||||
],
|
||||
[SyntaxKind.EnumDeclaration]: [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isIdentifier },
|
||||
{ name: "members", test: isEnumMember }
|
||||
],
|
||||
[SyntaxKind.ModuleDeclaration]: [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isModuleName },
|
||||
{ name: "body", test: isModuleBody }
|
||||
],
|
||||
[SyntaxKind.ModuleBlock]: [
|
||||
{ name: "statements", test: isStatement }
|
||||
],
|
||||
[SyntaxKind.ImportEqualsDeclaration]: [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isIdentifier },
|
||||
{ name: "moduleReference", test: isModuleReference }
|
||||
],
|
||||
[SyntaxKind.ExternalModuleReference]: [
|
||||
{ name: "expression", test: isExpression, optional: true }
|
||||
],
|
||||
[SyntaxKind.EnumMember]: [
|
||||
{ name: "name", test: isPropertyName },
|
||||
{ name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }
|
||||
]
|
||||
});
|
||||
function getNodeEdgeTraversal(kind: SyntaxKind): NodeTraversalPath {
|
||||
switch (kind) {
|
||||
case SyntaxKind.QualifiedName: return [
|
||||
{ name: "left", test: isEntityName },
|
||||
{ name: "right", test: isIdentifier }
|
||||
];
|
||||
case SyntaxKind.Decorator: return [
|
||||
{ name: "expression", test: isLeftHandSideExpression }
|
||||
];
|
||||
case SyntaxKind.TypeAssertionExpression: return [
|
||||
{ name: "type", test: isTypeNode },
|
||||
{ name: "expression", test: isUnaryExpression }
|
||||
];
|
||||
case SyntaxKind.AsExpression: return [
|
||||
{ name: "expression", test: isExpression },
|
||||
{ name: "type", test: isTypeNode }
|
||||
];
|
||||
case SyntaxKind.NonNullExpression: return [
|
||||
{ name: "expression", test: isLeftHandSideExpression }
|
||||
];
|
||||
case SyntaxKind.EnumDeclaration: return [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isIdentifier },
|
||||
{ name: "members", test: isEnumMember }
|
||||
];
|
||||
case SyntaxKind.ModuleDeclaration: return [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isModuleName },
|
||||
{ name: "body", test: isModuleBody }
|
||||
];
|
||||
case SyntaxKind.ModuleBlock: return [
|
||||
{ name: "statements", test: isStatement }
|
||||
];
|
||||
case SyntaxKind.ImportEqualsDeclaration: return [
|
||||
{ name: "decorators", test: isDecorator },
|
||||
{ name: "modifiers", test: isModifier },
|
||||
{ name: "name", test: isIdentifier },
|
||||
{ name: "moduleReference", test: isModuleReference }
|
||||
];
|
||||
case SyntaxKind.ExternalModuleReference: return [
|
||||
{ name: "expression", test: isExpression, optional: true }
|
||||
];
|
||||
case SyntaxKind.EnumMember: return [
|
||||
{ name: "name", test: isPropertyName },
|
||||
{ name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function reduceNode<T>(node: Node, f: (memo: T, node: Node) => T, initial: T) {
|
||||
return node ? f(initial, node) : initial;
|
||||
@@ -530,7 +532,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
default:
|
||||
const edgeTraversalPath = nodeEdgeTraversalMap[kind];
|
||||
const edgeTraversalPath = getNodeEdgeTraversal(kind);
|
||||
if (edgeTraversalPath) {
|
||||
for (const edge of edgeTraversalPath) {
|
||||
const value = (<MapLike<any>>node)[edge.name];
|
||||
@@ -1188,10 +1190,10 @@ namespace ts {
|
||||
|
||||
default:
|
||||
let updated: Node & MapLike<any>;
|
||||
const edgeTraversalPath = nodeEdgeTraversalMap[kind];
|
||||
const edgeTraversalPath = getNodeEdgeTraversal(kind);
|
||||
if (edgeTraversalPath) {
|
||||
for (const edge of edgeTraversalPath) {
|
||||
const value = <Node | NodeArray<Node>>(<Node & Map<any>>node)[edge.name];
|
||||
const value = <Node | NodeArray<Node>>(<Node & MapLike<any>>node)[edge.name];
|
||||
if (value !== undefined) {
|
||||
const visited = isArray(value)
|
||||
? visitNodes(value, visitor, edge.test, 0, value.length, edge.parenthesize, node)
|
||||
@@ -1328,7 +1330,7 @@ namespace ts {
|
||||
function aggregateTransformFlagsForSubtree(node: Node): TransformFlags {
|
||||
// We do not transform ambient declarations or types, so there is no need to
|
||||
// recursively aggregate transform flags.
|
||||
if (hasModifier(node, ModifierFlags.Ambient) || isTypeNode(node)) {
|
||||
if (hasModifier(node, ModifierFlags.Ambient) || (isTypeNode(node) && node.kind !== SyntaxKind.ExpressionWithTypeArguments)) {
|
||||
return TransformFlags.None;
|
||||
}
|
||||
|
||||
|
||||
+323
-211
@@ -40,10 +40,18 @@ namespace FourSlash {
|
||||
files: FourSlashFile[];
|
||||
|
||||
// A mapping from marker names to name/position pairs
|
||||
markerPositions: { [index: string]: Marker; };
|
||||
markerPositions: ts.Map<Marker>;
|
||||
|
||||
markers: Marker[];
|
||||
|
||||
/**
|
||||
* Inserted in source files by surrounding desired text
|
||||
* in a range with `[|` and `|]`. For example,
|
||||
*
|
||||
* [|text in range|]
|
||||
*
|
||||
* is a range with `text in range` "selected".
|
||||
*/
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
@@ -53,10 +61,6 @@ namespace FourSlash {
|
||||
data?: any;
|
||||
}
|
||||
|
||||
interface MarkerMap {
|
||||
[index: string]: Marker;
|
||||
}
|
||||
|
||||
export interface Range {
|
||||
fileName: string;
|
||||
start: number;
|
||||
@@ -86,7 +90,7 @@ namespace FourSlash {
|
||||
|
||||
export import IndentStyle = ts.IndentStyle;
|
||||
|
||||
const entityMap = ts.createMap({
|
||||
const entityMap = ts.createMapFromTemplate({
|
||||
"&": "&",
|
||||
"\"": """,
|
||||
"'": "'",
|
||||
@@ -96,7 +100,7 @@ namespace FourSlash {
|
||||
});
|
||||
|
||||
export function escapeXmlAttributeValue(s: string) {
|
||||
return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]);
|
||||
return s.replace(/[&<>"'\/]/g, ch => entityMap.get(ch));
|
||||
}
|
||||
|
||||
// Name of testcase metadata including ts.CompilerOptions properties that will be used by globalOptions
|
||||
@@ -222,7 +226,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
function tryAdd(path: string) {
|
||||
const inputFile = inputFiles[path];
|
||||
const inputFile = inputFiles.get(path);
|
||||
if (inputFile && !Harness.isDefaultLibraryFile(path)) {
|
||||
languageServiceAdapterHost.addScript(path, inputFile, /*isRootFile*/ true);
|
||||
return true;
|
||||
@@ -254,23 +258,12 @@ namespace FourSlash {
|
||||
// Initialize the language service with all the scripts
|
||||
let startResolveFileRef: FourSlashFile;
|
||||
|
||||
let configFileName: string;
|
||||
ts.forEach(testData.files, file => {
|
||||
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
|
||||
this.inputFiles[file.fileName] = file.content;
|
||||
|
||||
this.inputFiles.set(file.fileName, file.content);
|
||||
if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") {
|
||||
const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content);
|
||||
assert.isTrue(configJson.config !== undefined);
|
||||
|
||||
// Extend our existing compiler options so that we can also support tsconfig only options
|
||||
if (configJson.config.compilerOptions) {
|
||||
const baseDirectory = ts.normalizePath(ts.getDirectoryPath(file.fileName));
|
||||
const tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDirectory, file.fileName);
|
||||
|
||||
if (!tsConfig.errors || !tsConfig.errors.length) {
|
||||
compilationOptions = ts.extend(compilationOptions, tsConfig.options);
|
||||
}
|
||||
}
|
||||
configFileName = file.fileName;
|
||||
}
|
||||
|
||||
if (!startResolveFileRef && file.fileOptions[metadataOptionNames.resolveReference] === "true") {
|
||||
@@ -282,6 +275,21 @@ namespace FourSlash {
|
||||
}
|
||||
});
|
||||
|
||||
if (configFileName) {
|
||||
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
|
||||
const host = new Utils.MockParseConfigHost(baseDir, /*ignoreCase*/ false, this.inputFiles);
|
||||
|
||||
const configJsonObj = ts.parseConfigFileTextToJson(configFileName, this.inputFiles.get(configFileName));
|
||||
assert.isTrue(configJsonObj.config !== undefined);
|
||||
|
||||
const { options, errors } = ts.parseJsonConfigFileContent(configJsonObj.config, host, baseDir);
|
||||
|
||||
// Extend our existing compiler options so that we can also support tsconfig only options
|
||||
if (!errors || errors.length === 0) {
|
||||
compilationOptions = ts.extend(compilationOptions, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (compilationOptions.typeRoots) {
|
||||
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
|
||||
@@ -322,11 +330,11 @@ namespace FourSlash {
|
||||
}
|
||||
else {
|
||||
// resolveReference file-option is not specified then do not resolve any files and include all inputFiles
|
||||
for (const fileName in this.inputFiles) {
|
||||
this.inputFiles.forEach((file, fileName) => {
|
||||
if (!Harness.isDefaultLibraryFile(fileName)) {
|
||||
this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true);
|
||||
this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
|
||||
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
|
||||
}
|
||||
@@ -341,6 +349,7 @@ namespace FourSlash {
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
insertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterKeywordsInControlFlowStatements: true,
|
||||
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
@@ -363,8 +372,8 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
// Entry points from fourslash.ts
|
||||
public goToMarker(name = "") {
|
||||
const marker = this.getMarkerByName(name);
|
||||
public goToMarker(name: string | Marker = "") {
|
||||
const marker = typeof name === "string" ? this.getMarkerByName(name) : name;
|
||||
if (this.activeFile.fileName !== marker.fileName) {
|
||||
this.openFile(marker.fileName);
|
||||
}
|
||||
@@ -373,10 +382,37 @@ namespace FourSlash {
|
||||
if (marker.position === -1 || marker.position > content.length) {
|
||||
throw new Error(`Marker "${name}" has been invalidated by unrecoverable edits to the file.`);
|
||||
}
|
||||
this.lastKnownMarker = name;
|
||||
const mName = typeof name === "string" ? name : this.markerName(marker);
|
||||
this.lastKnownMarker = mName;
|
||||
this.goToPosition(marker.position);
|
||||
}
|
||||
|
||||
public goToEachMarker(action: () => void) {
|
||||
const markers = this.getMarkers();
|
||||
assert(markers.length);
|
||||
for (const marker of markers) {
|
||||
this.goToMarker(marker);
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
public goToEachRange(action: () => void) {
|
||||
const ranges = this.getRanges();
|
||||
assert(ranges.length);
|
||||
for (const range of ranges) {
|
||||
this.goToRangeStart(range);
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
private markerName(m: Marker): string {
|
||||
return ts.forEachEntry(this.testData.markerPositions, (marker, name) => {
|
||||
if (marker === m) {
|
||||
return name;
|
||||
}
|
||||
})!;
|
||||
}
|
||||
|
||||
public goToPosition(pos: number) {
|
||||
this.currentCaretPosition = pos;
|
||||
}
|
||||
@@ -412,8 +448,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private raiseError(message: string) {
|
||||
message = this.messageAtLastKnownMarker(message);
|
||||
throw new Error(message);
|
||||
throw new Error(this.messageAtLastKnownMarker(message));
|
||||
}
|
||||
|
||||
private messageAtLastKnownMarker(message: string) {
|
||||
@@ -478,7 +513,7 @@ namespace FourSlash {
|
||||
endPos = endMarker.position;
|
||||
}
|
||||
|
||||
errors.forEach(function(error: ts.Diagnostic) {
|
||||
errors.forEach(function (error: ts.Diagnostic) {
|
||||
if (predicate(error.start, error.start + error.length, startPos, endPos)) {
|
||||
exists = true;
|
||||
}
|
||||
@@ -495,7 +530,7 @@ namespace FourSlash {
|
||||
Harness.IO.log("Unexpected error(s) found. Error list is:");
|
||||
}
|
||||
|
||||
errors.forEach(function(error: ts.Diagnostic) {
|
||||
errors.forEach(function (error: ts.Diagnostic) {
|
||||
Harness.IO.log(" minChar: " + error.start +
|
||||
", limChar: " + (error.start + error.length) +
|
||||
", message: " + ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()) + "\n");
|
||||
@@ -527,53 +562,66 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public verifyGoToDefinitionIs(endMarker: string | string[]) {
|
||||
this.verifyGoToDefinitionWorker(endMarker instanceof Array ? endMarker : [endMarker]);
|
||||
this.verifyGoToXWorker(endMarker instanceof Array ? endMarker : [endMarker], () => this.getGoToDefinition());
|
||||
}
|
||||
|
||||
public verifyGoToDefinition(arg0: any, endMarkerNames?: string | string[]) {
|
||||
this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinition());
|
||||
}
|
||||
|
||||
private getGoToDefinition(): ts.DefinitionInfo[] {
|
||||
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)
|
||||
}
|
||||
|
||||
public verifyGoToType(arg0: any, endMarkerNames?: string | string[]) {
|
||||
this.verifyGoToX(arg0, endMarkerNames, () =>
|
||||
this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition));
|
||||
}
|
||||
|
||||
private verifyGoToX(arg0: any, endMarkerNames: string | string[] | undefined, getDefs: () => ts.DefinitionInfo[] | undefined) {
|
||||
if (endMarkerNames) {
|
||||
this.verifyGoToDefinitionPlain(arg0, endMarkerNames);
|
||||
this.verifyGoToXPlain(arg0, endMarkerNames, getDefs);
|
||||
}
|
||||
else if (arg0 instanceof Array) {
|
||||
const pairs: [string | string[], string | string[]][] = arg0;
|
||||
for (const [start, end] of pairs) {
|
||||
this.verifyGoToDefinitionPlain(start, end);
|
||||
this.verifyGoToXPlain(start, end, getDefs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const obj: { [startMarkerName: string]: string | string[] } = arg0;
|
||||
for (const startMarkerName in obj) {
|
||||
if (ts.hasProperty(obj, startMarkerName)) {
|
||||
this.verifyGoToDefinitionPlain(startMarkerName, obj[startMarkerName]);
|
||||
this.verifyGoToXPlain(startMarkerName, obj[startMarkerName], getDefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private verifyGoToDefinitionPlain(startMarkerNames: string | string[], endMarkerNames: string | string[]) {
|
||||
private verifyGoToXPlain(startMarkerNames: string | string[], endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
|
||||
if (startMarkerNames instanceof Array) {
|
||||
for (const start of startMarkerNames) {
|
||||
this.verifyGoToDefinitionSingle(start, endMarkerNames);
|
||||
this.verifyGoToXSingle(start, endMarkerNames, getDefs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.verifyGoToDefinitionSingle(startMarkerNames, endMarkerNames);
|
||||
this.verifyGoToXSingle(startMarkerNames, endMarkerNames, getDefs);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyGoToDefinitionForMarkers(markerNames: string[]) {
|
||||
for (const markerName of markerNames) {
|
||||
this.verifyGoToDefinitionSingle(`${markerName}Reference`, `${markerName}Definition`);
|
||||
this.verifyGoToXSingle(`${markerName}Reference`, `${markerName}Definition`, () => this.getGoToDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private verifyGoToDefinitionSingle(startMarkerName: string, endMarkerNames: string | string[]) {
|
||||
private verifyGoToXSingle(startMarkerName: string, endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
|
||||
this.goToMarker(startMarkerName);
|
||||
this.verifyGoToDefinitionWorker(endMarkerNames instanceof Array ? endMarkerNames : [endMarkerNames]);
|
||||
this.verifyGoToXWorker(endMarkerNames instanceof Array ? endMarkerNames : [endMarkerNames], getDefs);
|
||||
}
|
||||
|
||||
private verifyGoToDefinitionWorker(endMarkers: string[]) {
|
||||
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition) || [];
|
||||
private verifyGoToXWorker(endMarkers: string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
|
||||
const definitions = getDefs() || [];
|
||||
|
||||
if (endMarkers.length !== definitions.length) {
|
||||
this.raiseError(`goToDefinitions failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
|
||||
@@ -607,23 +655,13 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
public verifyMemberListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if (members) {
|
||||
this.assertItemInCompletionList(members.entries, symbol, text, documentation, kind);
|
||||
}
|
||||
else {
|
||||
this.raiseError("Expected a member list, but none was provided");
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListCount(expectedCount: number, negative: boolean) {
|
||||
public verifyCompletionListCount(expectedCount: number, negative: boolean) {
|
||||
if (expectedCount === 0 && negative) {
|
||||
this.verifyMemberListIsEmpty(/*negative*/ false);
|
||||
this.verifyCompletionListIsEmpty(/*negative*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
const members = this.getMemberListAtCaret();
|
||||
const members = this.getCompletionListAtCaret();
|
||||
|
||||
if (members) {
|
||||
const match = members.entries.length === expectedCount;
|
||||
@@ -637,13 +675,6 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListDoesNotContain(symbol: string) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if (members && members.entries.filter(e => e.name === symbol).length !== 0) {
|
||||
this.raiseError(`Member list did contain ${symbol}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
const itemsCount = completions.entries.length;
|
||||
@@ -676,25 +707,16 @@ namespace FourSlash {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
const uniqueItems = ts.createMap<string>();
|
||||
for (const item of completions.entries) {
|
||||
if (!(item.name in uniqueItems)) {
|
||||
uniqueItems[item.name] = item.kind;
|
||||
const uniqueItem = uniqueItems.get(item.name);
|
||||
if (!uniqueItem) {
|
||||
uniqueItems.set(item.name, item.kind);
|
||||
}
|
||||
else {
|
||||
assert.equal(item.kind, uniqueItems[item.name], `Items should have the same kind, got ${item.kind} and ${uniqueItems[item.name]}`);
|
||||
assert.equal(item.kind, uniqueItem, `Items should have the same kind, got ${item.kind} and ${uniqueItem}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListIsEmpty(negative: boolean) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if ((!members || members.entries.length === 0) && negative) {
|
||||
this.raiseError("Member list is empty at Caret");
|
||||
}
|
||||
else if ((members && members.entries.length !== 0) && !negative) {
|
||||
this.raiseError(`Member list is not empty at Caret:\nMember List contains: ${stringify(members.entries.map(e => e.name))}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListIsEmpty(negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
if ((!completions || completions.entries.length === 0) && negative) {
|
||||
@@ -724,6 +746,27 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionsAt(markerName: string, expected: string[]) {
|
||||
this.goToMarker(markerName);
|
||||
|
||||
const actualCompletions = this.getCompletionListAtCaret();
|
||||
if (!actualCompletions) {
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
|
||||
}
|
||||
|
||||
const actual = actualCompletions.entries;
|
||||
|
||||
if (actual.length !== expected.length) {
|
||||
this.raiseError(`Expected ${expected.length} completions, got ${actual.map(a => a.name)}.`);
|
||||
}
|
||||
|
||||
ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
|
||||
if (completion.name !== expectedCompletion) {
|
||||
this.raiseError(`Expected completion at index ${index} to be ${expectedCompletion}, got ${completion.name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
if (completions) {
|
||||
@@ -843,9 +886,8 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyReferencesOf({fileName, start}: Range, references: Range[]) {
|
||||
this.openFile(fileName);
|
||||
this.goToPosition(start);
|
||||
public verifyReferencesOf(range: Range, references: Range[]) {
|
||||
this.goToRangeStart(range);
|
||||
this.verifyReferencesAre(references);
|
||||
}
|
||||
|
||||
@@ -858,7 +900,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public verifyRangesWithSameTextReferenceEachOther() {
|
||||
ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges));
|
||||
this.rangesByText().forEach(ranges => this.verifyRangesReferenceEachOther(ranges));
|
||||
}
|
||||
|
||||
public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) {
|
||||
@@ -892,10 +934,6 @@ namespace FourSlash {
|
||||
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
|
||||
}
|
||||
|
||||
private getMemberListAtCaret() {
|
||||
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
}
|
||||
|
||||
private getCompletionListAtCaret() {
|
||||
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
}
|
||||
@@ -934,7 +972,8 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) {
|
||||
ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => {
|
||||
for (const name in namesAndTexts) if (ts.hasProperty(namesAndTexts, name)) {
|
||||
const text = namesAndTexts[name];
|
||||
if (text instanceof Array) {
|
||||
assert(text.length === 2);
|
||||
const [expectedText, expectedDocumentation] = text;
|
||||
@@ -943,7 +982,7 @@ namespace FourSlash {
|
||||
else {
|
||||
this.verifyQuickInfoAt(name, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public verifyQuickInfoString(expectedText: string, expectedDocumentation?: string) {
|
||||
@@ -1353,11 +1392,6 @@ namespace FourSlash {
|
||||
Harness.IO.log(stringify(sigHelp));
|
||||
}
|
||||
|
||||
public printMemberListMembers() {
|
||||
const members = this.getMemberListAtCaret();
|
||||
this.printMembersOrCompletions(members);
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
this.printMembersOrCompletions(completions);
|
||||
@@ -1568,7 +1602,8 @@ namespace FourSlash {
|
||||
let runningOffset = 0;
|
||||
edits = edits.sort((a, b) => a.span.start - b.span.start);
|
||||
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
|
||||
const oldContent = this.getFileContent(this.activeFile.fileName);
|
||||
const oldContent = this.getFileContent(fileName);
|
||||
|
||||
for (const edit of edits) {
|
||||
this.languageServiceAdapterHost.editScript(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
this.updateMarkersForEdit(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
@@ -1660,6 +1695,11 @@ namespace FourSlash {
|
||||
this.goToPosition(len);
|
||||
}
|
||||
|
||||
public goToRangeStart({fileName, start}: Range) {
|
||||
this.openFile(fileName);
|
||||
this.goToPosition(start);
|
||||
}
|
||||
|
||||
public goToTypeDefinition(definitionIndex: number) {
|
||||
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
if (!definitions || !definitions.length) {
|
||||
@@ -1785,7 +1825,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public getMarkerNames(): string[] {
|
||||
return Object.keys(this.testData.markerPositions);
|
||||
return ts.arrayFrom(this.testData.markerPositions.keys());
|
||||
}
|
||||
|
||||
public getRanges(): Range[] {
|
||||
@@ -1793,10 +1833,10 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public rangesByText(): ts.Map<Range[]> {
|
||||
const result = ts.createMap<Range[]>();
|
||||
const result = ts.createMultiMap<Range>();
|
||||
for (const range of this.getRanges()) {
|
||||
const text = this.rangeText(range);
|
||||
ts.multiMapAdd(result, text, range);
|
||||
result.add(text, range);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2005,54 +2045,93 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
private getCodeFixes(errorCode?: number) {
|
||||
const fileName = this.activeFile.fileName;
|
||||
const diagnostics = this.getDiagnostics(fileName);
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
this.raiseError("Errors expected.");
|
||||
}
|
||||
|
||||
if (diagnostics.length > 1 && errorCode === undefined) {
|
||||
this.raiseError("When there's more than one error, you must specify the errror to fix.");
|
||||
}
|
||||
|
||||
const diagnostic = !errorCode ? diagnostics[0] : ts.find(diagnostics, d => d.code == errorCode);
|
||||
|
||||
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code]);
|
||||
}
|
||||
|
||||
public verifyCodeFixAtPosition(expectedText: string, errorCode?: number) {
|
||||
/**
|
||||
* Compares expected text to the text that would be in the sole range
|
||||
* (ie: [|...|]) in the file after applying the codefix sole codefix
|
||||
* in the source file.
|
||||
*
|
||||
* Because codefixes are only applied on the working file, it is unsafe
|
||||
* to apply this more than once (consider a refactoring across files).
|
||||
*/
|
||||
public verifyRangeAfterCodeFix(expectedText: string, errorCode?: number) {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length == 0) {
|
||||
this.raiseError("At least one range should be specified in the testfile.");
|
||||
if (ranges.length !== 1) {
|
||||
this.raiseError("Exactly one range should be specified in the testfile.");
|
||||
}
|
||||
|
||||
const actual = this.getCodeFixes(errorCode);
|
||||
const fileName = this.activeFile.fileName;
|
||||
|
||||
if (!actual || actual.length == 0) {
|
||||
this.raiseError("No codefixes returned.");
|
||||
}
|
||||
this.applyCodeFixActions(fileName, this.getCodeFixActions(fileName, errorCode));
|
||||
|
||||
if (actual.length > 1) {
|
||||
this.raiseError("More than 1 codefix returned.");
|
||||
}
|
||||
|
||||
this.applyEdits(actual[0].changes[0].fileName, actual[0].changes[0].textChanges, /*isFormattingEdit*/ false);
|
||||
const actualText = this.rangeText(ranges[0]);
|
||||
|
||||
if (this.removeWhitespace(actualText) !== this.removeWhitespace(expectedText)) {
|
||||
this.raiseError(`Actual text doesn't match expected text. Actual: '${actualText}' Expected: '${expectedText}'`);
|
||||
this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies fixes for the errors in fileName and compares the results to
|
||||
* expectedContents after all fixes have been applied.
|
||||
|
||||
* Note: applying one codefix may generate another (eg: remove duplicate implements
|
||||
* may generate an extends -> interface conversion fix).
|
||||
* @param expectedContents The contents of the file after the fixes are applied.
|
||||
* @param fileName The file to check. If not supplied, the current open file is used.
|
||||
*/
|
||||
public verifyFileAfterCodeFix(expectedContents: string, fileName?: string) {
|
||||
fileName = fileName ? fileName : this.activeFile.fileName;
|
||||
|
||||
this.applyCodeFixActions(fileName, this.getCodeFixActions(fileName));
|
||||
|
||||
const actualContents: string = this.getFileContent(fileName);
|
||||
if (this.removeWhitespace(actualContents) !== this.removeWhitespace(expectedContents)) {
|
||||
this.raiseError(`Actual text doesn't match expected text. Actual:\n${actualContents}\n\nExpected:\n${expectedContents}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
|
||||
* @param fileName Path to file where error should be retrieved from.
|
||||
*/
|
||||
private getCodeFixActions(fileName: string, errorCode?: number): ts.CodeAction[] {
|
||||
const diagnostics: ts.Diagnostic[] = this.getDiagnostics(fileName);
|
||||
|
||||
let actions: ts.CodeAction[] = undefined;
|
||||
for (const diagnostic of diagnostics) {
|
||||
|
||||
if (errorCode && errorCode !== diagnostic.code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code]);
|
||||
if (newActions && newActions.length) {
|
||||
actions = actions ? actions.concat(newActions) : newActions;
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
private applyCodeFixActions(fileName: string, actions: ts.CodeAction[]): void {
|
||||
if (!(actions && actions.length === 1)) {
|
||||
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`);
|
||||
}
|
||||
|
||||
const fileChanges = ts.find(actions[0].changes, change => change.fileName === fileName);
|
||||
if (!fileChanges) {
|
||||
this.raiseError("The CodeFix found doesn't provide any changes in this file.");
|
||||
}
|
||||
|
||||
this.applyEdits(fileChanges.fileName, fileChanges.textChanges, /*isFormattingEdit*/ false);
|
||||
}
|
||||
|
||||
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length == 0) {
|
||||
this.raiseError("At least one range should be specified in the testfile.");
|
||||
}
|
||||
|
||||
const codeFixes = this.getCodeFixes(errorCode);
|
||||
const codeFixes = this.getCodeFixActions(this.activeFile.fileName, errorCode);
|
||||
|
||||
if (!codeFixes || codeFixes.length == 0) {
|
||||
this.raiseError("No codefixes returned.");
|
||||
@@ -2113,7 +2192,7 @@ namespace FourSlash {
|
||||
|
||||
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
|
||||
|
||||
const openBraceMap = ts.createMap<ts.CharacterCodes>({
|
||||
const openBraceMap = ts.createMapFromTemplate<ts.CharacterCodes>({
|
||||
"(": ts.CharacterCodes.openParen,
|
||||
"{": ts.CharacterCodes.openBrace,
|
||||
"[": ts.CharacterCodes.openBracket,
|
||||
@@ -2123,7 +2202,7 @@ namespace FourSlash {
|
||||
"<": ts.CharacterCodes.lessThan
|
||||
});
|
||||
|
||||
const charCode = openBraceMap[openingBrace];
|
||||
const charCode = openBraceMap.get(openingBrace);
|
||||
|
||||
if (!charCode) {
|
||||
this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`);
|
||||
@@ -2313,52 +2392,72 @@ namespace FourSlash {
|
||||
return this.languageService.getDocumentHighlights(this.activeFile.fileName, this.currentCaretPosition, filesToSearch);
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsAtPositionListContains(fileName: string, start: number, end: number, fileNamesToSearch: string[], kind?: string) {
|
||||
const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNamesToSearch);
|
||||
|
||||
if (!documentHighlights || documentHighlights.length === 0) {
|
||||
this.raiseError("verifyDocumentHighlightsAtPositionListContains failed - found 0 highlights, expected at least one.");
|
||||
public verifyRangesAreOccurrences(isWriteAccess?: boolean) {
|
||||
const ranges = this.getRanges();
|
||||
for (const r of ranges) {
|
||||
this.goToRangeStart(r);
|
||||
this.verifyOccurrencesAtPositionListCount(ranges.length);
|
||||
for (const range of ranges) {
|
||||
this.verifyOccurrencesAtPositionListContains(range.fileName, range.start, range.end, isWriteAccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const documentHighlight of documentHighlights) {
|
||||
if (documentHighlight.fileName === fileName) {
|
||||
const { highlightSpans } = documentHighlight;
|
||||
public verifyRangesAreRenameLocations(findInStrings: boolean, findInComments: boolean) {
|
||||
this.goToEachRange(() => this.verifyRenameLocations(findInStrings, findInComments));
|
||||
}
|
||||
|
||||
for (const highlight of highlightSpans) {
|
||||
if (highlight && highlight.textSpan.start === start && ts.textSpanEnd(highlight.textSpan) === end) {
|
||||
if (typeof kind !== "undefined" && highlight.kind !== kind) {
|
||||
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - item "kind" value does not match, actual: ${highlight.kind}, expected: ${kind}.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
public verifyRangesWithSameTextAreDocumentHighlights() {
|
||||
this.rangesByText().forEach(ranges => this.verifyRangesAreDocumentHighlights(ranges));
|
||||
}
|
||||
|
||||
public verifyRangesAreDocumentHighlights(ranges?: Range[]) {
|
||||
ranges = ranges || this.getRanges();
|
||||
const fileNames = unique(ranges, range => range.fileName);
|
||||
for (const range of ranges) {
|
||||
this.goToRangeStart(range);
|
||||
this.verifyDocumentHighlights(ranges, fileNames);
|
||||
}
|
||||
}
|
||||
|
||||
private verifyDocumentHighlights(expectedRanges: Range[], fileNames: string[] = [this.activeFile.fileName]) {
|
||||
const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNames) || [];
|
||||
|
||||
for (const dh of documentHighlights) {
|
||||
if (fileNames.indexOf(dh.fileName) === -1) {
|
||||
this.raiseError(`verifyDocumentHighlights failed - got highlights in unexpected file name ${dh.fileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
const missingItem = { fileName: fileName, start: start, end: end, kind: kind };
|
||||
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(documentHighlights)})`);
|
||||
}
|
||||
for (const fileName of fileNames) {
|
||||
const expectedRangesInFile = expectedRanges.filter(r => r.fileName === fileName);
|
||||
const highlights = ts.find(documentHighlights, dh => dh.fileName === fileName);
|
||||
if (!highlights) {
|
||||
this.raiseError(`verifyDocumentHighlights failed - found no highlights in ${fileName}`);
|
||||
}
|
||||
const spansInFile = highlights.highlightSpans.sort((s1, s2) => s1.textSpan.start - s2.textSpan.start);
|
||||
|
||||
public verifyDocumentHighlightsAtPositionListCount(expectedCount: number, fileNamesToSearch: string[]) {
|
||||
const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNamesToSearch);
|
||||
const actualCount = documentHighlights
|
||||
? documentHighlights.reduce((currentCount, { highlightSpans }) => currentCount + highlightSpans.length, 0)
|
||||
: 0;
|
||||
if (expectedRangesInFile.length !== spansInFile.length) {
|
||||
this.raiseError(`verifyDocumentHighlights failed - In ${fileName}, expected ${expectedRangesInFile.length} highlights, got ${spansInFile.length}`);
|
||||
}
|
||||
|
||||
if (expectedCount !== actualCount) {
|
||||
this.raiseError("verifyDocumentHighlightsAtPositionListCount failed - actual: " + actualCount + ", expected:" + expectedCount);
|
||||
ts.zipWith(expectedRangesInFile, spansInFile, (expectedRange, span) => {
|
||||
if (span.textSpan.start !== expectedRange.start || ts.textSpanEnd(span.textSpan) !== expectedRange.end) {
|
||||
this.raiseError(`verifyDocumentHighlights failed - span does not match, actual: ${JSON.stringify(span.textSpan)}, expected: ${expectedRange.start}--${expectedRange.end}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCodeFixAvailable(negative: boolean, errorCode?: number) {
|
||||
const fixes = this.getCodeFixes(errorCode);
|
||||
public verifyCodeFixAvailable(negative: boolean) {
|
||||
const codeFix = this.getCodeFixActions(this.activeFile.fileName);
|
||||
|
||||
if (negative && fixes && fixes.length > 0) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes, actual: ${fixes.length}`);
|
||||
if (negative && codeFix) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`);
|
||||
}
|
||||
|
||||
if (!negative && (fixes === undefined || fixes.length === 0)) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected code fixes, actual: 0`);
|
||||
if (!(negative || codeFix)) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected code fixes but none found.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2479,11 +2578,9 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public getMarkerByName(markerName: string) {
|
||||
const markerPos = this.testData.markerPositions[markerName];
|
||||
const markerPos = this.testData.markerPositions.get(markerName);
|
||||
if (markerPos === undefined) {
|
||||
const markerNames: string[] = [];
|
||||
for (const m in this.testData.markerPositions) markerNames.push(m);
|
||||
throw new Error(`Unknown marker "${markerName}" Available markers: ${markerNames.map(m => "\"" + m + "\"").join(", ")}`);
|
||||
throw new Error(`Unknown marker "${markerName}" Available markers: ${this.getMarkerNames().map(m => "\"" + m + "\"").join(", ")}`);
|
||||
}
|
||||
else {
|
||||
return markerPos;
|
||||
@@ -2576,7 +2673,7 @@ ${code}
|
||||
// we have to string-based splitting instead and try to figure out the delimiting chars
|
||||
const lines = contents.split("\n");
|
||||
|
||||
const markerPositions: MarkerMap = {};
|
||||
const markerPositions = ts.createMap<Marker>();
|
||||
const markers: Marker[] = [];
|
||||
const ranges: Range[] = [];
|
||||
|
||||
@@ -2715,7 +2812,7 @@ ${code}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: MarkerMap, markers: Marker[]): Marker {
|
||||
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
|
||||
let markerValue: any = undefined;
|
||||
try {
|
||||
// Attempt to parse the marker value as JSON
|
||||
@@ -2738,7 +2835,7 @@ ${code}
|
||||
|
||||
// Object markers can be anonymous
|
||||
if (markerValue.name) {
|
||||
markerMap[markerValue.name] = marker;
|
||||
markerMap.set(markerValue.name, marker);
|
||||
}
|
||||
|
||||
markers.push(marker);
|
||||
@@ -2746,26 +2843,26 @@ ${code}
|
||||
return marker;
|
||||
}
|
||||
|
||||
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: MarkerMap, markers: Marker[]): Marker {
|
||||
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
|
||||
const marker: Marker = {
|
||||
fileName,
|
||||
position: location.position
|
||||
};
|
||||
|
||||
// Verify markers for uniqueness
|
||||
if (markerMap[name] !== undefined) {
|
||||
if (markerMap.has(name)) {
|
||||
const message = "Marker '" + name + "' is duplicated in the source file contents.";
|
||||
reportError(marker.fileName, location.sourceLine, location.sourceColumn, message);
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
markerMap[name] = marker;
|
||||
markerMap.set(name, marker);
|
||||
markers.push(marker);
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
|
||||
function parseFileContent(content: string, fileName: string, markerMap: MarkerMap, markers: Marker[], ranges: Range[]): FourSlashFile {
|
||||
function parseFileContent(content: string, fileName: string, markerMap: ts.Map<Marker>, markers: Marker[], ranges: Range[]): FourSlashFile {
|
||||
content = chompLeadingSpace(content);
|
||||
|
||||
// Any slash-star comment with a character not in this string is not a marker.
|
||||
@@ -2975,6 +3072,16 @@ ${code}
|
||||
function stringify(data: any, replacer?: (key: string, value: any) => any): string {
|
||||
return JSON.stringify(data, replacer, 2);
|
||||
}
|
||||
|
||||
/** Collects an array of unique outputs. */
|
||||
function unique<T>(inputs: T[], getOutput: (t: T) => string): string[] {
|
||||
const set = ts.createMap<true>();
|
||||
for (const input of inputs) {
|
||||
const out = getOutput(input);
|
||||
set.set(out, true);
|
||||
}
|
||||
return ts.arrayFrom(set.keys());
|
||||
}
|
||||
}
|
||||
|
||||
namespace FourSlashInterface {
|
||||
@@ -3013,10 +3120,22 @@ namespace FourSlashInterface {
|
||||
// Moves the caret to the specified marker,
|
||||
// or the anonymous marker ('/**/') if no name
|
||||
// is given
|
||||
public marker(name?: string) {
|
||||
public marker(name?: string | FourSlash.Marker) {
|
||||
this.state.goToMarker(name);
|
||||
}
|
||||
|
||||
public eachMarker(action: () => void) {
|
||||
this.state.goToEachMarker(action);
|
||||
}
|
||||
|
||||
public rangeStart(range: FourSlash.Range) {
|
||||
this.state.goToRangeStart(range);
|
||||
}
|
||||
|
||||
public eachRange(action: () => void) {
|
||||
this.state.goToEachRange(action);
|
||||
}
|
||||
|
||||
public bof() {
|
||||
this.state.goToBOF();
|
||||
}
|
||||
@@ -3025,10 +3144,6 @@ namespace FourSlashInterface {
|
||||
this.state.goToEOF();
|
||||
}
|
||||
|
||||
public type(definitionIndex = 0) {
|
||||
this.state.goToTypeDefinition(definitionIndex);
|
||||
}
|
||||
|
||||
public implementation() {
|
||||
this.state.goToImplementation();
|
||||
}
|
||||
@@ -3061,19 +3176,8 @@ namespace FourSlashInterface {
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the member list contains the specified symbol. The
|
||||
// member list is brought up if necessary
|
||||
public memberListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
|
||||
if (this.negative) {
|
||||
this.state.verifyMemberListDoesNotContain(symbol);
|
||||
}
|
||||
else {
|
||||
this.state.verifyMemberListContains(symbol, text, documentation, kind);
|
||||
}
|
||||
}
|
||||
|
||||
public memberListCount(expectedCount: number) {
|
||||
this.state.verifyMemberListCount(expectedCount, this.negative);
|
||||
public completionListCount(expectedCount: number) {
|
||||
this.state.verifyCompletionListCount(expectedCount, this.negative);
|
||||
}
|
||||
|
||||
// Verifies the completion list contains the specified symbol. The
|
||||
@@ -3109,10 +3213,6 @@ namespace FourSlashInterface {
|
||||
this.state.verifyCompletionListAllowsNewIdentifier(this.negative);
|
||||
}
|
||||
|
||||
public memberListIsEmpty() {
|
||||
this.state.verifyMemberListIsEmpty(this.negative);
|
||||
}
|
||||
|
||||
public signatureHelpPresent() {
|
||||
this.state.verifySignatureHelpPresent(!this.negative);
|
||||
}
|
||||
@@ -3145,8 +3245,8 @@ namespace FourSlashInterface {
|
||||
this.state.verifyBraceCompletionAtPosition(this.negative, openingBrace);
|
||||
}
|
||||
|
||||
public codeFixAvailable(errorCode?: number) {
|
||||
this.state.verifyCodeFixAvailable(this.negative, errorCode);
|
||||
public codeFixAvailable() {
|
||||
this.state.verifyCodeFixAvailable(this.negative);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3155,6 +3255,10 @@ namespace FourSlashInterface {
|
||||
super(state);
|
||||
}
|
||||
|
||||
public completionsAt(markerName: string, completions: string[]) {
|
||||
this.state.verifyCompletionsAt(markerName, completions);
|
||||
}
|
||||
|
||||
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
|
||||
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
|
||||
}
|
||||
@@ -3211,6 +3315,13 @@ namespace FourSlashInterface {
|
||||
this.state.verifyGoToDefinition(arg0, endMarkerName);
|
||||
}
|
||||
|
||||
public goToType(startMarkerName: string | string[], endMarkerName: string | string[]): void;
|
||||
public goToType(startsAndEnds: [string | string[], string | string[]][]): void;
|
||||
public goToType(startsAndEnds: { [startMarkerName: string]: string | string[] }): void;
|
||||
public goToType(arg0: any, endMarkerName?: string | string[]) {
|
||||
this.state.verifyGoToType(arg0, endMarkerName);
|
||||
}
|
||||
|
||||
public goToDefinitionForMarkers(...markerNames: string[]) {
|
||||
this.state.verifyGoToDefinitionForMarkers(markerNames);
|
||||
}
|
||||
@@ -3331,8 +3442,8 @@ namespace FourSlashInterface {
|
||||
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
|
||||
}
|
||||
|
||||
public codeFixAtPosition(expectedText: string, errorCode?: number): void {
|
||||
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
|
||||
public rangeAfterCodeFix(expectedText: string, errorCode?: number): void {
|
||||
this.state.verifyRangeAfterCodeFix(expectedText, errorCode);
|
||||
}
|
||||
|
||||
public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
|
||||
@@ -3375,12 +3486,20 @@ namespace FourSlashInterface {
|
||||
this.state.verifyOccurrencesAtPositionListCount(expectedCount);
|
||||
}
|
||||
|
||||
public documentHighlightsAtPositionContains(range: FourSlash.Range, fileNamesToSearch: string[], kind?: string) {
|
||||
this.state.verifyDocumentHighlightsAtPositionListContains(range.fileName, range.start, range.end, fileNamesToSearch, kind);
|
||||
public rangesAreOccurrences(isWriteAccess?: boolean) {
|
||||
this.state.verifyRangesAreOccurrences(isWriteAccess);
|
||||
}
|
||||
|
||||
public documentHighlightsAtPositionCount(expectedCount: number, fileNamesToSearch: string[]) {
|
||||
this.state.verifyDocumentHighlightsAtPositionListCount(expectedCount, fileNamesToSearch);
|
||||
public rangesAreRenameLocations(findInStrings = false, findInComments = false) {
|
||||
this.state.verifyRangesAreRenameLocations(findInStrings, findInComments);
|
||||
}
|
||||
|
||||
public rangesAreDocumentHighlights(ranges?: FourSlash.Range[]) {
|
||||
this.state.verifyRangesAreDocumentHighlights(ranges);
|
||||
}
|
||||
|
||||
public rangesWithSameTextAreDocumentHighlights() {
|
||||
this.state.verifyRangesWithSameTextAreDocumentHighlights();
|
||||
}
|
||||
|
||||
public completionEntryDetailIs(entryName: string, text: string, documentation?: string, kind?: string) {
|
||||
@@ -3514,10 +3633,6 @@ namespace FourSlashInterface {
|
||||
this.state.printCurrentSignatureHelp();
|
||||
}
|
||||
|
||||
public printMemberListMembers() {
|
||||
this.state.printMemberListMembers();
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
this.state.printCompletionListMembers();
|
||||
}
|
||||
@@ -3578,11 +3693,8 @@ namespace FourSlashInterface {
|
||||
this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key);
|
||||
}
|
||||
|
||||
public setOption(name: string, value: number): void;
|
||||
public setOption(name: string, value: string): void;
|
||||
public setOption(name: string, value: boolean): void;
|
||||
public setOption(name: string, value: any): void {
|
||||
(<any>this.state.formatCodeSettings)[name] = value;
|
||||
public setOption(name: keyof ts.FormatCodeSettings, value: number | string | boolean): void {
|
||||
this.state.formatCodeSettings[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-15
@@ -40,6 +40,19 @@ declare namespace NodeJS {
|
||||
ActiveXObject: typeof ActiveXObject;
|
||||
}
|
||||
}
|
||||
|
||||
declare var window: {};
|
||||
declare var XMLHttpRequest: {
|
||||
new(): XMLHttpRequest;
|
||||
}
|
||||
interface XMLHttpRequest {
|
||||
readonly readyState: number;
|
||||
readonly responseText: string;
|
||||
readonly status: number;
|
||||
open(method: string, url: string, async?: boolean, user?: string, password?: string): void;
|
||||
send(data?: string): void;
|
||||
setRequestHeader(header: string, value: string): void;
|
||||
}
|
||||
/* tslint:enable:no-var-keyword */
|
||||
|
||||
namespace Utils {
|
||||
@@ -85,7 +98,7 @@ namespace Utils {
|
||||
eval(fileContents);
|
||||
break;
|
||||
case ExecutionEnvironment.Node:
|
||||
let vm = require("vm");
|
||||
const vm = require("vm");
|
||||
if (nodeContext) {
|
||||
vm.runInNewContext(fileContents, nodeContext, fileName);
|
||||
}
|
||||
@@ -175,9 +188,9 @@ namespace Utils {
|
||||
assert.isFalse(array.end > node.end, "array.end > node.end");
|
||||
assert.isFalse(array.pos < currentPos, "array.pos < currentPos");
|
||||
|
||||
for (let i = 0, n = array.length; i < n; i++) {
|
||||
assert.isFalse(array[i].pos < currentPos, "array[i].pos < currentPos");
|
||||
currentPos = array[i].end;
|
||||
for (const item of array) {
|
||||
assert.isFalse(item.pos < currentPos, "array[i].pos < currentPos");
|
||||
currentPos = item.end;
|
||||
}
|
||||
|
||||
currentPos = array.end;
|
||||
@@ -344,7 +357,7 @@ namespace Utils {
|
||||
|
||||
assert.equal(array1.length, array2.length, "array1.length !== array2.length");
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
const d1 = array1[i];
|
||||
const d2 = array2[i];
|
||||
|
||||
@@ -400,7 +413,7 @@ namespace Utils {
|
||||
assert.equal(array1.end, array2.end, "array1.end !== array2.end");
|
||||
assert.equal(array1.length, array2.length, "array1.length !== array2.length");
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
assertStructuralEquals(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
@@ -909,7 +922,7 @@ namespace Harness {
|
||||
export const defaultLibFileName = "lib.d.ts";
|
||||
export const es2015DefaultLibFileName = "lib.es2015.d.ts";
|
||||
|
||||
const libFileNameSourceFileMap = ts.createMap<ts.SourceFile>({
|
||||
const libFileNameSourceFileMap = ts.createMapFromTemplate<ts.SourceFile>({
|
||||
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest)
|
||||
});
|
||||
|
||||
@@ -918,10 +931,11 @@ namespace Harness {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!libFileNameSourceFileMap[fileName]) {
|
||||
libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest);
|
||||
let sourceFile = libFileNameSourceFileMap.get(fileName);
|
||||
if (!sourceFile) {
|
||||
libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest));
|
||||
}
|
||||
return libFileNameSourceFileMap[fileName];
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
export function getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
@@ -1103,10 +1117,10 @@ namespace Harness {
|
||||
optionsIndex = ts.createMap<ts.CommandLineOption>();
|
||||
const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations);
|
||||
for (const option of optionDeclarations) {
|
||||
optionsIndex[option.name.toLowerCase()] = option;
|
||||
optionsIndex.set(option.name.toLowerCase(), option);
|
||||
}
|
||||
}
|
||||
return optionsIndex[name.toLowerCase()];
|
||||
return optionsIndex.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void {
|
||||
@@ -1466,7 +1480,7 @@ namespace Harness {
|
||||
const fullResults = ts.createMap<TypeWriterResult[]>();
|
||||
|
||||
for (const sourceFile of allFiles) {
|
||||
fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName);
|
||||
fullResults.set(sourceFile.unitName, fullWalker.getTypeAndSymbols(sourceFile.unitName));
|
||||
}
|
||||
|
||||
// Produce baselines. The first gives the types for all expressions.
|
||||
@@ -1519,7 +1533,7 @@ namespace Harness {
|
||||
|
||||
allFiles.forEach(file => {
|
||||
const codeLines = file.content.split("\n");
|
||||
typeWriterResults[file.unitName].forEach(result => {
|
||||
typeWriterResults.get(file.unitName).forEach(result => {
|
||||
if (isSymbolBaseline && !result.symbol) {
|
||||
return;
|
||||
}
|
||||
@@ -2015,7 +2029,7 @@ namespace Harness {
|
||||
|
||||
export function isDefaultLibraryFile(filePath: string): boolean {
|
||||
// We need to make sure that the filePath is prefixed with "lib." not just containing "lib." and end with ".d.ts"
|
||||
const fileName = ts.getBaseFileName(filePath);
|
||||
const fileName = ts.getBaseFileName(ts.normalizeSlashes(filePath));
|
||||
return ts.startsWith(fileName, "lib.") && ts.endsWith(fileName, ".d.ts");
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace Harness.LanguageService {
|
||||
this.getModuleResolutionsForFile = (fileName) => {
|
||||
const scriptInfo = this.getScriptInfo(fileName);
|
||||
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
|
||||
const imports = ts.createMap<string>();
|
||||
const imports: ts.MapLike<string> = {};
|
||||
for (const module of preprocessInfo.importedFiles) {
|
||||
const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
|
||||
if (resolutionInfo.resolvedModule) {
|
||||
@@ -275,7 +275,7 @@ namespace Harness.LanguageService {
|
||||
const scriptInfo = this.getScriptInfo(fileName);
|
||||
if (scriptInfo) {
|
||||
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
|
||||
const resolutions = ts.createMap<ts.ResolvedTypeReferenceDirective>();
|
||||
const resolutions: ts.MapLike<ts.ResolvedTypeReferenceDirective> = {};
|
||||
const settings = this.nativeHost.getCompilationSettings();
|
||||
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
|
||||
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
|
||||
|
||||
@@ -256,17 +256,20 @@ class ProjectRunner extends RunnerBase {
|
||||
// Set the values specified using json
|
||||
const optionNameMap = ts.arrayToMap(ts.optionDeclarations, option => option.name);
|
||||
for (const name in testCase) {
|
||||
if (name !== "mapRoot" && name !== "sourceRoot" && name in optionNameMap) {
|
||||
const option = optionNameMap[name];
|
||||
const optType = option.type;
|
||||
let value = <any>testCase[name];
|
||||
if (typeof optType !== "string") {
|
||||
const key = value.toLowerCase();
|
||||
if (key in optType) {
|
||||
value = optType[key];
|
||||
if (name !== "mapRoot" && name !== "sourceRoot") {
|
||||
const option = optionNameMap.get(name);
|
||||
if (option) {
|
||||
const optType = option.type;
|
||||
let value = <any>testCase[name];
|
||||
if (typeof optType !== "string") {
|
||||
const key = value.toLowerCase();
|
||||
const optTypeValue = optType.get(key);
|
||||
if (optTypeValue) {
|
||||
value = optTypeValue;
|
||||
}
|
||||
}
|
||||
compilerOptions[option.name] = value;
|
||||
}
|
||||
compilerOptions[option.name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace RWC {
|
||||
}
|
||||
// Do not include the library in the baselines to avoid noise
|
||||
const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
|
||||
const errors = compilerResult.errors.filter(e => !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
const errors = compilerResult.errors.filter(e => e.file && !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
return Harness.Compiler.getErrorBaseline(baselineFiles, errors);
|
||||
}, baselineOpts);
|
||||
});
|
||||
|
||||
+15
-11
@@ -1,19 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"pretty": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "../../built/local/run.js",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"stripInternal": true,
|
||||
"types": [
|
||||
"node", "mocha", "chai"
|
||||
],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../compiler/core.ts",
|
||||
@@ -74,7 +67,18 @@
|
||||
"../services/formatting/rulesProvider.ts",
|
||||
"../services/formatting/smartIndenter.ts",
|
||||
"../services/formatting/tokenRange.ts",
|
||||
"harness.ts",
|
||||
"../services/codeFixProvider.ts",
|
||||
"../services/codefixes/fixes.ts",
|
||||
"../services/codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
"../services/codefixes/fixClassIncorrectlyImplementsInterface.ts",
|
||||
"../services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
|
||||
"../services/codefixes/fixClassSuperMustPrecedeThisAccess.ts",
|
||||
"../services/codefixes/fixConstructorForDerivedNeedSuperCall.ts",
|
||||
"../services/codefixes/helpers.ts",
|
||||
"../services/codefixes/importFixes.ts",
|
||||
"../services/codefixes/unusedIdentifierFixes.ts",
|
||||
"../services/harness.ts",
|
||||
|
||||
"sourceMapRecorder.ts",
|
||||
"harnessLanguageService.ts",
|
||||
"fourslash.ts",
|
||||
@@ -108,7 +112,7 @@
|
||||
"./unittests/commandLineParsing.ts",
|
||||
"./unittests/configurationExtension.ts",
|
||||
"./unittests/convertCompilerOptionsFromJson.ts",
|
||||
"./unittests/convertTypingOptionsFromJson.ts",
|
||||
"./unittests/convertTypeAcquisitionFromJson.ts",
|
||||
"./unittests/tsserverProjectSystem.ts",
|
||||
"./unittests/matchFiles.ts",
|
||||
"./unittests/initializeTSConfig.ts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
namespace ts {
|
||||
interface File {
|
||||
@@ -8,25 +8,28 @@ namespace ts {
|
||||
|
||||
function createDefaultServerHost(fileMap: Map<File>): server.ServerHost {
|
||||
const existingDirectories = createMap<boolean>();
|
||||
for (const name in fileMap) {
|
||||
forEachKey(fileMap, name => {
|
||||
let dir = getDirectoryPath(name);
|
||||
let previous: string;
|
||||
do {
|
||||
existingDirectories[dir] = true;
|
||||
existingDirectories.set(dir, true);
|
||||
previous = dir;
|
||||
dir = getDirectoryPath(dir);
|
||||
} while (dir !== previous);
|
||||
}
|
||||
});
|
||||
return {
|
||||
args: <string[]>[],
|
||||
newLine: "\r\n",
|
||||
useCaseSensitiveFileNames: false,
|
||||
write: noop,
|
||||
readFile: path => path in fileMap ? fileMap[path].content : undefined,
|
||||
readFile: path => {
|
||||
const file = fileMap.get(path);
|
||||
return file && file.content;
|
||||
},
|
||||
writeFile: notImplemented,
|
||||
resolvePath: notImplemented,
|
||||
fileExists: path => path in fileMap,
|
||||
directoryExists: path => existingDirectories[path] || false,
|
||||
fileExists: path => fileMap.has(path),
|
||||
directoryExists: path => existingDirectories.get(path) || false,
|
||||
createDirectory: noop,
|
||||
getExecutingFilePath: () => "",
|
||||
getCurrentDirectory: () => "",
|
||||
@@ -83,7 +86,7 @@ namespace ts {
|
||||
content: `foo()`
|
||||
};
|
||||
|
||||
const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported }));
|
||||
const serverHost = createDefaultServerHost(createMapFromTemplate({ [root.name]: root, [imported.name]: imported }));
|
||||
const { project, rootScriptInfo } = createProject(root.name, serverHost);
|
||||
|
||||
// ensure that imported file was found
|
||||
@@ -167,7 +170,7 @@ namespace ts {
|
||||
content: `export var y = 1`
|
||||
};
|
||||
|
||||
const fileMap = createMap({ [root.name]: root });
|
||||
const fileMap = createMapFromTemplate({ [root.name]: root });
|
||||
const serverHost = createDefaultServerHost(fileMap);
|
||||
const originalFileExists = serverHost.fileExists;
|
||||
|
||||
@@ -191,7 +194,7 @@ namespace ts {
|
||||
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
|
||||
|
||||
// assert that import will success once file appear on disk
|
||||
fileMap[imported.name] = imported;
|
||||
fileMap.set(imported.name, imported);
|
||||
fileExistsCalledForBar = false;
|
||||
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react-native', 'react'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
|
||||
@@ -467,6 +467,36 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("EmitFile test", () => {
|
||||
it("should respect line endings", () => {
|
||||
test("\n");
|
||||
test("\r\n");
|
||||
|
||||
function test(newLine: string) {
|
||||
const lines = ["var x = 1;", "var y = 2;"];
|
||||
const path = "/a/app";
|
||||
const f = {
|
||||
path: path + ".ts",
|
||||
content: lines.join(newLine)
|
||||
};
|
||||
const host = createServerHost([f], { newLine });
|
||||
const session = createSession(host);
|
||||
session.executeCommand(<server.protocol.OpenRequest>{
|
||||
seq: 1,
|
||||
type: "request",
|
||||
command: "open",
|
||||
arguments: { file: f.path }
|
||||
});
|
||||
session.executeCommand(<server.protocol.CompileOnSaveEmitFileRequest>{
|
||||
seq: 2,
|
||||
type: "request",
|
||||
command: "compileOnSaveEmitFile",
|
||||
arguments: { file: f.path }
|
||||
});
|
||||
const emitOutput = host.readFile(path + ".js");
|
||||
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
|
||||
}
|
||||
})
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
@@ -480,7 +510,7 @@ namespace ts.projectSystem {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
const host = createServerHost([file1, file2, configFile, libFile]);
|
||||
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
|
||||
const typingsInstaller = createTestTypingsInstaller(host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/// <reference path="..\virtualFileSystem.ts" />
|
||||
|
||||
namespace ts {
|
||||
const testContents = {
|
||||
const testContents = createMapFromTemplate({
|
||||
"/dev/tsconfig.json": `{
|
||||
"extends": "./configs/base",
|
||||
"files": [
|
||||
@@ -86,10 +86,10 @@ namespace ts {
|
||||
"/dev/tests/utils.ts": "",
|
||||
"/dev/tests/scenarios/first.json": "",
|
||||
"/dev/tests/baselines/first/output.ts": ""
|
||||
};
|
||||
});
|
||||
|
||||
const caseInsensitiveBasePath = "c:/dev/";
|
||||
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapObject(testContents, (key, content) => [`c:${key}`, content]));
|
||||
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapEntries(testContents, (key, content) => [`c:${key}`, content]));
|
||||
|
||||
const caseSensitiveBasePath = "/dev/";
|
||||
const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents);
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react-native', 'react'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
|
||||
+72
-49
@@ -2,12 +2,13 @@
|
||||
/// <reference path="..\..\compiler\commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
describe("convertTypingOptionsFromJson", () => {
|
||||
function assertTypingOptions(json: any, configFileName: string, expectedResult: { typingOptions: TypingOptions, errors: Diagnostic[] }) {
|
||||
const { options: actualTypingOptions, errors: actualErrors } = convertTypingOptionsFromJson(json["typingOptions"], "/apath/", configFileName);
|
||||
const parsedTypingOptions = JSON.stringify(actualTypingOptions);
|
||||
const expectedTypingOptions = JSON.stringify(expectedResult.typingOptions);
|
||||
assert.equal(parsedTypingOptions, expectedTypingOptions);
|
||||
describe("convertTypeAcquisitionFromJson", () => {
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: { typeAcquisition: TypeAcquisition, errors: Diagnostic[] }) {
|
||||
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
|
||||
const { options: actualTypeAcquisition, errors: actualErrors } = convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
|
||||
const parsedTypeAcquisition = JSON.stringify(actualTypeAcquisition);
|
||||
const expectedTypeAcquisition = JSON.stringify(expectedResult.typeAcquisition);
|
||||
assert.equal(parsedTypeAcquisition, expectedTypeAcquisition);
|
||||
|
||||
const expectedErrors = expectedResult.errors;
|
||||
assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`);
|
||||
@@ -20,8 +21,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// tsconfig.json
|
||||
it("Convert correctly format tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert deprecated typingOptions.enableAutoDiscovery format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
{
|
||||
@@ -32,9 +33,9 @@ namespace ts {
|
||||
},
|
||||
"tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: ["0.d.ts", "1.d.ts"],
|
||||
exclude: ["0.js", "1.js"]
|
||||
},
|
||||
@@ -42,25 +43,47 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert correctly format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enable": true,
|
||||
"include": ["0.d.ts", "1.d.ts"],
|
||||
"exclude": ["0.js", "1.js"]
|
||||
}
|
||||
},
|
||||
"tsconfig.json",
|
||||
{
|
||||
typeAcquisition:
|
||||
{
|
||||
enable: true,
|
||||
include: ["0.d.ts", "1.d.ts"],
|
||||
exclude: ["0.js", "1.js"]
|
||||
},
|
||||
errors: <Diagnostic[]>[]
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovy": true,
|
||||
}
|
||||
}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
category: Diagnostics.Unknown_typing_option_0.category,
|
||||
code: Diagnostics.Unknown_typing_option_0.code,
|
||||
category: Diagnostics.Unknown_type_acquisition_option_0.category,
|
||||
code: Diagnostics.Unknown_type_acquisition_option_0.code,
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
@@ -70,12 +93,12 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert default tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions({}, "tsconfig.json",
|
||||
it("Convert default tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition({}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -83,18 +106,18 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert tsconfig.json with only enableAutoDiscovery property to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert tsconfig.json with only enable property to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": true
|
||||
"enable": true
|
||||
}
|
||||
}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -103,20 +126,20 @@ namespace ts {
|
||||
});
|
||||
|
||||
// jsconfig.json
|
||||
it("Convert jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": false,
|
||||
"enable": false,
|
||||
"include": ["0.d.ts"],
|
||||
"exclude": ["0.js"]
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: ["0.d.ts"],
|
||||
exclude: ["0.js"]
|
||||
},
|
||||
@@ -124,12 +147,12 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert default jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions({ }, "jsconfig.json",
|
||||
it("Convert default jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition({ }, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -137,25 +160,25 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert incorrect format jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovy": true,
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
category: Diagnostics.Unknown_compiler_option_0.category,
|
||||
code: Diagnostics.Unknown_typing_option_0.code,
|
||||
code: Diagnostics.Unknown_type_acquisition_option_0.code,
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
@@ -165,18 +188,18 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert jsconfig.json with only enableAutoDiscovery property to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert jsconfig.json with only enable property to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": false
|
||||
"enable": false
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -28,7 +28,7 @@ namespace ts {
|
||||
const diagnostics2 = file2.parseDiagnostics;
|
||||
|
||||
assert.equal(diagnostics1.length, diagnostics2.length, "diagnostics1.length !== diagnostics2.length");
|
||||
for (let i = 0, n = diagnostics1.length; i < n; i++) {
|
||||
for (let i = 0; i < diagnostics1.length; i++) {
|
||||
const d1 = diagnostics1[i];
|
||||
const d2 = diagnostics2[i];
|
||||
|
||||
|
||||
@@ -288,5 +288,24 @@ namespace ts {
|
||||
*/`);
|
||||
});
|
||||
});
|
||||
describe("getFirstToken", () => {
|
||||
it("gets jsdoc", () => {
|
||||
const root = ts.createSourceFile("foo.ts", "/** comment */var a = true;", ts.ScriptTarget.ES5, /*setParentNodes*/ true);
|
||||
assert.isDefined(root);
|
||||
assert.equal(root.kind, ts.SyntaxKind.SourceFile);
|
||||
const first = root.getFirstToken();
|
||||
assert.isDefined(first);
|
||||
assert.equal(first.kind, ts.SyntaxKind.VarKeyword);
|
||||
});
|
||||
});
|
||||
describe("getLastToken", () => {
|
||||
it("gets jsdoc", () => {
|
||||
const root = ts.createSourceFile("foo.ts", "var a = true;/** comment */", ts.ScriptTarget.ES5, /*setParentNodes*/ true);
|
||||
assert.isDefined(root);
|
||||
const last = root.getLastToken();
|
||||
assert.isDefined(last);
|
||||
assert.equal(last.kind, ts.SyntaxKind.EndOfFileToken);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ namespace ts {
|
||||
"c:/dev/g.min.js/.g/g.ts"
|
||||
]);
|
||||
|
||||
const defaultExcludes = ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
function assertParsed(actual: ts.ParsedCommandLine, expected: ts.ParsedCommandLine): void {
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
@@ -98,6 +96,23 @@ namespace ts {
|
||||
}
|
||||
|
||||
describe("matchFiles", () => {
|
||||
it("with defaults", () => {
|
||||
const json = {};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
|
||||
describe("with literal file list", () => {
|
||||
it("without exclusions", () => {
|
||||
const json = {
|
||||
@@ -192,7 +207,7 @@ namespace ts {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
@@ -211,7 +226,7 @@ namespace ts {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
@@ -330,7 +345,10 @@ namespace ts {
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.ts"
|
||||
"c:/dev/b.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
@@ -372,8 +390,7 @@ namespace ts {
|
||||
"node_modules/a.ts",
|
||||
"bower_components/a.ts",
|
||||
"jspm_packages/a.ts"
|
||||
],
|
||||
exclude: <string[]>[]
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
@@ -530,7 +547,7 @@ namespace ts {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {
|
||||
@@ -600,7 +617,10 @@ namespace ts {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts"
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
@@ -671,7 +691,7 @@ namespace ts {
|
||||
},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {
|
||||
@@ -889,6 +909,31 @@ namespace ts {
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=react-native, allowJs=false", () => {
|
||||
const json = {
|
||||
compilerOptions: {
|
||||
jsx: "react-native",
|
||||
allowJs: false
|
||||
}
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {
|
||||
jsx: ts.JsxEmit.ReactNative,
|
||||
allowJs: false
|
||||
},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.tsx",
|
||||
"c:/dev/c.tsx",
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=none, allowJs=true", () => {
|
||||
const json = {
|
||||
compilerOptions: {
|
||||
@@ -941,6 +986,33 @@ namespace ts {
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=react-native, allowJs=true", () => {
|
||||
const json = {
|
||||
compilerOptions: {
|
||||
jsx: "react-native",
|
||||
allowJs: true
|
||||
}
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {
|
||||
jsx: ts.JsxEmit.ReactNative,
|
||||
allowJs: true
|
||||
},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.tsx",
|
||||
"c:/dev/c.tsx",
|
||||
"c:/dev/d.js",
|
||||
"c:/dev/e.jsx",
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude .min.js files using wildcards", () => {
|
||||
const json = {
|
||||
compilerOptions: {
|
||||
@@ -980,7 +1052,7 @@ namespace ts {
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
@@ -1022,7 +1094,7 @@ namespace ts {
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
@@ -1071,7 +1143,7 @@ namespace ts {
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
@@ -1091,7 +1163,7 @@ namespace ts {
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes))
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
@@ -1286,4 +1358,4 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ts {
|
||||
for (const f of files) {
|
||||
let name = getDirectoryPath(f.name);
|
||||
while (true) {
|
||||
directories[name] = name;
|
||||
directories.set(name, name);
|
||||
const baseName = getDirectoryPath(name);
|
||||
if (baseName === name) {
|
||||
break;
|
||||
@@ -46,20 +46,19 @@ namespace ts {
|
||||
}
|
||||
return {
|
||||
readFile,
|
||||
directoryExists: path => {
|
||||
return path in directories;
|
||||
},
|
||||
directoryExists: path => directories.has(path),
|
||||
fileExists: path => {
|
||||
assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`);
|
||||
return path in map;
|
||||
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
|
||||
return map.has(path);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return { readFile, fileExists: path => path in map, };
|
||||
return { readFile, fileExists: path => map.has(path) };
|
||||
}
|
||||
function readFile(path: string): string {
|
||||
return path in map ? map[path].content : undefined;
|
||||
const file = map.get(path);
|
||||
return file && file.content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +299,8 @@ namespace ts {
|
||||
const host: CompilerHost = {
|
||||
getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
|
||||
const path = normalizePath(combinePaths(currentDirectory, fileName));
|
||||
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
|
||||
const file = files.get(path);
|
||||
return file && createSourceFile(fileName, file, languageVersion);
|
||||
},
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: notImplemented,
|
||||
@@ -311,7 +311,7 @@ namespace ts {
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
fileExists: fileName => {
|
||||
const path = normalizePath(combinePaths(currentDirectory, fileName));
|
||||
return path in files;
|
||||
return files.has(path);
|
||||
},
|
||||
readFile: notImplemented
|
||||
};
|
||||
@@ -331,7 +331,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
it("should find all modules", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c/first/shared.ts": `
|
||||
class A {}
|
||||
export = A`,
|
||||
@@ -350,7 +350,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should find modules in node_modules", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/parent/node_modules/mod/index.d.ts": "export var x",
|
||||
"/parent/app/myapp.ts": `import {x} from "mod"`
|
||||
});
|
||||
@@ -358,7 +358,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should find file referenced via absolute and relative names", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c.ts": `/// <reference path="b.ts"/>`,
|
||||
"/a/b/b.ts": "var x"
|
||||
});
|
||||
@@ -371,7 +371,11 @@ export = C;
|
||||
function test(files: Map<string>, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void {
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
if (!useCaseSensitiveFileNames) {
|
||||
files = reduceProperties(files, (files, file, fileName) => (files[getCanonicalFileName(fileName)] = file, files), createMap<string>());
|
||||
const oldFiles = files;
|
||||
files = createMap<string>();
|
||||
oldFiles.forEach((file, fileName) => {
|
||||
files.set(getCanonicalFileName(fileName), file);
|
||||
});
|
||||
}
|
||||
|
||||
const host: CompilerHost = {
|
||||
@@ -380,7 +384,8 @@ export = C;
|
||||
return library;
|
||||
}
|
||||
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
|
||||
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
|
||||
const file = files.get(path);
|
||||
return file && createSourceFile(fileName, file, languageVersion);
|
||||
},
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: notImplemented,
|
||||
@@ -391,7 +396,7 @@ export = C;
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
fileExists: fileName => {
|
||||
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
|
||||
return path in files;
|
||||
return files.has(path);
|
||||
},
|
||||
readFile: notImplemented
|
||||
};
|
||||
@@ -404,7 +409,7 @@ export = C;
|
||||
}
|
||||
|
||||
it("should succeed when the same file is referenced using absolute and relative names", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c.ts": `/// <reference path="d.ts"/>`,
|
||||
"/a/b/d.ts": "var x"
|
||||
});
|
||||
@@ -412,7 +417,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when two files used in program differ only in casing (tripleslash references)", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c.ts": `/// <reference path="D.ts"/>`,
|
||||
"/a/b/d.ts": "var x"
|
||||
});
|
||||
@@ -420,7 +425,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when two files used in program differ only in casing (imports)", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c.ts": `import {x} from "D"`,
|
||||
"/a/b/d.ts": "export var x"
|
||||
});
|
||||
@@ -428,7 +433,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when two files used in program differ only in casing (imports, relative module names)", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"moduleA.ts": `import {x} from "./ModuleB"`,
|
||||
"moduleB.ts": "export var x"
|
||||
});
|
||||
@@ -436,7 +441,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when two files exist on disk that differs only in casing", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/b/c.ts": `import {x} from "D"`,
|
||||
"/a/b/D.ts": "export var x",
|
||||
"/a/b/d.ts": "export var y"
|
||||
@@ -445,7 +450,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when module name in 'require' calls has inconsistent casing", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"moduleA.ts": `import a = require("./ModuleC")`,
|
||||
"moduleB.ts": `import a = require("./moduleC")`,
|
||||
"moduleC.ts": "export var x"
|
||||
@@ -454,7 +459,7 @@ export = C;
|
||||
});
|
||||
|
||||
it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/B/c/moduleA.ts": `import a = require("./ModuleC")`,
|
||||
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
|
||||
"/a/B/c/moduleC.ts": "export var x",
|
||||
@@ -466,7 +471,7 @@ import b = require("./moduleB");
|
||||
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]);
|
||||
});
|
||||
it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => {
|
||||
const files = createMap({
|
||||
const files = createMapFromTemplate({
|
||||
"/a/B/c/moduleA.ts": `import a = require("./moduleC")`,
|
||||
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
|
||||
"/a/B/c/moduleC.ts": "export var x",
|
||||
@@ -1020,8 +1025,8 @@ import b = require("./moduleB");
|
||||
const names = map(files, f => f.name);
|
||||
const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES2015)), f => f.fileName);
|
||||
const compilerHost: CompilerHost = {
|
||||
fileExists : fileName => fileName in sourceFiles,
|
||||
getSourceFile: fileName => sourceFiles[fileName],
|
||||
fileExists : fileName => sourceFiles.has(fileName),
|
||||
getSourceFile: fileName => sourceFiles.get(fileName),
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => "/",
|
||||
@@ -1029,7 +1034,10 @@ import b = require("./moduleB");
|
||||
getCanonicalFileName: f => f.toLowerCase(),
|
||||
getNewLine: () => "\r\n",
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined
|
||||
readFile: fileName => {
|
||||
const file = sourceFiles.get(fileName);
|
||||
return file && file.text;
|
||||
}
|
||||
};
|
||||
const program1 = createProgram(names, {}, compilerHost);
|
||||
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\..\harness\harnessLanguageService.ts" />
|
||||
|
||||
namespace ts {
|
||||
@@ -122,7 +122,7 @@ namespace ts {
|
||||
trace: s => trace.push(s),
|
||||
getTrace: () => trace,
|
||||
getSourceFile(fileName): SourceFile {
|
||||
return files[fileName];
|
||||
return files.get(fileName);
|
||||
},
|
||||
getDefaultLibFileName(): string {
|
||||
return "lib.d.ts";
|
||||
@@ -143,9 +143,10 @@ namespace ts {
|
||||
getNewLine(): string {
|
||||
return sys ? sys.newLine : newLine;
|
||||
},
|
||||
fileExists: fileName => fileName in files,
|
||||
fileExists: fileName => files.has(fileName),
|
||||
readFile: fileName => {
|
||||
return fileName in files ? files[fileName].text : undefined;
|
||||
const file = files.get(fileName);
|
||||
return file && file.text;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -188,10 +189,24 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
assert.isTrue(cache !== undefined, `expected ${caption} to be set`);
|
||||
assert.isTrue(equalOwnProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`);
|
||||
assert.isTrue(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`);
|
||||
}
|
||||
}
|
||||
|
||||
/** True if the maps have the same keys and values. */
|
||||
function mapsAreEqual<T>(left: Map<T>, right: Map<T>, valuesAreEqual?: (left: T, right: T) => boolean): boolean {
|
||||
if (left === right) return true;
|
||||
if (!left || !right) return false;
|
||||
const someInLeftHasNoMatch = forEachEntry(left, (leftValue, leftKey) => {
|
||||
if (!right.has(leftKey)) return true;
|
||||
const rightValue = right.get(leftKey);
|
||||
return !(valuesAreEqual ? valuesAreEqual(leftValue, rightValue) : leftValue === rightValue);
|
||||
});
|
||||
if (someInLeftHasNoMatch) return false;
|
||||
const someInRightHasNoMatch = forEachKey(right, rightKey => !left.has(rightKey));
|
||||
return !someInRightHasNoMatch;
|
||||
}
|
||||
|
||||
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<ResolvedModule>): void {
|
||||
checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule);
|
||||
}
|
||||
@@ -307,7 +322,7 @@ namespace ts {
|
||||
const options: CompilerOptions = { target };
|
||||
|
||||
const program_1 = newProgram(files, ["a.ts"], options);
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
||||
@@ -316,7 +331,7 @@ namespace ts {
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
// imports has changed - program is not reused
|
||||
@@ -333,7 +348,7 @@ namespace ts {
|
||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": createResolvedModule("b.ts"), "c": undefined }));
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
|
||||
});
|
||||
|
||||
it("resolved type directives cache follows type directives", () => {
|
||||
@@ -344,7 +359,7 @@ namespace ts {
|
||||
const options: CompilerOptions = { target, typeRoots: ["/types"] };
|
||||
|
||||
const program_1 = newProgram(files, ["/a.ts"], options);
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined);
|
||||
|
||||
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
|
||||
@@ -353,7 +368,7 @@ namespace ts {
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined);
|
||||
|
||||
// type reference directives has changed - program is not reused
|
||||
@@ -371,7 +386,7 @@ namespace ts {
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
});
|
||||
|
||||
it("can reuse ambient module declarations from non-modified files", () => {
|
||||
|
||||
@@ -13,8 +13,7 @@ describe("Colorization", function () {
|
||||
|
||||
function getEntryAtPosition(result: ts.ClassificationResult, position: number) {
|
||||
let entryPosition = 0;
|
||||
for (let i = 0, n = result.entries.length; i < n; i++) {
|
||||
const entry = result.entries[i];
|
||||
for (const entry of result.entries) {
|
||||
if (entryPosition === position) {
|
||||
return entry;
|
||||
}
|
||||
@@ -43,9 +42,7 @@ describe("Colorization", function () {
|
||||
function testLexicalClassification(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
|
||||
const result = classifier.getClassificationsForLine(text, initialEndOfLineState, /*syntacticClassifierAbsent*/ false);
|
||||
|
||||
for (let i = 0, n = expectedEntries.length; i < n; i++) {
|
||||
const expectedEntry = expectedEntries[i];
|
||||
|
||||
for (const expectedEntry of expectedEntries) {
|
||||
if (expectedEntry.classification === undefined) {
|
||||
assert.equal(result.finalLexState, expectedEntry.value, "final endOfLineState does not match expected.");
|
||||
}
|
||||
@@ -352,9 +349,9 @@ describe("Colorization", function () {
|
||||
// Adjusts 'pos' by accounting for the length of each portion of the string,
|
||||
// but only return the last given string
|
||||
function track(...vals: string[]): string {
|
||||
for (let i = 0, n = vals.length; i < n; i++) {
|
||||
for (const val of vals) {
|
||||
pos += lastLength;
|
||||
lastLength = vals[i].length;
|
||||
lastLength = val.length;
|
||||
}
|
||||
return ts.lastOrUndefined(vals);
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ describe("PatternMatcher", function () {
|
||||
function assertArrayEquals<T>(array1: T[], array2: T[]) {
|
||||
assert.equal(array1.length, array2.length);
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
assert.equal(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
const expect: typeof _chai.expect = _chai.expect;
|
||||
|
||||
@@ -416,14 +416,15 @@ namespace ts.server {
|
||||
class InProcClient {
|
||||
private server: InProcSession;
|
||||
private seq = 0;
|
||||
private callbacks = createMap<(resp: protocol.Response) => void>();
|
||||
private callbacks: Array<(resp: protocol.Response) => void> = [];
|
||||
private eventHandlers = createMap<(args: any) => void>();
|
||||
|
||||
handle(msg: protocol.Message): void {
|
||||
if (msg.type === "response") {
|
||||
const response = <protocol.Response>msg;
|
||||
if (response.request_seq in this.callbacks) {
|
||||
this.callbacks[response.request_seq](response);
|
||||
const handler = this.callbacks[response.request_seq];
|
||||
if (handler) {
|
||||
handler(response);
|
||||
delete this.callbacks[response.request_seq];
|
||||
}
|
||||
}
|
||||
@@ -434,13 +435,14 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
emit(name: string, args: any): void {
|
||||
if (name in this.eventHandlers) {
|
||||
this.eventHandlers[name](args);
|
||||
const handler = this.eventHandlers.get(name);
|
||||
if (handler) {
|
||||
handler(args);
|
||||
}
|
||||
}
|
||||
|
||||
on(name: string, handler: (args: any) => void): void {
|
||||
this.eventHandlers[name] = handler;
|
||||
this.eventHandlers.set(name, handler);
|
||||
}
|
||||
|
||||
connect(session: InProcSession): void {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="../../server/scriptVersionCache.ts"/>
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
|
||||
namespace ts.textStorage {
|
||||
describe("Text storage", () => {
|
||||
const f = {
|
||||
path: "/a/app.ts",
|
||||
content: `
|
||||
let x = 1;
|
||||
let y = 2;
|
||||
function bar(a: number) {
|
||||
return a + 1;
|
||||
}`
|
||||
};
|
||||
|
||||
it("text based storage should be have exactly the same as script version cache", () => {
|
||||
|
||||
const host = ts.projectSystem.createServerHost([f]);
|
||||
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.useScriptVersionCache();
|
||||
ts2.useText();
|
||||
|
||||
const lineMap = computeLineStarts(f.content);
|
||||
|
||||
for (let line = 0; line < lineMap.length; line++) {
|
||||
const start = lineMap[line];
|
||||
const end = line === lineMap.length - 1 ? f.path.length : lineMap[line + 1];
|
||||
|
||||
for (let offset = 0; offset < end - start; offset++) {
|
||||
const pos1 = ts1.lineOffsetToPosition(line + 1, offset + 1);
|
||||
const pos2 = ts2.lineOffsetToPosition(line + 1, offset + 1);
|
||||
assert.isTrue(pos1 === pos2, `lineOffsetToPosition ${line + 1}-${offset + 1}: expected ${pos1} to equal ${pos2}`);
|
||||
}
|
||||
|
||||
const {start: start1, length: length1 } = ts1.lineToTextSpan(line);
|
||||
const {start: start2, length: length2 } = ts2.lineToTextSpan(line);
|
||||
assert.isTrue(start1 === start2, `lineToTextSpan ${line}::start:: expected ${start1} to equal ${start2}`);
|
||||
assert.isTrue(length1 === length2, `lineToTextSpan ${line}::length:: expected ${length1} to equal ${length2}`);
|
||||
}
|
||||
|
||||
for (let pos = 0; pos < f.content.length; pos++) {
|
||||
const { line: line1, offset: offset1 } = ts1.positionToLineOffset(pos);
|
||||
const { line: line2, offset: offset2 } = ts2.positionToLineOffset(pos);
|
||||
assert.isTrue(line1 === line2, `positionToLineOffset ${pos}::line:: expected ${line1} to equal ${line2}`);
|
||||
assert.isTrue(offset1 === offset2, `positionToLineOffset ${pos}::offset:: expected ${offset1} to equal ${offset2}`);
|
||||
}
|
||||
});
|
||||
|
||||
it("should switch to script version cache if necessary", () => {
|
||||
const host = ts.projectSystem.createServerHost([f]);
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.getSnapshot();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 1");
|
||||
|
||||
ts1.edit(0, 5, " ");
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 1");
|
||||
|
||||
ts1.useText();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 2");
|
||||
|
||||
ts1.getLineInfo(0);
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2");
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
@@ -51,9 +51,8 @@ namespace ts.projectSystem {
|
||||
throttleLimit: number,
|
||||
installTypingHost: server.ServerHost,
|
||||
readonly typesRegistry = createMap<void>(),
|
||||
telemetryEnabled?: boolean,
|
||||
log?: TI.Log) {
|
||||
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, telemetryEnabled, log);
|
||||
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log);
|
||||
}
|
||||
|
||||
safeFileList = safeList.path;
|
||||
@@ -90,8 +89,8 @@ namespace ts.projectSystem {
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
const request = server.createInstallTypingsRequest(project, typingOptions, unresolvedImports, this.globalTypingsCacheLocation);
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
const request = server.createInstallTypingsRequest(project, typeAcquisition, unresolvedImports, this.globalTypingsCacheLocation);
|
||||
this.install(request);
|
||||
}
|
||||
|
||||
@@ -141,8 +140,8 @@ namespace ts.projectSystem {
|
||||
export interface TestServerHostCreationParameters {
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
executingFilePath?: string;
|
||||
libFile?: FileOrFolder;
|
||||
currentDirectory?: string;
|
||||
newLine?: string;
|
||||
}
|
||||
|
||||
export function createServerHost(fileOrFolderList: FileOrFolder[], params?: TestServerHostCreationParameters): TestServerHost {
|
||||
@@ -153,7 +152,8 @@ namespace ts.projectSystem {
|
||||
params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false,
|
||||
params.executingFilePath || getExecutingFilePathFromLibFile(),
|
||||
params.currentDirectory || "/",
|
||||
fileOrFolderList);
|
||||
fileOrFolderList,
|
||||
params.newLine);
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -244,9 +244,9 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
export function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
|
||||
assert.equal(reduceProperties(map, count => count + 1, 0), expectedKeys.length, `${caption}: incorrect size of map`);
|
||||
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`);
|
||||
for (const name of expectedKeys) {
|
||||
assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`);
|
||||
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
export class Callbacks {
|
||||
private map: { [n: number]: TimeOutCallback } = {};
|
||||
private map: TimeOutCallback[] = [];
|
||||
private nextId = 1;
|
||||
|
||||
register(cb: (...args: any[]) => void, args: any[]) {
|
||||
@@ -310,20 +310,16 @@ namespace ts.projectSystem {
|
||||
count() {
|
||||
let n = 0;
|
||||
for (const _ in this.map) {
|
||||
// TODO: GH#11734
|
||||
_;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
invoke() {
|
||||
for (const id in this.map) {
|
||||
if (hasProperty(this.map, id)) {
|
||||
this.map[id]();
|
||||
}
|
||||
for (const key in this.map) {
|
||||
this.map[key]();
|
||||
}
|
||||
this.map = {};
|
||||
this.map = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +327,6 @@ namespace ts.projectSystem {
|
||||
|
||||
export class TestServerHost implements server.ServerHost {
|
||||
args: string[] = [];
|
||||
newLine: "\n";
|
||||
|
||||
private fs: ts.FileMap<FSEntry>;
|
||||
private getCanonicalFileName: (s: string) => string;
|
||||
@@ -339,12 +334,12 @@ namespace ts.projectSystem {
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
private immediateCallbacks = new Callbacks();
|
||||
|
||||
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
|
||||
readonly watchedFiles = createMap<FileWatcherCallback[]>();
|
||||
readonly watchedDirectories = createMultiMap<{ cb: DirectoryWatcherCallback, recursive: boolean }>();
|
||||
readonly watchedFiles = createMultiMap<FileWatcherCallback>();
|
||||
|
||||
private filesOrFolders: FileOrFolder[];
|
||||
|
||||
constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) {
|
||||
constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[], public readonly newLine = "\n") {
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
|
||||
|
||||
@@ -426,11 +421,11 @@ namespace ts.projectSystem {
|
||||
watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
|
||||
const path = this.toPath(directoryName);
|
||||
const cbWithRecursive = { cb: callback, recursive };
|
||||
multiMapAdd(this.watchedDirectories, path, cbWithRecursive);
|
||||
this.watchedDirectories.add(path, cbWithRecursive);
|
||||
return {
|
||||
referenceCount: 0,
|
||||
directoryName,
|
||||
close: () => multiMapRemove(this.watchedDirectories, path, cbWithRecursive)
|
||||
close: () => this.watchedDirectories.remove(path, cbWithRecursive)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -440,7 +435,7 @@ namespace ts.projectSystem {
|
||||
|
||||
triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {
|
||||
const path = this.toPath(directoryName);
|
||||
const callbacks = this.watchedDirectories[path];
|
||||
const callbacks = this.watchedDirectories.get(path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback.cb(fileName);
|
||||
@@ -450,7 +445,7 @@ namespace ts.projectSystem {
|
||||
|
||||
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
|
||||
const path = this.toPath(fileName);
|
||||
const callbacks = this.watchedFiles[path];
|
||||
const callbacks = this.watchedFiles.get(path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback(path, removed);
|
||||
@@ -460,8 +455,8 @@ namespace ts.projectSystem {
|
||||
|
||||
watchFile(fileName: string, callback: FileWatcherCallback) {
|
||||
const path = this.toPath(fileName);
|
||||
multiMapAdd(this.watchedFiles, path, callback);
|
||||
return { close: () => multiMapRemove(this.watchedFiles, path, callback) };
|
||||
this.watchedFiles.add(path, callback);
|
||||
return { close: () => this.watchedFiles.remove(path, callback) };
|
||||
}
|
||||
|
||||
// TOOD: record and invoke callbacks to simulate timer events
|
||||
@@ -578,6 +573,35 @@ namespace ts.projectSystem {
|
||||
checkWatchedDirectories(host, ["/a/b/c", "/a/b", "/a"]);
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
include: []
|
||||
})
|
||||
};
|
||||
|
||||
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
|
||||
const service = createProjectService(host);
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName: "/a/b/project.csproj",
|
||||
rootFiles: toExternalFiles([f1.path, combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path))]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects[0], []);
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
|
||||
|
||||
checkProjectActualFiles(service.configuredProjects[0], []);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
|
||||
})
|
||||
|
||||
it("create configured project without file list", () => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
@@ -699,6 +723,66 @@ namespace ts.projectSystem {
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
});
|
||||
|
||||
it("remove not-listed external projects", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const f3 = {
|
||||
path: "/c/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const makeProject = (f: FileOrFolder) => ({ projectFileName: f.path + ".csproj", rootFiles: [toExternalFile(f.path)], options: {} });
|
||||
const p1 = makeProject(f1);
|
||||
const p2 = makeProject(f2);
|
||||
const p3 = makeProject(f3);
|
||||
|
||||
const host = createServerHost([f1, f2, f3]);
|
||||
const session = createSession(host);
|
||||
|
||||
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
|
||||
seq: 1,
|
||||
type: "request",
|
||||
command: "openExternalProjects",
|
||||
arguments: { projects: [p1, p2] }
|
||||
});
|
||||
|
||||
const projectService = session.getProjectService();
|
||||
checkNumberOfProjects(projectService, { externalProjects: 2 });
|
||||
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
|
||||
assert.equal(projectService.externalProjects[1].getProjectName(), p2.projectFileName);
|
||||
|
||||
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
|
||||
seq: 2,
|
||||
type: "request",
|
||||
command: "openExternalProjects",
|
||||
arguments: { projects: [p1, p3] }
|
||||
});
|
||||
checkNumberOfProjects(projectService, { externalProjects: 2 });
|
||||
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
|
||||
assert.equal(projectService.externalProjects[1].getProjectName(), p3.projectFileName);
|
||||
|
||||
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
|
||||
seq: 3,
|
||||
type: "request",
|
||||
command: "openExternalProjects",
|
||||
arguments: { projects: [] }
|
||||
});
|
||||
checkNumberOfProjects(projectService, { externalProjects: 0 });
|
||||
|
||||
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
|
||||
seq: 3,
|
||||
type: "request",
|
||||
command: "openExternalProjects",
|
||||
arguments: { projects: [p2] }
|
||||
});
|
||||
assert.equal(projectService.externalProjects[0].getProjectName(), p2.projectFileName);
|
||||
});
|
||||
|
||||
it("handle recreated files correctly", () => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
@@ -1057,6 +1141,69 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, {});
|
||||
});
|
||||
|
||||
it("reload regular file after closing", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "x."
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: "let x: number;"
|
||||
};
|
||||
|
||||
const host = createServerHost([f1, f2, libFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: toExternalFiles([f1.path, f2.path]), options: {} })
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.openClientFile(f2.path, "let x: string");
|
||||
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
|
||||
|
||||
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2);
|
||||
// should contain completions for string
|
||||
assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'");
|
||||
assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'");
|
||||
|
||||
service.closeClientFile(f2.path);
|
||||
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2);
|
||||
// should contain completions for string
|
||||
assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'");
|
||||
assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'");
|
||||
});
|
||||
|
||||
it("clear mixed content file after closing", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: " "
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.html",
|
||||
content: "<html/>"
|
||||
};
|
||||
|
||||
const host = createServerHost([f1, f2, libFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: [{ fileName: f1.path }, { fileName: f2.path, hasMixedContent: true }], options: {} })
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.openClientFile(f2.path, "let somelongname: string");
|
||||
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
|
||||
|
||||
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0);
|
||||
assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'");
|
||||
|
||||
service.closeClientFile(f2.path);
|
||||
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0);
|
||||
assert.isFalse(completions2.entries.some(e => e.name === "somelongname"), "should not contain 'somelongname'");
|
||||
const sf2 = service.externalProjects[0].getLanguageService().getProgram().getSourceFile(f2.path);
|
||||
assert.equal(sf2.text, "");
|
||||
});
|
||||
|
||||
|
||||
it("external project with included config file opened after configured project", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
@@ -1088,6 +1235,7 @@ namespace ts.projectSystem {
|
||||
projectService.closeExternalProject(externalProjectName);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0 });
|
||||
});
|
||||
|
||||
it("external project with included config file opened after configured project and then closed", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
@@ -1441,6 +1589,67 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]);
|
||||
});
|
||||
|
||||
it("tsconfig script block support", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
content: ` `
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/f2.html",
|
||||
content: `var hello = "hello";`
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ compilerOptions: { allowJs: true } })
|
||||
};
|
||||
const host = createServerHost([file1, file2, config]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file1], session);
|
||||
const projectService = session.getProjectService();
|
||||
|
||||
// HTML file will not be included in any projects yet
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
|
||||
// Specify .html extension as mixed content
|
||||
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
|
||||
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
|
||||
session.executeCommand(configureHostRequest).response;
|
||||
|
||||
// HTML file still not included in the project as it is closed
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
|
||||
// Open HTML file
|
||||
projectService.applyChangesInOpenFiles(
|
||||
/*openFiles*/[{ fileName: file2.path, hasMixedContent: true, scriptKind: ScriptKind.JS, content: `var hello = "hello";` }],
|
||||
/*changedFiles*/undefined,
|
||||
/*closedFiles*/undefined);
|
||||
|
||||
// Now HTML file is included in the project
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
|
||||
// Check identifiers defined in HTML content are available in .ts file
|
||||
const project = projectService.configuredProjects[0];
|
||||
let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1);
|
||||
assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`);
|
||||
|
||||
// Close HTML file
|
||||
projectService.applyChangesInOpenFiles(
|
||||
/*openFiles*/undefined,
|
||||
/*changedFiles*/undefined,
|
||||
/*closedFiles*/[file2.path]);
|
||||
|
||||
// HTML file is still included in project
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
|
||||
// Check identifiers defined in HTML content are not available in .ts file
|
||||
completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5);
|
||||
assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`);
|
||||
});
|
||||
|
||||
it("project structure update is deferred if files are not added\removed", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
@@ -1486,7 +1695,7 @@ namespace ts.projectSystem {
|
||||
const project = projectService.externalProjects[0];
|
||||
|
||||
const scriptInfo = project.getScriptInfo(file1.path);
|
||||
const snap = scriptInfo.snap();
|
||||
const snap = scriptInfo.getSnapshot();
|
||||
const actualText = snap.getText(0, snap.getLength());
|
||||
assert.equal(actualText, "", `expected content to be empty string, got "${actualText}"`);
|
||||
|
||||
@@ -1499,7 +1708,7 @@ namespace ts.projectSystem {
|
||||
projectService.closeClientFile(file1.path);
|
||||
|
||||
const scriptInfo2 = project.getScriptInfo(file1.path);
|
||||
const snap2 = scriptInfo2.snap();
|
||||
const snap2 = scriptInfo2.getSnapshot();
|
||||
const actualText2 = snap2.getText(0, snap.getLength());
|
||||
assert.equal(actualText2, "", `expected content to be empty string, got "${actualText2}"`);
|
||||
});
|
||||
@@ -1586,6 +1795,83 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0 });
|
||||
});
|
||||
|
||||
it("language service disabled state is updated in external projects", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: "var x = 1"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/largefile.js",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f1, f2]);
|
||||
const originalGetFileSize = host.getFileSize;
|
||||
host.getFileSize = (filePath: string) =>
|
||||
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
|
||||
|
||||
const service = createProjectService(host);
|
||||
const projectFileName = "/a/proj.csproj";
|
||||
|
||||
service.openExternalProject({
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 1");
|
||||
|
||||
service.openExternalProject({
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
assert.isTrue(service.externalProjects[0].languageServiceEnabled, "language service should be enabled");
|
||||
|
||||
service.openExternalProject({
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
|
||||
});
|
||||
|
||||
it("files are properly detached when language service is disabled", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: "var x = 1"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/largefile.js",
|
||||
content: ""
|
||||
};
|
||||
const f3 = {
|
||||
path: "/a/lib.js",
|
||||
content: "var x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/tsconfig.json",
|
||||
content: JSON.stringify({ compilerOptions: { allowJs: true } })
|
||||
};
|
||||
const host = createServerHost([f1, f2, f3, config]);
|
||||
const originalGetFileSize = host.getFileSize;
|
||||
host.getFileSize = (filePath: string) =>
|
||||
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
|
||||
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
|
||||
projectService.closeClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({});
|
||||
|
||||
for (const f of [f2, f3]) {
|
||||
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path));
|
||||
assert.equal(scriptInfo.containingProjects.length, 0, `expect 0 containing projects for '${f.path}'`)
|
||||
}
|
||||
});
|
||||
|
||||
it("language service disabled events are triggered", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
@@ -1685,6 +1971,31 @@ namespace ts.projectSystem {
|
||||
const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options);
|
||||
assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]);
|
||||
});
|
||||
|
||||
it("snapshot from different caches are incompatible", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const host = createServerHost([f1]);
|
||||
const projectFileName = "/a/b/proj.csproj";
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
rootFiles: [toExternalFile(f1.path)],
|
||||
options: {}
|
||||
})
|
||||
projectService.openClientFile(f1.path, "let x = 1;\nlet y = 2;");
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
projectService.externalProjects[0].getLanguageService(/*ensureSynchronized*/false).getNavigationBarItems(f1.path);
|
||||
projectService.closeClientFile(f1.path);
|
||||
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const navbar = projectService.externalProjects[0].getLanguageService(/*ensureSynchronized*/false).getNavigationBarItems(f1.path);
|
||||
assert.equal(navbar[0].spans[0].length, f1.content.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Proper errors", () => {
|
||||
@@ -1726,8 +2037,8 @@ namespace ts.projectSystem {
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const typingOptions = projectService.externalProjects[0].getTypingOptions();
|
||||
assert.isTrue(typingOptions.enableAutoDiscovery, "Typing autodiscovery should be enabled");
|
||||
const typeAcquisition = projectService.externalProjects[0].getTypeAcquisition();
|
||||
assert.isTrue(typeAcquisition.enable, "Typine acquisition should be enabled");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1754,58 +2065,17 @@ namespace ts.projectSystem {
|
||||
assert.deepEqual(resolutionTrace, [
|
||||
"======== Resolving module 'lib' from '/a/b/app.js'. ========",
|
||||
"Module resolution kind is not specified, using 'NodeJs'.",
|
||||
"Loading module 'lib' from 'node_modules' folder.",
|
||||
"File '/a/b/node_modules/lib.ts' does not exist.",
|
||||
"File '/a/b/node_modules/lib.tsx' does not exist.",
|
||||
"File '/a/b/node_modules/lib.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/lib/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/lib/index.ts' does not exist.",
|
||||
"File '/a/b/node_modules/lib/index.tsx' does not exist.",
|
||||
"File '/a/b/node_modules/lib/index.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/lib/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/@types/lib/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/lib.ts' does not exist.",
|
||||
"File '/a/node_modules/lib.tsx' does not exist.",
|
||||
"File '/a/node_modules/lib.d.ts' does not exist.",
|
||||
"File '/a/node_modules/lib/package.json' does not exist.",
|
||||
"File '/a/node_modules/lib/index.ts' does not exist.",
|
||||
"File '/a/node_modules/lib/index.tsx' does not exist.",
|
||||
"File '/a/node_modules/lib/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/lib/package.json' does not exist.",
|
||||
"File '/a/node_modules/@types/lib/index.d.ts' does not exist.",
|
||||
"File '/node_modules/lib.ts' does not exist.",
|
||||
"File '/node_modules/lib.tsx' does not exist.",
|
||||
"File '/node_modules/lib.d.ts' does not exist.",
|
||||
"File '/node_modules/lib/package.json' does not exist.",
|
||||
"File '/node_modules/lib/index.ts' does not exist.",
|
||||
"File '/node_modules/lib/index.tsx' does not exist.",
|
||||
"File '/node_modules/lib/index.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/lib/package.json' does not exist.",
|
||||
"File '/node_modules/@types/lib/index.d.ts' does not exist.",
|
||||
"Loading module 'lib' from 'node_modules' folder.",
|
||||
"File '/a/b/node_modules/lib.js' does not exist.",
|
||||
"File '/a/b/node_modules/lib.jsx' does not exist.",
|
||||
"File '/a/b/node_modules/lib/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/lib/index.js' does not exist.",
|
||||
"File '/a/b/node_modules/lib/index.jsx' does not exist.",
|
||||
"File '/a/node_modules/lib.js' does not exist.",
|
||||
"File '/a/node_modules/lib.jsx' does not exist.",
|
||||
"File '/a/node_modules/lib/package.json' does not exist.",
|
||||
"File '/a/node_modules/lib/index.js' does not exist.",
|
||||
"File '/a/node_modules/lib/index.jsx' does not exist.",
|
||||
"File '/node_modules/lib.js' does not exist.",
|
||||
"File '/node_modules/lib.jsx' does not exist.",
|
||||
"File '/node_modules/lib/package.json' does not exist.",
|
||||
"File '/node_modules/lib/index.js' does not exist.",
|
||||
"File '/node_modules/lib/index.jsx' does not exist.",
|
||||
"Loading module 'lib' from 'node_modules' folder, target file type 'TypeScript'.",
|
||||
"Directory '/a/b/node_modules' does not exist, skipping all lookups in it.",
|
||||
"Directory '/a/node_modules' does not exist, skipping all lookups in it.",
|
||||
"Directory '/node_modules' does not exist, skipping all lookups in it.",
|
||||
"Loading module 'lib' from 'node_modules' folder, target file type 'JavaScript'.",
|
||||
"Directory '/a/b/node_modules' does not exist, skipping all lookups in it.",
|
||||
"Directory '/a/node_modules' does not exist, skipping all lookups in it.",
|
||||
"Directory '/node_modules' does not exist, skipping all lookups in it.",
|
||||
"======== Module name 'lib' was not resolved. ========",
|
||||
`Auto discovery for typings is enabled in project '${proj.getProjectName()}'. Running extra resolution pass for module 'lib' using cache location '/a/cache'.`,
|
||||
"File '/a/cache/node_modules/lib.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/lib/package.json' does not exist.",
|
||||
"File '/a/cache/node_modules/lib/index.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib/package.json' does not exist.",
|
||||
"File '/a/cache/node_modules/@types/lib/index.d.ts' exist - use it as a name resolution result.",
|
||||
@@ -2197,13 +2467,13 @@ namespace ts.projectSystem {
|
||||
p.updateGraph();
|
||||
|
||||
const scriptInfo = p.getScriptInfo(f.path);
|
||||
checkSnapLength(scriptInfo.snap(), f.content.length);
|
||||
checkSnapLength(scriptInfo.getSnapshot(), f.content.length);
|
||||
|
||||
// open project and replace its content with empty string
|
||||
projectService.openClientFile(f.path, "");
|
||||
checkSnapLength(scriptInfo.snap(), 0);
|
||||
checkSnapLength(scriptInfo.getSnapshot(), 0);
|
||||
});
|
||||
function checkSnapLength(snap: server.LineIndexSnapshot, expectedLength: number) {
|
||||
function checkSnapLength(snap: IScriptSnapshot, expectedLength: number) {
|
||||
assert.equal(snap.getLength(), expectedLength, "Incorrect snapshot size");
|
||||
}
|
||||
});
|
||||
@@ -2339,6 +2609,29 @@ namespace ts.projectSystem {
|
||||
projectService.openExternalProject({ rootFiles: toExternalFiles([f1.path, config.path]), options: {}, projectFileName: projectName });
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
});
|
||||
|
||||
it("types should load from config file path if config exists", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ compilerOptions: { types: ["node"], typeRoots: [] } })
|
||||
};
|
||||
const node = {
|
||||
path: "/a/b/node_modules/@types/node/index.d.ts",
|
||||
content: "declare var process: any"
|
||||
};
|
||||
const cwd = {
|
||||
path: "/a/c"
|
||||
};
|
||||
const host = createServerHost([f1, config, node, cwd], { currentDirectory: cwd.path });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, node.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("add the missing module file for inferred project", () => {
|
||||
@@ -2428,6 +2721,43 @@ namespace ts.projectSystem {
|
||||
openFilesForSession([file], session);
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 1);
|
||||
});
|
||||
|
||||
it("are generated when the config file changes", () => {
|
||||
const serverEventManager = new TestServerEventManager();
|
||||
const file = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {}
|
||||
}`
|
||||
};
|
||||
|
||||
const host = createServerHost([file, configFile]);
|
||||
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
|
||||
openFilesForSession([file], session);
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 1);
|
||||
|
||||
configFile.content = `{
|
||||
"compilerOptions": {
|
||||
"haha": 123
|
||||
}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 2);
|
||||
|
||||
configFile.content = `{
|
||||
"compilerOptions": {}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("skipLibCheck", () => {
|
||||
@@ -2566,7 +2896,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// verify content
|
||||
const projectServiice = session.getProjectService();
|
||||
const snap1 = projectServiice.getScriptInfo(f1.path).snap();
|
||||
const snap1 = projectServiice.getScriptInfo(f1.path).getSnapshot();
|
||||
assert.equal(snap1.getText(0, snap1.getLength()), tmp.content, "content should be equal to the content of temp file");
|
||||
|
||||
// reload from original file file
|
||||
@@ -2578,7 +2908,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
// verify content
|
||||
const snap2 = projectServiice.getScriptInfo(f1.path).snap();
|
||||
const snap2 = projectServiice.getScriptInfo(f1.path).getSnapshot();
|
||||
assert.equal(snap2.getText(0, snap2.getLength()), f1.content, "content should be equal to the content of original file");
|
||||
|
||||
});
|
||||
@@ -2639,6 +2969,65 @@ namespace ts.projectSystem {
|
||||
arguments: { projectFileName: projectName }
|
||||
}).response;
|
||||
assert.isTrue(diags.length === 0);
|
||||
|
||||
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.CompilerOptionsForInferredProjects,
|
||||
seq: 3,
|
||||
arguments: { options: { module: ModuleKind.CommonJS } }
|
||||
});
|
||||
const diagsAfterUpdate = session.executeCommand(<server.protocol.CompilerOptionsDiagnosticsRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.CompilerOptionsDiagnosticsFull,
|
||||
seq: 4,
|
||||
arguments: { projectFileName: projectName }
|
||||
}).response;
|
||||
assert.isTrue(diagsAfterUpdate.length === 0);
|
||||
});
|
||||
|
||||
it("for external project", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/f1.js",
|
||||
content: "function test1() { }"
|
||||
};
|
||||
const host = createServerHost([f1, libFile]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
const projectFileName = "/a/b/project.csproj";
|
||||
const externalFiles = toExternalFiles([f1.path]);
|
||||
projectService.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: externalFiles,
|
||||
options: {}
|
||||
});
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
|
||||
const diags = session.executeCommand(<server.protocol.CompilerOptionsDiagnosticsRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.CompilerOptionsDiagnosticsFull,
|
||||
seq: 2,
|
||||
arguments: { projectFileName }
|
||||
}).response;
|
||||
assert.isTrue(diags.length === 0);
|
||||
|
||||
session.executeCommand(<server.protocol.OpenExternalProjectRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.OpenExternalProject,
|
||||
seq: 3,
|
||||
arguments: {
|
||||
projectFileName,
|
||||
rootFiles: externalFiles,
|
||||
options: { module: ModuleKind.CommonJS }
|
||||
}
|
||||
});
|
||||
const diagsAfterUpdate = session.executeCommand(<server.protocol.CompilerOptionsDiagnosticsRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.CompilerOptionsDiagnosticsFull,
|
||||
seq: 4,
|
||||
arguments: { projectFileName }
|
||||
}).response;
|
||||
assert.isTrue(diagsAfterUpdate.length === 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2658,4 +3047,59 @@ namespace ts.projectSystem {
|
||||
service.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxNodeModuleJsDepth for inferred projects", () => {
|
||||
it("should be set to 2 if the project has js root files", () => {
|
||||
const file1: FileOrFolder = {
|
||||
path: "/a/b/file1.js",
|
||||
content: `var t = require("test"); t.`
|
||||
};
|
||||
const moduleFile: FileOrFolder = {
|
||||
path: "/a/b/node_modules/test/index.js",
|
||||
content: `var v = 10; module.exports = v;`
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, moduleFile]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
let project = projectService.inferredProjects[0];
|
||||
let options = project.getCompilerOptions();
|
||||
assert.isTrue(options.maxNodeModuleJsDepth === 2);
|
||||
|
||||
// Assert the option sticks
|
||||
projectService.setCompilerOptionsForInferredProjects({ target: ScriptTarget.ES2016 });
|
||||
project = projectService.inferredProjects[0];
|
||||
options = project.getCompilerOptions();
|
||||
assert.isTrue(options.maxNodeModuleJsDepth === 2);
|
||||
});
|
||||
|
||||
it("should return to normal state when all js root files are removed from project", () => {
|
||||
const file1 = {
|
||||
path: "/a/file1.ts",
|
||||
content: "let x =1;"
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/file2.js",
|
||||
content: "let x =1;"
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, file2, libFile]);
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true });
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
let project = projectService.inferredProjects[0];
|
||||
assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth);
|
||||
|
||||
projectService.openClientFile(file2.path);
|
||||
project = projectService.inferredProjects[0];
|
||||
assert.isTrue(project.getCompilerOptions().maxNodeModuleJsDepth === 2);
|
||||
|
||||
projectService.closeClientFile(file2.path);
|
||||
project = projectService.inferredProjects[0];
|
||||
assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
@@ -14,19 +14,18 @@ namespace ts.projectSystem {
|
||||
function createTypesRegistry(...list: string[]): Map<void> {
|
||||
const map = createMap<void>();
|
||||
for (const l of list) {
|
||||
map[l] = undefined;
|
||||
map.set(l, undefined);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
class Installer extends TestTypingsInstaller {
|
||||
constructor(host: server.ServerHost, p?: InstallerParams, telemetryEnabled?: boolean, log?: TI.Log) {
|
||||
constructor(host: server.ServerHost, p?: InstallerParams, log?: TI.Log) {
|
||||
super(
|
||||
(p && p.globalTypingsCacheLocation) || "/a/data",
|
||||
(p && p.throttleLimit) || 5,
|
||||
host,
|
||||
(p && p.typesRegistry),
|
||||
telemetryEnabled,
|
||||
log);
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
self.addPostExecAction(installedTypings, success => {
|
||||
for (const file of typingFiles) {
|
||||
host.createFileOrFolder(file, /*createParentDirectory*/ true);
|
||||
@@ -57,8 +56,8 @@ namespace ts.projectSystem {
|
||||
compilerOptions: {
|
||||
allowJs: true
|
||||
},
|
||||
typingOptions: {
|
||||
enableAutoDiscovery: true
|
||||
typeAcquisition: {
|
||||
enable: true
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -145,7 +144,7 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it("external project - no typing options, no .d.ts/js files", () => {
|
||||
it("external project - no type acquisition, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@@ -173,7 +172,7 @@ namespace ts.projectSystem {
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
|
||||
it("external project - no auto in typing acquisition, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@@ -194,11 +193,11 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { include: ["jquery"] }
|
||||
typeAcquisition: { include: ["jquery"] }
|
||||
});
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery even if typing options is set
|
||||
// in this case project contain only ts files - no auto discovery even if type acquisition is set
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
@@ -217,9 +216,9 @@ namespace ts.projectSystem {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports);
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/node"];
|
||||
@@ -234,17 +233,17 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { enableAutoDiscovery: true, include: ["jquery"] }
|
||||
typeAcquisition: { enable: true, include: ["jquery"] }
|
||||
});
|
||||
|
||||
assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true");
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
// autoDiscovery is set in typing options - use it even if project contains only .ts files
|
||||
// auto is set in type acquisition - use it even if project contains only .ts files
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - no typing options, with only js, jsx, d.ts files", () => {
|
||||
it("external project - no type acquisition, with only js, jsx, d.ts files", () => {
|
||||
// Tests:
|
||||
// 1. react typings are installed for .jsx
|
||||
// 2. loose files names are matched against safe list for typings if
|
||||
@@ -288,7 +287,7 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
|
||||
typingOptions: {}
|
||||
typeAcquisition: {}
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
@@ -301,7 +300,7 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
|
||||
});
|
||||
|
||||
it("external project - no typing options, with js & ts files", () => {
|
||||
it("external project - no type acquisition, with js & ts files", () => {
|
||||
// Tests:
|
||||
// 1. No typings are included for JS projects when the project contains ts files
|
||||
const file1 = {
|
||||
@@ -319,9 +318,9 @@ namespace ts.projectSystem {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports);
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings: string[] = [];
|
||||
@@ -336,7 +335,7 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path)],
|
||||
typingOptions: {}
|
||||
typeAcquisition: {}
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
@@ -349,11 +348,11 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(p, [file1.path, file2.path]);
|
||||
});
|
||||
|
||||
it("external project - with typing options, with only js, d.ts files", () => {
|
||||
it("external project - with type acquisition, with only js, d.ts files", () => {
|
||||
// Tests:
|
||||
// 1. Safelist matching, typing options includes/excludes and package.json typings are all acquired
|
||||
// 2. Types for safelist matches are not included when they also appear in the typing option exclude list
|
||||
// 3. Multiple includes and excludes are respected in typing options
|
||||
// 1. Safelist matching, type acquisition includes/excludes and package.json typings are all acquired
|
||||
// 2. Types for safelist matches are not included when they also appear in the type acquisition exclude list
|
||||
// 3. Multiple includes and excludes are respected in type acquisition
|
||||
const file1 = {
|
||||
path: "/a/b/lodash.js",
|
||||
content: ""
|
||||
@@ -411,7 +410,7 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "moment"], exclude: ["lodash"] }
|
||||
typeAcquisition: { include: ["jquery", "moment"], exclude: ["lodash"] }
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
@@ -486,7 +485,7 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "moment"] }
|
||||
typeAcquisition: { include: ["jquery", "moment"] }
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
@@ -572,7 +571,7 @@ namespace ts.projectSystem {
|
||||
projectFileName: projectFileName1,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "cordova"] }
|
||||
typeAcquisition: { include: ["jquery", "cordova"] }
|
||||
});
|
||||
|
||||
installer.checkPendingCommands(/*expectedCount*/ 1);
|
||||
@@ -584,7 +583,7 @@ namespace ts.projectSystem {
|
||||
projectFileName: projectFileName2,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["grunt", "gulp"] }
|
||||
typeAcquisition: { include: ["grunt", "gulp"] }
|
||||
});
|
||||
assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request");
|
||||
|
||||
@@ -907,7 +906,7 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([f1, packageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" }, /*telemetryEnabled*/ false, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
assert(false, "runCommand should not be invoked");
|
||||
@@ -930,7 +929,7 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([f]);
|
||||
const cache = createMap<string>();
|
||||
for (const name of JsTyping.nodeCoreModuleList) {
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enableAutoDiscovery: true }, [name, "somename"]);
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]);
|
||||
assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]);
|
||||
}
|
||||
});
|
||||
@@ -945,8 +944,8 @@ namespace ts.projectSystem {
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f, node]);
|
||||
const cache = createMap<string>({ "node": node.path });
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enableAutoDiscovery: true }, ["fs", "bar"]);
|
||||
const cache = createMapFromTemplate<string>({ "node": node.path });
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]);
|
||||
assert.deepEqual(result.cachedTypingPaths, [node.path]);
|
||||
assert.deepEqual(result.newTypingNames, ["bar"]);
|
||||
});
|
||||
@@ -971,15 +970,18 @@ namespace ts.projectSystem {
|
||||
let seenTelemetryEvent = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") }, /*telemetryEnabled*/ true);
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.TypingsInstallEvent) {
|
||||
if (response.kind === server.EventInstall) {
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
assert.deepEqual(response.packagesToInstall, ["@types/commander"]);
|
||||
seenTelemetryEvent = true;
|
||||
return;
|
||||
@@ -997,4 +999,102 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("progress notifications", () => {
|
||||
it ("should be sent for success", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const commander = {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f1, package]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isTrue(endEvent.installSuccess);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
|
||||
});
|
||||
|
||||
it ("should be sent for error", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const host = createServerHost([f1, package]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
executeCommand(this, host, "", [], cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isFalse(endEvent.installSuccess);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -307,7 +307,7 @@ and grew 1cm per day`;
|
||||
|
||||
it("Start pos from line", () => {
|
||||
for (let i = 0; i < iterationCount; i++) {
|
||||
for (let j = 0, llen = lines.length; j < llen; j++) {
|
||||
for (let j = 0; j < lines.length; j++) {
|
||||
const lineInfo = lineIndex.lineNumberToInfo(j + 1);
|
||||
const lineIndexOffset = lineInfo.offset;
|
||||
const lineMapOffset = lineMap[j];
|
||||
|
||||
@@ -195,11 +195,17 @@ namespace Utils {
|
||||
}
|
||||
|
||||
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
|
||||
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.MapLike<string> | string[]) {
|
||||
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.Map<string> | string[]) {
|
||||
super(currentDirectory, ignoreCase);
|
||||
const fileNames = (files instanceof Array) ? files : ts.getOwnKeys(files);
|
||||
for (const file of fileNames) {
|
||||
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, (files as ts.MapLike<string>)[file], /*isRootFile*/false));
|
||||
if (files instanceof Array) {
|
||||
for (const file of files) {
|
||||
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, undefined, /*isRootFile*/false));
|
||||
}
|
||||
}
|
||||
else {
|
||||
files.forEach((fileContent, fileName) => {
|
||||
this.addFile(fileName, new Harness.LanguageService.ScriptInfo(fileName, fileContent, /*isRootFile*/false));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1687
-2599
File diff suppressed because it is too large
Load Diff
Vendored
+2
-2
@@ -4,7 +4,7 @@ interface Map<K, V> {
|
||||
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): this;
|
||||
set(key: K, value: V): this;
|
||||
readonly size: number;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ interface WeakMap<K, V> {
|
||||
delete(key: K): boolean;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): this;
|
||||
set(key: K, value: V): this;
|
||||
}
|
||||
|
||||
interface WeakMapConstructor {
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ interface Symbol {
|
||||
toString(): string;
|
||||
|
||||
/** Returns the primitive value of the specified object. */
|
||||
valueOf(): Object;
|
||||
valueOf(): symbol;
|
||||
}
|
||||
|
||||
interface SymbolConstructor {
|
||||
|
||||
Vendored
+9
@@ -7,6 +7,15 @@ interface Array<T> {
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
}
|
||||
|
||||
interface ReadonlyArray<T> {
|
||||
/**
|
||||
* Determines whether an array includes a certain element, returning true or false as appropriate.
|
||||
* @param searchElement The element to search for.
|
||||
* @param fromIndex The position in this array at which to begin searching for searchElement.
|
||||
*/
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
}
|
||||
|
||||
interface Int8Array {
|
||||
/**
|
||||
* Determines whether an array includes a certain element, returning true or false as appropriate.
|
||||
|
||||
Vendored
+12
-1
@@ -4,11 +4,22 @@ interface ObjectConstructor {
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
values<T>(o: { [s: string]: T }): T[];
|
||||
|
||||
/**
|
||||
* Returns an array of values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
values(o: any): any[];
|
||||
|
||||
/**
|
||||
* Returns an array of key/values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries<T>(o: { [s: string]: T }): [string, T][];
|
||||
|
||||
/**
|
||||
* Returns an array of key/values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries<T extends { [key: string]: any }, K extends keyof T>(o: T): [keyof T, T[K]][];
|
||||
entries(o: any): [string, any][];
|
||||
}
|
||||
|
||||
Vendored
+15
-2
@@ -176,6 +176,18 @@ interface ObjectConstructor {
|
||||
*/
|
||||
seal<T>(o: T): T;
|
||||
|
||||
/**
|
||||
* Prevents the modification of existing property attributes and values, and prevents the addition of new properties.
|
||||
* @param o Object on which to lock the attributes.
|
||||
*/
|
||||
freeze<T>(a: T[]): ReadonlyArray<T>;
|
||||
|
||||
/**
|
||||
* Prevents the modification of existing property attributes and values, and prevents the addition of new properties.
|
||||
* @param o Object on which to lock the attributes.
|
||||
*/
|
||||
freeze<T extends Function>(f: T): T;
|
||||
|
||||
/**
|
||||
* Prevents the modification of existing property attributes and values, and prevents the addition of new properties.
|
||||
* @param o Object on which to lock the attributes.
|
||||
@@ -1159,8 +1171,9 @@ interface Array<T> {
|
||||
/**
|
||||
* Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
* @param deleteCount The number of elements to remove.
|
||||
*/
|
||||
splice(start: number): T[];
|
||||
splice(start: number, deleteCount?: number): T[];
|
||||
/**
|
||||
* Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
@@ -1367,7 +1380,7 @@ type Pick<T, K extends keyof T> = {
|
||||
/**
|
||||
* Construct a type with a set of properties K of type T
|
||||
*/
|
||||
type Record<K extends string | number, T> = {
|
||||
type Record<K extends string, T> = {
|
||||
[P in K]: T;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+131
-83
@@ -8,6 +8,7 @@ interface Algorithm {
|
||||
}
|
||||
|
||||
interface EventInit {
|
||||
scoped?: boolean;
|
||||
bubbles?: boolean;
|
||||
cancelable?: boolean;
|
||||
}
|
||||
@@ -242,10 +243,12 @@ interface Event {
|
||||
readonly target: EventTarget;
|
||||
readonly timeStamp: number;
|
||||
readonly type: string;
|
||||
readonly scoped: boolean;
|
||||
initEvent(eventTypeArg: string, canBubbleArg: boolean, cancelableArg: boolean): void;
|
||||
preventDefault(): void;
|
||||
stopImmediatePropagation(): void;
|
||||
stopPropagation(): void;
|
||||
deepPath(): EventTarget[];
|
||||
readonly AT_TARGET: number;
|
||||
readonly BUBBLING_PHASE: number;
|
||||
readonly CAPTURING_PHASE: number;
|
||||
@@ -298,6 +301,7 @@ interface FileReader extends EventTarget, MSBaseReader {
|
||||
readAsBinaryString(blob: Blob): void;
|
||||
readAsDataURL(blob: Blob): void;
|
||||
readAsText(blob: Blob, encoding?: string): void;
|
||||
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -339,11 +343,16 @@ declare var IDBCursorWithValue: {
|
||||
new(): IDBCursorWithValue;
|
||||
}
|
||||
|
||||
interface IDBDatabaseEventMap {
|
||||
"abort": Event;
|
||||
"error": ErrorEvent;
|
||||
}
|
||||
|
||||
interface IDBDatabase extends EventTarget {
|
||||
readonly name: string;
|
||||
readonly objectStoreNames: DOMStringList;
|
||||
onabort: (this: this, ev: Event) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onabort: (this: IDBDatabase, ev: Event) => any;
|
||||
onerror: (this: IDBDatabase, ev: ErrorEvent) => any;
|
||||
version: number;
|
||||
onversionchange: (ev: IDBVersionChangeEvent) => any;
|
||||
close(): void;
|
||||
@@ -351,8 +360,7 @@ interface IDBDatabase extends EventTarget {
|
||||
deleteObjectStore(name: string): void;
|
||||
transaction(storeNames: string | string[], mode?: string): IDBTransaction;
|
||||
addEventListener(type: "versionchange", listener: (ev: IDBVersionChangeEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -429,13 +437,15 @@ declare var IDBObjectStore: {
|
||||
new(): IDBObjectStore;
|
||||
}
|
||||
|
||||
interface IDBOpenDBRequestEventMap extends IDBRequestEventMap {
|
||||
"blocked": Event;
|
||||
"upgradeneeded": IDBVersionChangeEvent;
|
||||
}
|
||||
|
||||
interface IDBOpenDBRequest extends IDBRequest {
|
||||
onblocked: (this: this, ev: Event) => any;
|
||||
onupgradeneeded: (this: this, ev: IDBVersionChangeEvent) => any;
|
||||
addEventListener(type: "blocked", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "success", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "upgradeneeded", listener: (this: this, ev: IDBVersionChangeEvent) => any, useCapture?: boolean): void;
|
||||
onblocked: (this: IDBOpenDBRequest, ev: Event) => any;
|
||||
onupgradeneeded: (this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any;
|
||||
addEventListener<K extends keyof IDBOpenDBRequestEventMap>(type: K, listener: (this: IDBOpenDBRequest, ev: IDBOpenDBRequestEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -444,16 +454,20 @@ declare var IDBOpenDBRequest: {
|
||||
new(): IDBOpenDBRequest;
|
||||
}
|
||||
|
||||
interface IDBRequestEventMap {
|
||||
"error": ErrorEvent;
|
||||
"success": Event;
|
||||
}
|
||||
|
||||
interface IDBRequest extends EventTarget {
|
||||
readonly error: DOMError;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onsuccess: (this: this, ev: Event) => any;
|
||||
onerror: (this: IDBRequest, ev: ErrorEvent) => any;
|
||||
onsuccess: (this: IDBRequest, ev: Event) => any;
|
||||
readonly readyState: string;
|
||||
readonly result: any;
|
||||
source: IDBObjectStore | IDBIndex | IDBCursor;
|
||||
readonly transaction: IDBTransaction;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "success", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof IDBRequestEventMap>(type: K, listener: (this: IDBRequest, ev: IDBRequestEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -462,21 +476,25 @@ declare var IDBRequest: {
|
||||
new(): IDBRequest;
|
||||
}
|
||||
|
||||
interface IDBTransactionEventMap {
|
||||
"abort": Event;
|
||||
"complete": Event;
|
||||
"error": ErrorEvent;
|
||||
}
|
||||
|
||||
interface IDBTransaction extends EventTarget {
|
||||
readonly db: IDBDatabase;
|
||||
readonly error: DOMError;
|
||||
readonly mode: string;
|
||||
onabort: (this: this, ev: Event) => any;
|
||||
oncomplete: (this: this, ev: Event) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onabort: (this: IDBTransaction, ev: Event) => any;
|
||||
oncomplete: (this: IDBTransaction, ev: Event) => any;
|
||||
onerror: (this: IDBTransaction, ev: ErrorEvent) => any;
|
||||
abort(): void;
|
||||
objectStore(name: string): IDBObjectStore;
|
||||
readonly READ_ONLY: string;
|
||||
readonly READ_WRITE: string;
|
||||
readonly VERSION_CHANGE: string;
|
||||
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "complete", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof IDBTransactionEventMap>(type: K, listener: (this: IDBTransaction, ev: IDBTransactionEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -533,18 +551,22 @@ interface MSApp {
|
||||
}
|
||||
declare var MSApp: MSApp;
|
||||
|
||||
interface MSAppAsyncOperationEventMap {
|
||||
"complete": Event;
|
||||
"error": ErrorEvent;
|
||||
}
|
||||
|
||||
interface MSAppAsyncOperation extends EventTarget {
|
||||
readonly error: DOMError;
|
||||
oncomplete: (this: this, ev: Event) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
oncomplete: (this: MSAppAsyncOperation, ev: Event) => any;
|
||||
onerror: (this: MSAppAsyncOperation, ev: ErrorEvent) => any;
|
||||
readonly readyState: number;
|
||||
readonly result: any;
|
||||
start(): void;
|
||||
readonly COMPLETED: number;
|
||||
readonly ERROR: number;
|
||||
readonly STARTED: number;
|
||||
addEventListener(type: "complete", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof MSAppAsyncOperationEventMap>(type: K, listener: (this: MSAppAsyncOperation, ev: MSAppAsyncOperationEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -584,6 +606,7 @@ interface MSStreamReader extends EventTarget, MSBaseReader {
|
||||
readAsBlob(stream: MSStream, size?: number): void;
|
||||
readAsDataURL(stream: MSStream, size?: number): void;
|
||||
readAsText(stream: MSStream, encoding?: string, size?: number): void;
|
||||
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -627,12 +650,16 @@ declare var MessageEvent: {
|
||||
new(type: string, eventInitDict?: MessageEventInit): MessageEvent;
|
||||
}
|
||||
|
||||
interface MessagePortEventMap {
|
||||
"message": MessageEvent;
|
||||
}
|
||||
|
||||
interface MessagePort extends EventTarget {
|
||||
onmessage: (this: this, ev: MessageEvent) => any;
|
||||
onmessage: (this: MessagePort, ev: MessageEvent) => any;
|
||||
close(): void;
|
||||
postMessage(message?: any, ports?: any): void;
|
||||
start(): void;
|
||||
addEventListener(type: "message", listener: (this: this, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof MessagePortEventMap>(type: K, listener: (this: MessagePort, ev: MessagePortEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -680,14 +707,21 @@ declare var ProgressEvent: {
|
||||
new(type: string, eventInitDict?: ProgressEventInit): ProgressEvent;
|
||||
}
|
||||
|
||||
interface WebSocketEventMap {
|
||||
"close": CloseEvent;
|
||||
"error": ErrorEvent;
|
||||
"message": MessageEvent;
|
||||
"open": Event;
|
||||
}
|
||||
|
||||
interface WebSocket extends EventTarget {
|
||||
binaryType: string;
|
||||
readonly bufferedAmount: number;
|
||||
readonly extensions: string;
|
||||
onclose: (this: this, ev: CloseEvent) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onmessage: (this: this, ev: MessageEvent) => any;
|
||||
onopen: (this: this, ev: Event) => any;
|
||||
onclose: (this: WebSocket, ev: CloseEvent) => any;
|
||||
onerror: (this: WebSocket, ev: ErrorEvent) => any;
|
||||
onmessage: (this: WebSocket, ev: MessageEvent) => any;
|
||||
onopen: (this: WebSocket, ev: Event) => any;
|
||||
readonly protocol: string;
|
||||
readonly readyState: number;
|
||||
readonly url: string;
|
||||
@@ -697,10 +731,7 @@ interface WebSocket extends EventTarget {
|
||||
readonly CLOSING: number;
|
||||
readonly CONNECTING: number;
|
||||
readonly OPEN: number;
|
||||
addEventListener(type: "close", listener: (this: this, ev: CloseEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "message", listener: (this: this, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "open", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -713,12 +744,15 @@ declare var WebSocket: {
|
||||
readonly OPEN: number;
|
||||
}
|
||||
|
||||
interface WorkerEventMap extends AbstractWorkerEventMap {
|
||||
"message": MessageEvent;
|
||||
}
|
||||
|
||||
interface Worker extends EventTarget, AbstractWorker {
|
||||
onmessage: (this: this, ev: MessageEvent) => any;
|
||||
onmessage: (this: Worker, ev: MessageEvent) => any;
|
||||
postMessage(message: any, ports?: any): void;
|
||||
terminate(): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "message", listener: (this: this, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -727,8 +761,12 @@ declare var Worker: {
|
||||
new(stringUrl: string): Worker;
|
||||
}
|
||||
|
||||
interface XMLHttpRequestEventMap extends XMLHttpRequestEventTargetEventMap {
|
||||
"readystatechange": Event;
|
||||
}
|
||||
|
||||
interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
|
||||
onreadystatechange: (this: this, ev: Event) => any;
|
||||
onreadystatechange: (this: XMLHttpRequest, ev: Event) => any;
|
||||
readonly readyState: number;
|
||||
readonly response: any;
|
||||
readonly responseText: string;
|
||||
@@ -755,14 +793,7 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
|
||||
readonly LOADING: number;
|
||||
readonly OPENED: number;
|
||||
readonly UNSENT: number;
|
||||
addEventListener(type: "abort", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "load", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadstart", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "readystatechange", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "timeout", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -778,6 +809,7 @@ declare var XMLHttpRequest: {
|
||||
}
|
||||
|
||||
interface XMLHttpRequestUpload extends EventTarget, XMLHttpRequestEventTarget {
|
||||
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -786,31 +818,39 @@ declare var XMLHttpRequestUpload: {
|
||||
new(): XMLHttpRequestUpload;
|
||||
}
|
||||
|
||||
interface AbstractWorkerEventMap {
|
||||
"error": ErrorEvent;
|
||||
}
|
||||
|
||||
interface AbstractWorker {
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
onerror: (this: AbstractWorker, ev: ErrorEvent) => any;
|
||||
addEventListener<K extends keyof AbstractWorkerEventMap>(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
interface MSBaseReaderEventMap {
|
||||
"abort": Event;
|
||||
"error": ErrorEvent;
|
||||
"load": Event;
|
||||
"loadend": ProgressEvent;
|
||||
"loadstart": Event;
|
||||
"progress": ProgressEvent;
|
||||
}
|
||||
|
||||
interface MSBaseReader {
|
||||
onabort: (this: this, ev: Event) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onload: (this: this, ev: Event) => any;
|
||||
onloadend: (this: this, ev: ProgressEvent) => any;
|
||||
onloadstart: (this: this, ev: Event) => any;
|
||||
onprogress: (this: this, ev: ProgressEvent) => any;
|
||||
onabort: (this: MSBaseReader, ev: Event) => any;
|
||||
onerror: (this: MSBaseReader, ev: ErrorEvent) => any;
|
||||
onload: (this: MSBaseReader, ev: Event) => any;
|
||||
onloadend: (this: MSBaseReader, ev: ProgressEvent) => any;
|
||||
onloadstart: (this: MSBaseReader, ev: Event) => any;
|
||||
onprogress: (this: MSBaseReader, ev: ProgressEvent) => any;
|
||||
readonly readyState: number;
|
||||
readonly result: any;
|
||||
abort(): void;
|
||||
readonly DONE: number;
|
||||
readonly EMPTY: number;
|
||||
readonly LOADING: number;
|
||||
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "load", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadstart", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -838,21 +878,25 @@ interface WindowConsole {
|
||||
readonly console: Console;
|
||||
}
|
||||
|
||||
interface XMLHttpRequestEventTargetEventMap {
|
||||
"abort": Event;
|
||||
"error": ErrorEvent;
|
||||
"load": Event;
|
||||
"loadend": ProgressEvent;
|
||||
"loadstart": Event;
|
||||
"progress": ProgressEvent;
|
||||
"timeout": ProgressEvent;
|
||||
}
|
||||
|
||||
interface XMLHttpRequestEventTarget {
|
||||
onabort: (this: this, ev: Event) => any;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onload: (this: this, ev: Event) => any;
|
||||
onloadend: (this: this, ev: ProgressEvent) => any;
|
||||
onloadstart: (this: this, ev: Event) => any;
|
||||
onprogress: (this: this, ev: ProgressEvent) => any;
|
||||
ontimeout: (this: this, ev: ProgressEvent) => any;
|
||||
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "load", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "loadstart", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "timeout", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
|
||||
onabort: (this: XMLHttpRequestEventTarget, ev: Event) => any;
|
||||
onerror: (this: XMLHttpRequestEventTarget, ev: ErrorEvent) => any;
|
||||
onload: (this: XMLHttpRequestEventTarget, ev: Event) => any;
|
||||
onloadend: (this: XMLHttpRequestEventTarget, ev: ProgressEvent) => any;
|
||||
onloadstart: (this: XMLHttpRequestEventTarget, ev: Event) => any;
|
||||
onprogress: (this: XMLHttpRequestEventTarget, ev: ProgressEvent) => any;
|
||||
ontimeout: (this: XMLHttpRequestEventTarget, ev: ProgressEvent) => any;
|
||||
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -868,15 +912,18 @@ declare var FileReaderSync: {
|
||||
new(): FileReaderSync;
|
||||
}
|
||||
|
||||
interface WorkerGlobalScopeEventMap extends DedicatedWorkerGlobalScopeEventMap {
|
||||
"error": ErrorEvent;
|
||||
}
|
||||
|
||||
interface WorkerGlobalScope extends EventTarget, WorkerUtils, DedicatedWorkerGlobalScope, WindowConsole {
|
||||
readonly location: WorkerLocation;
|
||||
onerror: (this: this, ev: ErrorEvent) => any;
|
||||
onerror: (this: WorkerGlobalScope, ev: ErrorEvent) => any;
|
||||
readonly self: WorkerGlobalScope;
|
||||
close(): void;
|
||||
msWriteProfilerMark(profilerMarkName: string): void;
|
||||
toString(): string;
|
||||
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: "message", listener: (this: this, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -904,7 +951,6 @@ declare var WorkerLocation: {
|
||||
|
||||
interface WorkerNavigator extends Object, NavigatorID, NavigatorOnLine {
|
||||
readonly hardwareConcurrency: number;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
declare var WorkerNavigator: {
|
||||
@@ -912,10 +958,14 @@ declare var WorkerNavigator: {
|
||||
new(): WorkerNavigator;
|
||||
}
|
||||
|
||||
interface DedicatedWorkerGlobalScopeEventMap {
|
||||
"message": MessageEvent;
|
||||
}
|
||||
|
||||
interface DedicatedWorkerGlobalScope {
|
||||
onmessage: (this: this, ev: MessageEvent) => any;
|
||||
onmessage: (this: DedicatedWorkerGlobalScope, ev: MessageEvent) => any;
|
||||
postMessage(data: any): void;
|
||||
addEventListener(type: "message", listener: (this: this, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -1176,7 +1226,6 @@ declare var self: WorkerGlobalScope;
|
||||
declare function close(): void;
|
||||
declare function msWriteProfilerMark(profilerMarkName: string): void;
|
||||
declare function toString(): string;
|
||||
declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
declare function dispatchEvent(evt: Event): boolean;
|
||||
declare function removeEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
declare var indexedDB: IDBFactory;
|
||||
@@ -1197,8 +1246,7 @@ declare function btoa(rawString: string): string;
|
||||
declare var onmessage: (this: WorkerGlobalScope, ev: MessageEvent) => any;
|
||||
declare function postMessage(data: any): void;
|
||||
declare var console: Console;
|
||||
declare function addEventListener(type: "error", listener: (this: WorkerGlobalScope, ev: ErrorEvent) => any, useCapture?: boolean): void;
|
||||
declare function addEventListener(type: "message", listener: (this: WorkerGlobalScope, ev: MessageEvent) => any, useCapture?: boolean): void;
|
||||
declare function addEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, useCapture?: boolean): void;
|
||||
declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
|
||||
type AlgorithmIdentifier = string | Algorithm;
|
||||
type IDBKeyPath = string;
|
||||
|
||||
+32
-12
@@ -75,17 +75,32 @@ namespace ts.server {
|
||||
getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
|
||||
onProjectUpdateGraph(): void;
|
||||
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
abstract class AbstractBuilder<T extends BuilderFileInfo> implements Builder {
|
||||
|
||||
private fileInfos = createFileMap<T>();
|
||||
/**
|
||||
* stores set of files from the project.
|
||||
* NOTE: this field is created on demand and should not be accessed directly.
|
||||
* Use 'getFileInfos' instead.
|
||||
*/
|
||||
private fileInfos_doNotAccessDirectly: FileMap<T>;
|
||||
|
||||
constructor(public readonly project: Project, private ctor: { new (scriptInfo: ScriptInfo, project: Project): T }) {
|
||||
}
|
||||
|
||||
private getFileInfos() {
|
||||
return this.fileInfos_doNotAccessDirectly || (this.fileInfos_doNotAccessDirectly = createFileMap<T>());
|
||||
}
|
||||
|
||||
public clear() {
|
||||
// drop the existing list - it will be re-created as necessary
|
||||
this.fileInfos_doNotAccessDirectly = undefined;
|
||||
}
|
||||
|
||||
protected getFileInfo(path: Path): T {
|
||||
return this.fileInfos.get(path);
|
||||
return this.getFileInfos().get(path);
|
||||
}
|
||||
|
||||
protected getOrCreateFileInfo(path: Path): T {
|
||||
@@ -99,19 +114,19 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
protected getFileInfoPaths(): Path[] {
|
||||
return this.fileInfos.getKeys();
|
||||
return this.getFileInfos().getKeys();
|
||||
}
|
||||
|
||||
protected setFileInfo(path: Path, info: T) {
|
||||
this.fileInfos.set(path, info);
|
||||
this.getFileInfos().set(path, info);
|
||||
}
|
||||
|
||||
protected removeFileInfo(path: Path) {
|
||||
this.fileInfos.remove(path);
|
||||
this.getFileInfos().remove(path);
|
||||
}
|
||||
|
||||
protected forEachFileInfo(action: (fileInfo: T) => any) {
|
||||
this.fileInfos.forEachValue((_path, value) => action(value));
|
||||
this.getFileInfos().forEachValue((_path, value) => action(value));
|
||||
}
|
||||
|
||||
abstract getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
|
||||
@@ -231,6 +246,11 @@ namespace ts.server {
|
||||
|
||||
private projectVersionForDependencyGraph: string;
|
||||
|
||||
public clear() {
|
||||
this.projectVersionForDependencyGraph = undefined;
|
||||
super.clear();
|
||||
}
|
||||
|
||||
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): ModuleBuilderFileInfo[] {
|
||||
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
|
||||
return [];
|
||||
@@ -336,24 +356,24 @@ namespace ts.server {
|
||||
// Use slice to clone the array to avoid manipulating in place
|
||||
const queue = fileInfo.referencedBy.slice(0);
|
||||
const fileNameSet = createMap<ScriptInfo>();
|
||||
fileNameSet[scriptInfo.fileName] = scriptInfo;
|
||||
fileNameSet.set(scriptInfo.fileName, scriptInfo);
|
||||
while (queue.length > 0) {
|
||||
const processingFileInfo = queue.pop();
|
||||
if (processingFileInfo.updateShapeSignature() && processingFileInfo.referencedBy.length > 0) {
|
||||
for (const potentialFileInfo of processingFileInfo.referencedBy) {
|
||||
if (!fileNameSet[potentialFileInfo.scriptInfo.fileName]) {
|
||||
if (!fileNameSet.has(potentialFileInfo.scriptInfo.fileName)) {
|
||||
queue.push(potentialFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
fileNameSet[processingFileInfo.scriptInfo.fileName] = processingFileInfo.scriptInfo;
|
||||
fileNameSet.set(processingFileInfo.scriptInfo.fileName, processingFileInfo.scriptInfo);
|
||||
}
|
||||
const result: string[] = [];
|
||||
for (const fileName in fileNameSet) {
|
||||
if (shouldEmitFile(fileNameSet[fileName])) {
|
||||
fileNameSet.forEach((scriptInfo, fileName) => {
|
||||
if (shouldEmitFile(scriptInfo)) {
|
||||
result.push(fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"cancellationToken.ts"
|
||||
|
||||
@@ -31,10 +31,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getLineMap(fileName: string): number[] {
|
||||
let lineMap = this.lineMaps[fileName];
|
||||
let lineMap = this.lineMaps.get(fileName);
|
||||
if (!lineMap) {
|
||||
const scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
lineMap = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
this.lineMaps.set(fileName, lineMap);
|
||||
}
|
||||
return lineMap;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ namespace ts.server {
|
||||
|
||||
changeFile(fileName: string, start: number, end: number, newText: string): void {
|
||||
// clear the line map after an edit
|
||||
this.lineMaps[fileName] = undefined;
|
||||
this.lineMaps.set(fileName, undefined);
|
||||
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
|
||||
+176
-93
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\compiler\commandLineParser.ts" />
|
||||
/// <reference path="..\compiler\commandLineParser.ts" />
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="utilities.ts" />
|
||||
/// <reference path="session.ts" />
|
||||
@@ -41,17 +41,17 @@ namespace ts.server {
|
||||
if (typeof option.type === "object") {
|
||||
const optionMap = <Map<number>>option.type;
|
||||
// verify that map contains only numbers
|
||||
for (const id in optionMap) {
|
||||
Debug.assert(typeof optionMap[id] === "number");
|
||||
}
|
||||
map[option.name] = optionMap;
|
||||
optionMap.forEach(value => {
|
||||
Debug.assert(typeof value === "number");
|
||||
});
|
||||
map.set(option.name, optionMap);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
const compilerOptionConverters = prepareConvertersForEnumLikeCompilerOptions(optionDeclarations);
|
||||
const indentStyle = createMap({
|
||||
const indentStyle = createMapFromTemplate({
|
||||
"none": IndentStyle.None,
|
||||
"block": IndentStyle.Block,
|
||||
"smart": IndentStyle.Smart
|
||||
@@ -59,20 +59,19 @@ namespace ts.server {
|
||||
|
||||
export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings {
|
||||
if (typeof protocolOptions.indentStyle === "string") {
|
||||
protocolOptions.indentStyle = indentStyle[protocolOptions.indentStyle.toLowerCase()];
|
||||
protocolOptions.indentStyle = indentStyle.get(protocolOptions.indentStyle.toLowerCase());
|
||||
Debug.assert(protocolOptions.indentStyle !== undefined);
|
||||
}
|
||||
return <any>protocolOptions;
|
||||
}
|
||||
|
||||
export function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin {
|
||||
for (const id in compilerOptionConverters) {
|
||||
compilerOptionConverters.forEach((mappedValues, id) => {
|
||||
const propertyValue = protocolOptions[id];
|
||||
if (typeof propertyValue === "string") {
|
||||
const mappedValues = compilerOptionConverters[id];
|
||||
protocolOptions[id] = mappedValues[propertyValue.toLowerCase()];
|
||||
protocolOptions[id] = mappedValues.get(propertyValue.toLowerCase());
|
||||
}
|
||||
}
|
||||
});
|
||||
return <any>protocolOptions;
|
||||
}
|
||||
|
||||
@@ -108,6 +107,7 @@ namespace ts.server {
|
||||
export interface HostConfiguration {
|
||||
formatCodeOptions: FormatCodeSettings;
|
||||
hostInfo: string;
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
||||
interface ConfigFileConversionResult {
|
||||
@@ -125,20 +125,23 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export interface OpenConfiguredProjectResult {
|
||||
configFileName?: string;
|
||||
configFileName?: NormalizedPath;
|
||||
configFileErrors?: Diagnostic[];
|
||||
}
|
||||
|
||||
interface FilePropertyReader<T> {
|
||||
getFileName(f: T): string;
|
||||
getScriptKind(f: T): ScriptKind;
|
||||
hasMixedContent(f: T): boolean;
|
||||
hasMixedContent(f: T, extraFileExtensions: FileExtensionInfo[]): boolean;
|
||||
}
|
||||
|
||||
const fileNamePropertyReader: FilePropertyReader<string> = {
|
||||
getFileName: x => x,
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: _ => false
|
||||
hasMixedContent: (fileName, extraFileExtensions) => {
|
||||
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension))
|
||||
}
|
||||
};
|
||||
|
||||
const externalFilePropertyReader: FilePropertyReader<protocol.ExternalFile> = {
|
||||
@@ -189,11 +192,12 @@ namespace ts.server {
|
||||
|
||||
stopWatchingDirectory(directory: string) {
|
||||
// if the ref count for this directory watcher drops to 0, it's time to close it
|
||||
this.directoryWatchersRefCount[directory]--;
|
||||
if (this.directoryWatchersRefCount[directory] === 0) {
|
||||
const refCount = this.directoryWatchersRefCount.get(directory) - 1;
|
||||
this.directoryWatchersRefCount.set(directory, refCount);
|
||||
if (refCount === 0) {
|
||||
this.projectService.logger.info(`Close directory watcher for: ${directory}`);
|
||||
this.directoryWatchersForTsconfig[directory].close();
|
||||
delete this.directoryWatchersForTsconfig[directory];
|
||||
this.directoryWatchersForTsconfig.get(directory).close();
|
||||
this.directoryWatchersForTsconfig.delete(directory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,13 +205,13 @@ namespace ts.server {
|
||||
let currentPath = getDirectoryPath(fileName);
|
||||
let parentPath = getDirectoryPath(currentPath);
|
||||
while (currentPath != parentPath) {
|
||||
if (!this.directoryWatchersForTsconfig[currentPath]) {
|
||||
if (!this.directoryWatchersForTsconfig.has(currentPath)) {
|
||||
this.projectService.logger.info(`Add watcher for: ${currentPath}`);
|
||||
this.directoryWatchersForTsconfig[currentPath] = this.projectService.host.watchDirectory(currentPath, callback);
|
||||
this.directoryWatchersRefCount[currentPath] = 1;
|
||||
this.directoryWatchersForTsconfig.set(currentPath, this.projectService.host.watchDirectory(currentPath, callback));
|
||||
this.directoryWatchersRefCount.set(currentPath, 1);
|
||||
}
|
||||
else {
|
||||
this.directoryWatchersRefCount[currentPath] += 1;
|
||||
this.directoryWatchersRefCount.set(currentPath, this.directoryWatchersRefCount.get(currentPath) + 1);
|
||||
}
|
||||
project.directoriesWatchedForTsconfig.push(currentPath);
|
||||
currentPath = parentPath;
|
||||
@@ -257,7 +261,7 @@ namespace ts.server {
|
||||
|
||||
private changedFiles: ScriptInfo[];
|
||||
|
||||
private toCanonicalFileName: (f: string) => string;
|
||||
readonly toCanonicalFileName: (f: string) => string;
|
||||
|
||||
public lastDeletedFile: ScriptInfo;
|
||||
|
||||
@@ -282,12 +286,14 @@ namespace ts.server {
|
||||
|
||||
this.hostConfiguration = {
|
||||
formatCodeOptions: getDefaultFormatCodeSettings(this.host),
|
||||
hostInfo: "Unknown host"
|
||||
hostInfo: "Unknown host",
|
||||
extraFileExtensions: []
|
||||
};
|
||||
|
||||
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames, host.getCurrentDirectory());
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
getChangedFiles_TestOnly() {
|
||||
return this.changedFiles;
|
||||
}
|
||||
@@ -317,7 +323,7 @@ namespace ts.server {
|
||||
}
|
||||
switch (response.kind) {
|
||||
case ActionSet:
|
||||
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.unresolvedImports, response.typings);
|
||||
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings);
|
||||
break;
|
||||
case ActionInvalidate:
|
||||
this.typingsCache.deleteTypingsForProject(response.projectName);
|
||||
@@ -424,8 +430,8 @@ namespace ts.server {
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
else {
|
||||
if (info && (!info.isOpen)) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
if (info && (!info.isScriptOpen())) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.reloadFromFile();
|
||||
this.updateProjectGraphs(info.containingProjects);
|
||||
@@ -440,11 +446,11 @@ namespace ts.server {
|
||||
|
||||
// TODO: handle isOpen = true case
|
||||
|
||||
if (!info.isOpen) {
|
||||
if (!info.isScriptOpen()) {
|
||||
this.filenameToScriptInfo.remove(info.path);
|
||||
this.lastDeletedFile = info;
|
||||
|
||||
// capture list of projects since detachAllProjects will wipe out original list
|
||||
// capture list of projects since detachAllProjects will wipe out original list
|
||||
const containingProjects = info.containingProjects.slice();
|
||||
|
||||
info.detachAllProjects();
|
||||
@@ -486,7 +492,7 @@ namespace ts.server {
|
||||
// If a change was made inside "folder/file", node will trigger the callback twice:
|
||||
// one with the fileName being "folder/file", and the other one with "folder".
|
||||
// We don't respond to the second one.
|
||||
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions())) {
|
||||
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -520,8 +526,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private onConfigChangedForConfiguredProject(project: ConfiguredProject) {
|
||||
this.logger.info(`Config file changed: ${project.getConfigFilePath()}`);
|
||||
this.updateConfiguredProject(project);
|
||||
const configFileName = project.getConfigFilePath();
|
||||
this.logger.info(`Config file changed: ${configFileName}`);
|
||||
const configFileErrors = this.updateConfiguredProject(project);
|
||||
this.reportConfigFileDiagnostics(configFileName, configFileErrors, /*triggerFile*/ configFileName);
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
|
||||
@@ -598,7 +606,7 @@ namespace ts.server {
|
||||
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info);
|
||||
if (!this.useSingleInferredProject) {
|
||||
// if useOneInferredProject is not set then try to fixup ownership of open files
|
||||
// check 'defaultProject !== inferredProject' is necessary to handle cases
|
||||
// check 'defaultProject !== inferredProject' is necessary to handle cases
|
||||
// when creation inferred project for some file has added other open files into this project (i.e. as referenced files)
|
||||
// we definitely don't want to delete the project that was just created
|
||||
for (const f of this.openFiles) {
|
||||
@@ -608,7 +616,7 @@ namespace ts.server {
|
||||
}
|
||||
const defaultProject = f.getDefaultProject();
|
||||
if (isRootFileInInferredProject(info) && defaultProject !== inferredProject && inferredProject.containsScriptInfo(f)) {
|
||||
// open file used to be root in inferred project,
|
||||
// open file used to be root in inferred project,
|
||||
// this inferred project is different from the one we've just created for current file
|
||||
// and new inferred project references this open file.
|
||||
// We should delete old inferred project and attach open file to the new one
|
||||
@@ -632,15 +640,17 @@ namespace ts.server {
|
||||
// Closing file should trigger re-reading the file content from disk. This is
|
||||
// because the user may chose to discard the buffer content before saving
|
||||
// to the disk, and the server's version of the file can be out of sync.
|
||||
info.reloadFromFile();
|
||||
info.close();
|
||||
|
||||
removeItemFromSet(this.openFiles, info);
|
||||
info.isOpen = false;
|
||||
|
||||
// collect all projects that should be removed
|
||||
let projectsToRemove: Project[];
|
||||
for (const p of info.containingProjects) {
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
if (info.hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
// last open file in configured project - close it
|
||||
if ((<ConfiguredProject>p).deleteOpenRef() === 0) {
|
||||
(projectsToRemove || (projectsToRemove = [])).push(p);
|
||||
@@ -650,6 +660,13 @@ namespace ts.server {
|
||||
// open file in inferred project
|
||||
(projectsToRemove || (projectsToRemove = [])).push(p);
|
||||
}
|
||||
|
||||
if (!p.languageServiceEnabled) {
|
||||
// if project language service is disabled then we create a program only for open files.
|
||||
// this means that project should be marked as dirty to force rebuilding of the program
|
||||
// on the next request
|
||||
p.markAsDirty();
|
||||
}
|
||||
}
|
||||
if (projectsToRemove) {
|
||||
for (const project of projectsToRemove) {
|
||||
@@ -775,7 +792,13 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private findConfiguredProjectByProjectName(configFileName: NormalizedPath) {
|
||||
return findProjectByName(configFileName, this.configuredProjects);
|
||||
// make sure that casing of config file name is consistent
|
||||
configFileName = asNormalizedPath(this.toCanonicalFileName(configFileName));
|
||||
for (const proj of this.configuredProjects) {
|
||||
if (proj.canonicalConfigFilePath === configFileName) {
|
||||
return proj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private findExternalProjectByProjectName(projectFileName: string) {
|
||||
@@ -803,7 +826,9 @@ namespace ts.server {
|
||||
this.host,
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
configFilename);
|
||||
configFilename,
|
||||
/*resolutionStack*/ [],
|
||||
this.hostConfiguration.extraFileExtensions);
|
||||
|
||||
if (parsedCommandLine.errors.length) {
|
||||
errors = concatenate(errors, parsedCommandLine.errors);
|
||||
@@ -820,8 +845,8 @@ namespace ts.server {
|
||||
files: parsedCommandLine.fileNames,
|
||||
compilerOptions: parsedCommandLine.options,
|
||||
configHasFilesProperty: config["files"] !== undefined,
|
||||
wildcardDirectories: createMap(parsedCommandLine.wildcardDirectories),
|
||||
typingOptions: parsedCommandLine.typingOptions,
|
||||
wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories),
|
||||
typeAcquisition: parsedCommandLine.typeAcquisition,
|
||||
compileOnSave: parsedCommandLine.compileOnSave
|
||||
};
|
||||
return { success: true, projectOptions, configFileErrors: errors };
|
||||
@@ -845,7 +870,7 @@ namespace ts.server {
|
||||
return false;
|
||||
}
|
||||
|
||||
private createAndAddExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typingOptions: TypingOptions) {
|
||||
private createAndAddExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition) {
|
||||
const compilerOptions = convertCompilerOptions(options);
|
||||
const project = new ExternalProject(
|
||||
projectFileName,
|
||||
@@ -855,7 +880,7 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(compilerOptions, files, externalFilePropertyReader),
|
||||
options.compileOnSave === undefined ? true : options.compileOnSave);
|
||||
|
||||
this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typingOptions, /*configFileErrors*/ undefined);
|
||||
this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition, /*configFileErrors*/ undefined);
|
||||
this.externalProjects.push(project);
|
||||
return project;
|
||||
}
|
||||
@@ -883,7 +908,7 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !sizeLimitExceeded,
|
||||
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
|
||||
|
||||
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typingOptions, configFileErrors);
|
||||
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
|
||||
|
||||
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
|
||||
if (!sizeLimitExceeded) {
|
||||
@@ -902,12 +927,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typingOptions: TypingOptions, configFileErrors: Diagnostic[]): void {
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void {
|
||||
let errors: Diagnostic[];
|
||||
for (const f of files) {
|
||||
const rootFilename = propertyReader.getFileName(f);
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
if (this.host.fileExists(rootFilename)) {
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName == rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
project.addRoot(info);
|
||||
@@ -917,7 +942,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
project.setProjectErrors(concatenate(configFileErrors, errors));
|
||||
project.setTypingOptions(typingOptions);
|
||||
project.setTypeAcquisition(typeAcquisition);
|
||||
project.updateGraph();
|
||||
}
|
||||
|
||||
@@ -934,7 +959,7 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean, configFileErrors: Diagnostic[]) {
|
||||
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypeAcquisition: TypeAcquisition, compileOnSave: boolean, configFileErrors: Diagnostic[]) {
|
||||
const oldRootScriptInfos = project.getRootScriptInfos();
|
||||
const newRootScriptInfos: ScriptInfo[] = [];
|
||||
const newRootScriptInfoMap: NormalizedPathMap<ScriptInfo> = createNormalizedPathMap<ScriptInfo>();
|
||||
@@ -953,7 +978,7 @@ namespace ts.server {
|
||||
rootFilesChanged = true;
|
||||
if (!scriptInfo) {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
}
|
||||
}
|
||||
@@ -981,8 +1006,8 @@ namespace ts.server {
|
||||
}
|
||||
if (toAdd) {
|
||||
for (const f of toAdd) {
|
||||
if (f.isOpen && isRootFileInInferredProject(f)) {
|
||||
// if file is already root in some inferred project
|
||||
if (f.isScriptOpen() && isRootFileInInferredProject(f)) {
|
||||
// if file is already root in some inferred project
|
||||
// - remove the file from that project and delete the project if necessary
|
||||
const inferredProject = f.containingProjects[0];
|
||||
inferredProject.removeFile(f);
|
||||
@@ -996,7 +1021,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
project.setCompilerOptions(newOptions);
|
||||
(<ExternalProject | ConfiguredProject>project).setTypingOptions(newTypingOptions);
|
||||
(<ExternalProject | ConfiguredProject>project).setTypeAcquisition(newTypeAcquisition);
|
||||
|
||||
// VS only set the CompileOnSaveEnabled option in the request if the option was changed recently
|
||||
// therefore if it is undefined, it should not be updated.
|
||||
@@ -1015,6 +1040,9 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
|
||||
// note: the returned "success" is true does not mean the "configFileErrors" is empty.
|
||||
// because we might have tolerated the errors and kept going. So always return the configFileErrors
|
||||
// regardless the "success" here is true or not.
|
||||
const { success, projectOptions, configFileErrors } = this.convertConfigFileContentToProjectOptions(project.getConfigFilePath());
|
||||
if (!success) {
|
||||
// reset project settings to default
|
||||
@@ -1026,18 +1054,17 @@ namespace ts.server {
|
||||
project.setCompilerOptions(projectOptions.compilerOptions);
|
||||
if (!project.languageServiceEnabled) {
|
||||
// language service is already disabled
|
||||
return;
|
||||
return configFileErrors;
|
||||
}
|
||||
project.disableLanguageService();
|
||||
project.stopWatchingDirectory();
|
||||
}
|
||||
else {
|
||||
if (!project.languageServiceEnabled) {
|
||||
project.enableLanguageService();
|
||||
}
|
||||
project.enableLanguageService();
|
||||
this.watchConfigDirectoryForProject(project, projectOptions);
|
||||
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typingOptions, projectOptions.compileOnSave, configFileErrors);
|
||||
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors);
|
||||
}
|
||||
return configFileErrors;
|
||||
}
|
||||
|
||||
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
|
||||
@@ -1077,32 +1104,34 @@ namespace ts.server {
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) {
|
||||
let info = this.getScriptInfoForNormalizedPath(fileName);
|
||||
if (!info) {
|
||||
let content: string;
|
||||
if (this.host.fileExists(fileName)) {
|
||||
// by default pick whatever content was supplied as the argument
|
||||
// if argument was not given - then for mixed content files assume that its content is empty string
|
||||
content = fileContent || (hasMixedContent ? "" : this.host.readFile(fileName));
|
||||
}
|
||||
if (!content) {
|
||||
if (openedByClient) {
|
||||
content = "";
|
||||
}
|
||||
}
|
||||
if (content !== undefined) {
|
||||
info = new ScriptInfo(this.host, fileName, content, scriptKind, openedByClient, hasMixedContent);
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (openedByClient || this.host.fileExists(fileName)) {
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent);
|
||||
|
||||
this.filenameToScriptInfo.set(info.path, info);
|
||||
if (!info.isOpen && !hasMixedContent) {
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
|
||||
if (openedByClient) {
|
||||
if (fileContent === undefined) {
|
||||
// if file is opened by client and its content is not specified - use file text
|
||||
fileContent = this.host.readFile(fileName) || "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (!hasMixedContent) {
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
if (fileContent !== undefined) {
|
||||
info.reload(fileContent);
|
||||
if (openedByClient && !info.isScriptOpen()) {
|
||||
info.open(fileContent);
|
||||
if (hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
}
|
||||
if (openedByClient) {
|
||||
info.isOpen = true;
|
||||
else if (fileContent !== undefined) {
|
||||
info.reload(fileContent);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
@@ -1131,9 +1160,13 @@ namespace ts.server {
|
||||
this.logger.info(`Host information ${args.hostInfo}`);
|
||||
}
|
||||
if (args.formatOptions) {
|
||||
mergeMaps(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
|
||||
mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
|
||||
this.logger.info("Format host information updated");
|
||||
}
|
||||
if (args.extraFileExtensions) {
|
||||
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
|
||||
this.logger.info("Host file extension mappings updated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1199,9 +1232,22 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean): OpenConfiguredProjectResult {
|
||||
const { configFileName = undefined, configFileErrors = undefined }: OpenConfiguredProjectResult = this.findContainingExternalProject(fileName)
|
||||
? {}
|
||||
: this.openOrUpdateConfiguredProjectForFile(fileName);
|
||||
let configFileName: NormalizedPath;
|
||||
let configFileErrors: Diagnostic[];
|
||||
|
||||
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
|
||||
if (!project) {
|
||||
({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName));
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
}
|
||||
}
|
||||
if (project && !project.languageServiceEnabled) {
|
||||
// if project language service is disabled then we create a program only for open files.
|
||||
// this means that project should be marked as dirty to force rebuilding of the program
|
||||
// on the next request
|
||||
project.markAsDirty();
|
||||
}
|
||||
|
||||
// at this point if file is the part of some configured/external project then this project should be created
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
|
||||
@@ -1218,7 +1264,6 @@ namespace ts.server {
|
||||
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName));
|
||||
if (info) {
|
||||
this.closeOpenFile(info);
|
||||
info.isOpen = false;
|
||||
}
|
||||
this.printProjects();
|
||||
}
|
||||
@@ -1230,6 +1275,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
synchronizeProjectList(knownProjects: protocol.ProjectVersionInfo[]): ProjectFilesWithTSDiagnostics[] {
|
||||
const files: ProjectFilesWithTSDiagnostics[] = [];
|
||||
this.collectChanges(knownProjects, this.externalProjects, files);
|
||||
@@ -1238,12 +1284,13 @@ namespace ts.server {
|
||||
return files;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
applyChangesInOpenFiles(openFiles: protocol.ExternalFile[], changedFiles: protocol.ChangedOpenFile[], closedFiles: string[]): void {
|
||||
const recordChangedFiles = changedFiles && !openFiles && !closedFiles;
|
||||
if (openFiles) {
|
||||
for (const file of openFiles) {
|
||||
const scriptInfo = this.getScriptInfo(file.fileName);
|
||||
Debug.assert(!scriptInfo || !scriptInfo.isOpen);
|
||||
Debug.assert(!scriptInfo || !scriptInfo.isScriptOpen());
|
||||
const normalizedPath = scriptInfo ? scriptInfo.fileName : toNormalizedPath(file.fileName);
|
||||
this.openClientFileWithNormalizedPath(normalizedPath, file.content, tryConvertScriptKindName(file.scriptKind), file.hasMixedContent);
|
||||
}
|
||||
@@ -1253,7 +1300,7 @@ namespace ts.server {
|
||||
for (const file of changedFiles) {
|
||||
const scriptInfo = this.getScriptInfo(file.fileName);
|
||||
Debug.assert(!!scriptInfo);
|
||||
// apply changes in reverse order
|
||||
// apply changes in reverse order
|
||||
for (let i = file.changes.length - 1; i >= 0; i--) {
|
||||
const change = file.changes[i];
|
||||
scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText);
|
||||
@@ -1290,7 +1337,7 @@ namespace ts.server {
|
||||
|
||||
closeExternalProject(uncheckedFileName: string, suppressRefresh = false): void {
|
||||
const fileName = toNormalizedPath(uncheckedFileName);
|
||||
const configFiles = this.externalProjectToConfiguredProjectMap[fileName];
|
||||
const configFiles = this.externalProjectToConfiguredProjectMap.get(fileName);
|
||||
if (configFiles) {
|
||||
let shouldRefreshInferredProjects = false;
|
||||
for (const configFile of configFiles) {
|
||||
@@ -1298,7 +1345,7 @@ namespace ts.server {
|
||||
shouldRefreshInferredProjects = true;
|
||||
}
|
||||
}
|
||||
delete this.externalProjectToConfiguredProjectMap[fileName];
|
||||
this.externalProjectToConfiguredProjectMap.delete(fileName);
|
||||
if (shouldRefreshInferredProjects && !suppressRefresh) {
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
@@ -1315,7 +1362,34 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
openExternalProject(proj: protocol.ExternalProject): void {
|
||||
openExternalProjects(projects: protocol.ExternalProject[]): void {
|
||||
// record project list before the update
|
||||
const projectsToClose = arrayToMap(this.externalProjects, p => p.getProjectName(), _ => true);
|
||||
forEachKey(this.externalProjectToConfiguredProjectMap, externalProjectName => {
|
||||
projectsToClose.set(externalProjectName, true);
|
||||
});
|
||||
|
||||
for (const externalProject of projects) {
|
||||
this.openExternalProject(externalProject, /*suppressRefreshOfInferredProjects*/ true);
|
||||
// delete project that is present in input list
|
||||
projectsToClose.delete(externalProject.projectFileName);
|
||||
}
|
||||
|
||||
// close projects that were missing in the input list
|
||||
forEachKey(projectsToClose, externalProjectName => {
|
||||
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true)
|
||||
});
|
||||
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
|
||||
openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void {
|
||||
// typingOptions has been deprecated and is only supported for backward compatibility
|
||||
// purposes. It should be removed in future releases - use typeAcquisition instead.
|
||||
if (proj.typingOptions && !proj.typeAcquisition) {
|
||||
const typeAcquisition = convertEnableAutoDiscoveryToEnable(proj.typingOptions);
|
||||
proj.typeAcquisition = typeAcquisition;
|
||||
}
|
||||
let tsConfigFiles: NormalizedPath[];
|
||||
const rootFiles: protocol.ExternalFile[] = [];
|
||||
for (const file of proj.rootFiles) {
|
||||
@@ -1339,15 +1413,22 @@ namespace ts.server {
|
||||
let exisingConfigFiles: string[];
|
||||
if (externalProject) {
|
||||
if (!tsConfigFiles) {
|
||||
const compilerOptions = convertCompilerOptions(proj.options);
|
||||
if (this.exceededTotalSizeLimitForNonTsFiles(compilerOptions, proj.rootFiles, externalFilePropertyReader)) {
|
||||
externalProject.disableLanguageService();
|
||||
}
|
||||
else {
|
||||
externalProject.enableLanguageService();
|
||||
}
|
||||
// external project already exists and not config files were added - update the project and return;
|
||||
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, convertCompilerOptions(proj.options), proj.typingOptions, proj.options.compileOnSave, /*configFileErrors*/ undefined);
|
||||
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave, /*configFileErrors*/ undefined);
|
||||
return;
|
||||
}
|
||||
// some config files were added to external project (that previously were not there)
|
||||
// close existing project and later we'll open a set of configured projects for these files
|
||||
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
|
||||
}
|
||||
else if (this.externalProjectToConfiguredProjectMap[proj.projectFileName]) {
|
||||
else if (this.externalProjectToConfiguredProjectMap.get(proj.projectFileName)) {
|
||||
// this project used to include config files
|
||||
if (!tsConfigFiles) {
|
||||
// config files were removed from the project - close existing external project which in turn will close configured projects
|
||||
@@ -1355,7 +1436,7 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
// project previously had some config files - compare them with new set of files and close all configured projects that correspond to unused files
|
||||
const oldConfigFiles = this.externalProjectToConfiguredProjectMap[proj.projectFileName];
|
||||
const oldConfigFiles = this.externalProjectToConfiguredProjectMap.get(proj.projectFileName);
|
||||
let iNew = 0;
|
||||
let iOld = 0;
|
||||
while (iNew < tsConfigFiles.length && iOld < oldConfigFiles.length) {
|
||||
@@ -1383,7 +1464,7 @@ namespace ts.server {
|
||||
}
|
||||
if (tsConfigFiles) {
|
||||
// store the list of tsconfig files that belong to the external project
|
||||
this.externalProjectToConfiguredProjectMap[proj.projectFileName] = tsConfigFiles;
|
||||
this.externalProjectToConfiguredProjectMap.set(proj.projectFileName, tsConfigFiles);
|
||||
for (const tsconfigFile of tsConfigFiles) {
|
||||
let project = this.findConfiguredProjectByProjectName(tsconfigFile);
|
||||
if (!project) {
|
||||
@@ -1399,10 +1480,12 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
// no config files - remove the item from the collection
|
||||
delete this.externalProjectToConfiguredProjectMap[proj.projectFileName];
|
||||
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typingOptions);
|
||||
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
|
||||
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
|
||||
}
|
||||
if (!suppressRefreshOfInferredProjects) {
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -23,7 +23,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.resolveModuleName = (moduleName, containingFile, compilerOptions, host) => {
|
||||
const globalCache = this.project.getTypingOptions().enableAutoDiscovery
|
||||
const globalCache = this.project.getTypeAcquisition().enable
|
||||
? this.project.projectService.typingsInstaller.globalTypingsCacheLocation
|
||||
: undefined;
|
||||
const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host);
|
||||
@@ -75,15 +75,16 @@ namespace ts.server {
|
||||
|
||||
for (const name of names) {
|
||||
// check if this is a duplicate entry in the list
|
||||
let resolution = newResolutions[name];
|
||||
let resolution = newResolutions.get(name);
|
||||
if (!resolution) {
|
||||
const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name];
|
||||
const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name);
|
||||
if (moduleResolutionIsValid(existingResolution)) {
|
||||
// ok, it is safe to use existing name resolution results
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this);
|
||||
resolution = loader(name, containingFile, compilerOptions, this);
|
||||
newResolutions.set(name, resolution);
|
||||
}
|
||||
if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
|
||||
this.filesWithChangedSetOfUnresolvedImports.push(path);
|
||||
@@ -135,6 +136,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return this.host.newLine;
|
||||
}
|
||||
|
||||
getProjectVersion() {
|
||||
return this.project.getProjectVersion();
|
||||
}
|
||||
@@ -169,7 +174,7 @@ namespace ts.server {
|
||||
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
|
||||
const scriptInfo = this.project.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.snap();
|
||||
return scriptInfo.getSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+148
-160
@@ -58,6 +58,7 @@ namespace ts.server {
|
||||
return counts.ts === 0 && counts.tsx === 0;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
|
||||
projectErrors: Diagnostic[];
|
||||
}
|
||||
@@ -90,87 +91,6 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
const emptyResult: any[] = [];
|
||||
const getEmptyResult = () => emptyResult;
|
||||
const getUndefined = () => <any>undefined;
|
||||
const emptyEncodedSemanticClassifications = { spans: emptyResult, endOfLineState: EndOfLineState.None };
|
||||
|
||||
export function createNoSemanticFeaturesWrapper(realLanguageService: LanguageService): LanguageService {
|
||||
return {
|
||||
cleanupSemanticCache: noop,
|
||||
getSyntacticDiagnostics: (fileName) =>
|
||||
fileName ? realLanguageService.getSyntacticDiagnostics(fileName) : emptyResult,
|
||||
getSemanticDiagnostics: getEmptyResult,
|
||||
getCompilerOptionsDiagnostics: () =>
|
||||
realLanguageService.getCompilerOptionsDiagnostics(),
|
||||
getSyntacticClassifications: (fileName, span) =>
|
||||
realLanguageService.getSyntacticClassifications(fileName, span),
|
||||
getEncodedSyntacticClassifications: (fileName, span) =>
|
||||
realLanguageService.getEncodedSyntacticClassifications(fileName, span),
|
||||
getSemanticClassifications: getEmptyResult,
|
||||
getEncodedSemanticClassifications: () =>
|
||||
emptyEncodedSemanticClassifications,
|
||||
getCompletionsAtPosition: getUndefined,
|
||||
findReferences: getEmptyResult,
|
||||
getCompletionEntryDetails: getUndefined,
|
||||
getQuickInfoAtPosition: getUndefined,
|
||||
findRenameLocations: getEmptyResult,
|
||||
getNameOrDottedNameSpan: (fileName, startPos, endPos) =>
|
||||
realLanguageService.getNameOrDottedNameSpan(fileName, startPos, endPos),
|
||||
getBreakpointStatementAtPosition: (fileName, position) =>
|
||||
realLanguageService.getBreakpointStatementAtPosition(fileName, position),
|
||||
getBraceMatchingAtPosition: (fileName, position) =>
|
||||
realLanguageService.getBraceMatchingAtPosition(fileName, position),
|
||||
getSignatureHelpItems: getUndefined,
|
||||
getDefinitionAtPosition: getEmptyResult,
|
||||
getRenameInfo: () => ({
|
||||
canRename: false,
|
||||
localizedErrorMessage: getLocaleSpecificMessage(Diagnostics.Language_service_is_disabled),
|
||||
displayName: undefined,
|
||||
fullDisplayName: undefined,
|
||||
kind: undefined,
|
||||
kindModifiers: undefined,
|
||||
triggerSpan: undefined
|
||||
}),
|
||||
getTypeDefinitionAtPosition: getUndefined,
|
||||
getReferencesAtPosition: getEmptyResult,
|
||||
getDocumentHighlights: getEmptyResult,
|
||||
getOccurrencesAtPosition: getEmptyResult,
|
||||
getNavigateToItems: getEmptyResult,
|
||||
getNavigationBarItems: fileName =>
|
||||
realLanguageService.getNavigationBarItems(fileName),
|
||||
getNavigationTree: fileName =>
|
||||
realLanguageService.getNavigationTree(fileName),
|
||||
getOutliningSpans: fileName =>
|
||||
realLanguageService.getOutliningSpans(fileName),
|
||||
getTodoComments: getEmptyResult,
|
||||
getIndentationAtPosition: (fileName, position, options) =>
|
||||
realLanguageService.getIndentationAtPosition(fileName, position, options),
|
||||
getFormattingEditsForRange: (fileName, start, end, options) =>
|
||||
realLanguageService.getFormattingEditsForRange(fileName, start, end, options),
|
||||
getFormattingEditsForDocument: (fileName, options) =>
|
||||
realLanguageService.getFormattingEditsForDocument(fileName, options),
|
||||
getFormattingEditsAfterKeystroke: (fileName, position, key, options) =>
|
||||
realLanguageService.getFormattingEditsAfterKeystroke(fileName, position, key, options),
|
||||
getDocCommentTemplateAtPosition: (fileName, position) =>
|
||||
realLanguageService.getDocCommentTemplateAtPosition(fileName, position),
|
||||
isValidBraceCompletionAtPosition: (fileName, position, openingBrace) =>
|
||||
realLanguageService.isValidBraceCompletionAtPosition(fileName, position, openingBrace),
|
||||
getEmitOutput: getUndefined,
|
||||
getProgram: () =>
|
||||
realLanguageService.getProgram(),
|
||||
getNonBoundSourceFile: fileName =>
|
||||
realLanguageService.getNonBoundSourceFile(fileName),
|
||||
dispose: () =>
|
||||
realLanguageService.dispose(),
|
||||
getCompletionEntrySymbol: getUndefined,
|
||||
getImplementationAtPosition: getEmptyResult,
|
||||
getSourceFile: fileName =>
|
||||
realLanguageService.getSourceFile(fileName),
|
||||
getCodeFixesAtPosition: getEmptyResult
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class Project {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
|
||||
@@ -181,12 +101,14 @@ namespace ts.server {
|
||||
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
|
||||
|
||||
private readonly languageService: LanguageService;
|
||||
// wrapper over the real language service that will suppress all semantic operations
|
||||
private readonly noSemanticFeaturesLanguageService: LanguageService;
|
||||
|
||||
public languageServiceEnabled = true;
|
||||
|
||||
builder: Builder;
|
||||
/**
|
||||
* Set of files names that were updated since the last call to getChangesSinceVersion.
|
||||
*/
|
||||
private updatedFileNames: Map<string>;
|
||||
/**
|
||||
* Set of files that was returned from the last call to getChangesSinceVersion.
|
||||
*/
|
||||
@@ -248,15 +170,12 @@ namespace ts.server {
|
||||
this.compilerOptions.allowNonTsExtensions = true;
|
||||
}
|
||||
|
||||
if (this.projectKind === ProjectKind.Inferred || this.projectKind === ProjectKind.External) {
|
||||
this.compilerOptions.noEmitForJsFiles = true;
|
||||
}
|
||||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
|
||||
this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
|
||||
this.lsHost.setCompilationSettings(this.compilerOptions);
|
||||
|
||||
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
|
||||
this.noSemanticFeaturesLanguageService = createNoSemanticFeaturesWrapper(this.languageService);
|
||||
|
||||
if (!languageServiceEnabled) {
|
||||
this.disableLanguageService();
|
||||
@@ -266,6 +185,12 @@ namespace ts.server {
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
private setInternalCompilerOptionsForEmittingJsFiles() {
|
||||
if (this.projectKind === ProjectKind.Inferred || this.projectKind === ProjectKind.External) {
|
||||
this.compilerOptions.noEmitForJsFiles = true;
|
||||
}
|
||||
}
|
||||
|
||||
getProjectErrors() {
|
||||
return this.projectErrors;
|
||||
}
|
||||
@@ -274,9 +199,7 @@ namespace ts.server {
|
||||
if (ensureSynchronized) {
|
||||
this.updateGraph();
|
||||
}
|
||||
return this.languageServiceEnabled
|
||||
? this.languageService
|
||||
: this.noSemanticFeaturesLanguageService;
|
||||
return this.languageService;
|
||||
}
|
||||
|
||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
|
||||
@@ -312,7 +235,7 @@ namespace ts.server {
|
||||
return this.projectName;
|
||||
}
|
||||
abstract getProjectRootPath(): string | undefined;
|
||||
abstract getTypingOptions(): TypingOptions;
|
||||
abstract getTypeAcquisition(): TypeAcquisition;
|
||||
|
||||
getSourceFile(path: Path) {
|
||||
if (!this.program) {
|
||||
@@ -335,8 +258,9 @@ namespace ts.server {
|
||||
info.detachFromProject(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// release all root files
|
||||
if (!this.program || !this.languageServiceEnabled) {
|
||||
// release all root files either if there is no program or language service is disabled.
|
||||
// in the latter case set of root files can be larger than the set of files in program.
|
||||
for (const root of this.rootFiles) {
|
||||
root.detachFromProject(this);
|
||||
}
|
||||
@@ -365,7 +289,10 @@ namespace ts.server {
|
||||
const result: string[] = [];
|
||||
if (this.rootFiles) {
|
||||
for (const f of this.rootFiles) {
|
||||
result.push(f.fileName);
|
||||
if (this.languageServiceEnabled || f.isScriptOpen()) {
|
||||
// if language service is disabled - process only files that are open
|
||||
result.push(f.fileName);
|
||||
}
|
||||
}
|
||||
if (this.typingFiles) {
|
||||
for (const f of this.typingFiles) {
|
||||
@@ -381,6 +308,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getScriptInfos() {
|
||||
if (!this.languageServiceEnabled) {
|
||||
// if language service is not enabled - return just root files
|
||||
return this.rootFiles;
|
||||
}
|
||||
return map(this.program.getSourceFiles(), sourceFile => {
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
|
||||
if (!scriptInfo) {
|
||||
@@ -444,7 +375,7 @@ namespace ts.server {
|
||||
|
||||
containsFile(filename: NormalizedPath, requireOpen?: boolean) {
|
||||
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
|
||||
if (info && (info.isOpen || !requireOpen)) {
|
||||
if (info && (info.isScriptOpen() || !requireOpen)) {
|
||||
return this.containsScriptInfo(info);
|
||||
}
|
||||
}
|
||||
@@ -465,7 +396,9 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
removeFile(info: ScriptInfo, detachFromProject = true) {
|
||||
this.removeRootFileIfNecessary(info);
|
||||
if (this.isRoot(info)) {
|
||||
this.removeRoot(info);
|
||||
}
|
||||
this.lsHost.notifyFileRemoved(info);
|
||||
this.cachedUnresolvedImportsPerFile.remove(info.path);
|
||||
|
||||
@@ -476,6 +409,10 @@ namespace ts.server {
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
registerFileUpdate(fileName: string) {
|
||||
(this.updatedFileNames || (this.updatedFileNames = createMap<string>())).set(fileName, fileName);
|
||||
}
|
||||
|
||||
markAsDirty() {
|
||||
this.projectStateVersion++;
|
||||
}
|
||||
@@ -491,9 +428,9 @@ namespace ts.server {
|
||||
}
|
||||
let unresolvedImports: string[];
|
||||
if (file.resolvedModules) {
|
||||
for (const name in file.resolvedModules) {
|
||||
file.resolvedModules.forEach((resolvedModule, name) => {
|
||||
// pick unresolved non-relative names
|
||||
if (!file.resolvedModules[name] && !isExternalModuleNameRelative(name)) {
|
||||
if (!resolvedModule && !isExternalModuleNameRelative(name)) {
|
||||
// for non-scoped names extract part up-to the first slash
|
||||
// for scoped names - extract up to the second slash
|
||||
let trimmed = name.trim();
|
||||
@@ -507,7 +444,7 @@ namespace ts.server {
|
||||
(unresolvedImports || (unresolvedImports = [])).push(trimmed);
|
||||
result.push(trimmed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray);
|
||||
}
|
||||
@@ -517,10 +454,6 @@ namespace ts.server {
|
||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
*/
|
||||
updateGraph(): boolean {
|
||||
if (!this.languageServiceEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.lsHost.startRecordingFilesWithChangedResolutions();
|
||||
|
||||
let hasChanges = this.updateGraphWorker();
|
||||
@@ -533,7 +466,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
// 1. no changes in structure, no changes in unresolved imports - do nothing
|
||||
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
|
||||
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
@@ -552,6 +485,16 @@ namespace ts.server {
|
||||
if (this.setTypings(cachedTypings)) {
|
||||
hasChanges = this.updateGraphWorker() || hasChanges;
|
||||
}
|
||||
|
||||
// update builder only if language service is enabled
|
||||
// otherwise tell it to drop its internal state
|
||||
if (this.languageServiceEnabled) {
|
||||
this.builder.onProjectUpdateGraph();
|
||||
}
|
||||
else {
|
||||
this.builder.clear();
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
this.projectStructureVersion++;
|
||||
}
|
||||
@@ -590,7 +533,6 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.builder.onProjectUpdateGraph();
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
@@ -627,9 +569,6 @@ namespace ts.server {
|
||||
|
||||
setCompilerOptions(compilerOptions: CompilerOptions) {
|
||||
if (compilerOptions) {
|
||||
if (this.projectKind === ProjectKind.Inferred) {
|
||||
compilerOptions.allowJs = true;
|
||||
}
|
||||
compilerOptions.allowNonTsExtensions = true;
|
||||
if (changesAffectModuleResolution(this.compilerOptions, compilerOptions)) {
|
||||
// reset cached unresolved imports if changes in compiler options affected module resolution
|
||||
@@ -637,6 +576,7 @@ namespace ts.server {
|
||||
this.lastCachedUnresolvedImportsList = undefined;
|
||||
}
|
||||
this.compilerOptions = compilerOptions;
|
||||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
this.lsHost.setCompilationSettings(compilerOptions);
|
||||
|
||||
this.markAsDirty();
|
||||
@@ -653,6 +593,7 @@ namespace ts.server {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
|
||||
this.updateGraph();
|
||||
|
||||
@@ -660,12 +601,15 @@ namespace ts.server {
|
||||
projectName: this.getProjectName(),
|
||||
version: this.projectStructureVersion,
|
||||
isInferred: this.projectKind === ProjectKind.Inferred,
|
||||
options: this.getCompilerOptions()
|
||||
options: this.getCompilerOptions(),
|
||||
languageServiceDisabled: !this.languageServiceEnabled
|
||||
};
|
||||
const updatedFileNames = this.updatedFileNames;
|
||||
this.updatedFileNames = undefined;
|
||||
// check if requested version is the same that we have reported last time
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info witout any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info without any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion && !updatedFileNames) {
|
||||
return { info, projectErrors: this.projectErrors };
|
||||
}
|
||||
// compute and return the difference
|
||||
@@ -674,19 +618,21 @@ namespace ts.server {
|
||||
|
||||
const added: string[] = [];
|
||||
const removed: string[] = [];
|
||||
for (const id in currentFiles) {
|
||||
if (!hasProperty(lastReportedFileNames, id)) {
|
||||
const updated: string[] = arrayFrom(updatedFileNames.keys());
|
||||
|
||||
forEachKey(currentFiles, id => {
|
||||
if (!lastReportedFileNames.has(id)) {
|
||||
added.push(id);
|
||||
}
|
||||
}
|
||||
for (const id in lastReportedFileNames) {
|
||||
if (!hasProperty(currentFiles, id)) {
|
||||
});
|
||||
forEachKey(lastReportedFileNames, id => {
|
||||
if (!currentFiles.has(id)) {
|
||||
removed.push(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, changes: { added, removed }, projectErrors: this.projectErrors };
|
||||
return { info, changes: { added, removed, updated }, projectErrors: this.projectErrors };
|
||||
}
|
||||
else {
|
||||
// unknown version - return everything
|
||||
@@ -717,7 +663,7 @@ namespace ts.server {
|
||||
if (symbol && symbol.declarations && symbol.declarations[0]) {
|
||||
const declarationSourceFile = symbol.declarations[0].getSourceFile();
|
||||
if (declarationSourceFile) {
|
||||
referencedFiles[declarationSourceFile.path] = true;
|
||||
referencedFiles.set(declarationSourceFile.path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -729,34 +675,31 @@ namespace ts.server {
|
||||
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
|
||||
for (const referencedFile of sourceFile.referencedFiles) {
|
||||
const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName);
|
||||
referencedFiles[referencedPath] = true;
|
||||
referencedFiles.set(referencedPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle type reference directives
|
||||
if (sourceFile.resolvedTypeReferenceDirectiveNames) {
|
||||
for (const typeName in sourceFile.resolvedTypeReferenceDirectiveNames) {
|
||||
const resolvedTypeReferenceDirective = sourceFile.resolvedTypeReferenceDirectiveNames[typeName];
|
||||
sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => {
|
||||
if (!resolvedTypeReferenceDirective) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = resolvedTypeReferenceDirective.resolvedFileName;
|
||||
const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
referencedFiles[typeFilePath] = true;
|
||||
}
|
||||
referencedFiles.set(typeFilePath, true);
|
||||
})
|
||||
}
|
||||
|
||||
const allFileNames = map(Object.keys(referencedFiles), key => <Path>key);
|
||||
const allFileNames = arrayFrom(referencedFiles.keys()) as Path[];
|
||||
return filter(allFileNames, file => this.projectService.host.fileExists(file));
|
||||
}
|
||||
|
||||
// remove a root file from project
|
||||
private removeRootFileIfNecessary(info: ScriptInfo): void {
|
||||
if (this.isRoot(info)) {
|
||||
remove(this.rootFiles, info);
|
||||
this.rootFilesMap.remove(info.path);
|
||||
}
|
||||
protected removeRoot(info: ScriptInfo): void {
|
||||
remove(this.rootFiles, info);
|
||||
this.rootFilesMap.remove(info.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,6 +714,32 @@ namespace ts.server {
|
||||
}
|
||||
})();
|
||||
|
||||
private _isJsInferredProject = false;
|
||||
|
||||
toggleJsInferredProject(isJsInferredProject: boolean) {
|
||||
if (isJsInferredProject !== this._isJsInferredProject) {
|
||||
this._isJsInferredProject = isJsInferredProject;
|
||||
this.setCompilerOptions();
|
||||
}
|
||||
}
|
||||
|
||||
setCompilerOptions(options?: CompilerOptions) {
|
||||
// Avoid manipulating the given options directly
|
||||
const newOptions = options ? clone(options) : this.getCompilerOptions();
|
||||
if (!newOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isJsInferredProject && typeof newOptions.maxNodeModuleJsDepth !== "number") {
|
||||
newOptions.maxNodeModuleJsDepth = 2;
|
||||
}
|
||||
else if (!this._isJsInferredProject) {
|
||||
newOptions.maxNodeModuleJsDepth = undefined;
|
||||
}
|
||||
newOptions.allowJs = true;
|
||||
super.setCompilerOptions(newOptions);
|
||||
}
|
||||
|
||||
// Used to keep track of what directories are watched for this project
|
||||
directoriesWatchedForTsconfig: string[] = [];
|
||||
|
||||
@@ -785,6 +754,22 @@ namespace ts.server {
|
||||
/*compileOnSaveEnabled*/ false);
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
if (!this._isJsInferredProject && info.isJavaScript()) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ true);
|
||||
}
|
||||
super.addRoot(info);
|
||||
}
|
||||
|
||||
removeRoot(info: ScriptInfo) {
|
||||
if (this._isJsInferredProject && info.isJavaScript()) {
|
||||
if (filter(this.getRootScriptInfos(), info => info.isJavaScript()).length === 0) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ false);
|
||||
}
|
||||
}
|
||||
super.removeRoot(info);
|
||||
}
|
||||
|
||||
getProjectRootPath() {
|
||||
// Single inferred project does not have a project root.
|
||||
if (this.projectService.useSingleInferredProject) {
|
||||
@@ -802,9 +787,9 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getTypingOptions(): TypingOptions {
|
||||
getTypeAcquisition(): TypeAcquisition {
|
||||
return {
|
||||
enableAutoDiscovery: allRootFilesAreJsOrDts(this),
|
||||
enable: allRootFilesAreJsOrDts(this),
|
||||
include: [],
|
||||
exclude: []
|
||||
};
|
||||
@@ -812,11 +797,12 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export class ConfiguredProject extends Project {
|
||||
private typingOptions: TypingOptions;
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
private projectFileWatcher: FileWatcher;
|
||||
private directoryWatcher: FileWatcher;
|
||||
private directoriesWatchedForWildcards: Map<FileWatcher>;
|
||||
private typeRootsWatchers: FileWatcher[];
|
||||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
|
||||
/** Used for configured projects which may have multiple open roots */
|
||||
openRefCount = 0;
|
||||
@@ -830,6 +816,7 @@ namespace ts.server {
|
||||
languageServiceEnabled: boolean,
|
||||
public compileOnSaveEnabled: boolean) {
|
||||
super(configFileName, ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
|
||||
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
|
||||
}
|
||||
|
||||
getConfigFilePath() {
|
||||
@@ -844,12 +831,12 @@ namespace ts.server {
|
||||
this.projectErrors = projectErrors;
|
||||
}
|
||||
|
||||
setTypingOptions(newTypingOptions: TypingOptions): void {
|
||||
this.typingOptions = newTypingOptions;
|
||||
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void {
|
||||
this.typeAcquisition = newTypeAcquisition;
|
||||
}
|
||||
|
||||
getTypingOptions() {
|
||||
return this.typingOptions;
|
||||
getTypeAcquisition() {
|
||||
return this.typeAcquisition;
|
||||
}
|
||||
|
||||
watchConfigFile(callback: (project: ConfiguredProject) => void) {
|
||||
@@ -881,18 +868,19 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
const configDirectoryPath = getDirectoryPath(this.getConfigFilePath());
|
||||
this.directoriesWatchedForWildcards = reduceProperties(this.wildcardDirectories, (watchers, flag, directory) => {
|
||||
|
||||
this.directoriesWatchedForWildcards = createMap<FileWatcher>();
|
||||
this.wildcardDirectories.forEach((flag, directory) => {
|
||||
if (comparePaths(configDirectoryPath, directory, ".", !this.projectService.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) {
|
||||
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
|
||||
this.projectService.logger.info(`Add ${recursive ? "recursive " : ""}watcher for: ${directory}`);
|
||||
watchers[directory] = this.projectService.host.watchDirectory(
|
||||
this.directoriesWatchedForWildcards.set(directory, this.projectService.host.watchDirectory(
|
||||
directory,
|
||||
path => callback(this, path),
|
||||
recursive
|
||||
);
|
||||
));
|
||||
}
|
||||
return watchers;
|
||||
}, <Map<FileWatcher>>{});
|
||||
});
|
||||
}
|
||||
|
||||
stopWatchingDirectory() {
|
||||
@@ -916,9 +904,9 @@ namespace ts.server {
|
||||
this.typeRootsWatchers = undefined;
|
||||
}
|
||||
|
||||
for (const id in this.directoriesWatchedForWildcards) {
|
||||
this.directoriesWatchedForWildcards[id].close();
|
||||
}
|
||||
this.directoriesWatchedForWildcards.forEach(watcher => {
|
||||
watcher.close();
|
||||
});
|
||||
this.directoriesWatchedForWildcards = undefined;
|
||||
|
||||
this.stopWatchingDirectory();
|
||||
@@ -939,7 +927,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export class ExternalProject extends Project {
|
||||
private typingOptions: TypingOptions;
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
constructor(externalProjectName: string,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: ts.DocumentRegistry,
|
||||
@@ -960,36 +948,36 @@ namespace ts.server {
|
||||
return getDirectoryPath(normalizeSlashes(this.getProjectName()));
|
||||
}
|
||||
|
||||
getTypingOptions() {
|
||||
return this.typingOptions;
|
||||
getTypeAcquisition() {
|
||||
return this.typeAcquisition;
|
||||
}
|
||||
|
||||
setProjectErrors(projectErrors: Diagnostic[]) {
|
||||
this.projectErrors = projectErrors;
|
||||
}
|
||||
|
||||
setTypingOptions(newTypingOptions: TypingOptions): void {
|
||||
if (!newTypingOptions) {
|
||||
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void {
|
||||
if (!newTypeAcquisition) {
|
||||
// set default typings options
|
||||
newTypingOptions = {
|
||||
enableAutoDiscovery: allRootFilesAreJsOrDts(this),
|
||||
newTypeAcquisition = {
|
||||
enable: allRootFilesAreJsOrDts(this),
|
||||
include: [],
|
||||
exclude: []
|
||||
};
|
||||
}
|
||||
else {
|
||||
if (newTypingOptions.enableAutoDiscovery === undefined) {
|
||||
if (newTypeAcquisition.enable === undefined) {
|
||||
// if autoDiscovery was not specified by the caller - set it based on the content of the project
|
||||
newTypingOptions.enableAutoDiscovery = allRootFilesAreJsOrDts(this);
|
||||
newTypeAcquisition.enable = allRootFilesAreJsOrDts(this);
|
||||
}
|
||||
if (!newTypingOptions.include) {
|
||||
newTypingOptions.include = [];
|
||||
if (!newTypeAcquisition.include) {
|
||||
newTypeAcquisition.include = [];
|
||||
}
|
||||
if (!newTypingOptions.exclude) {
|
||||
newTypingOptions.exclude = [];
|
||||
if (!newTypeAcquisition.exclude) {
|
||||
newTypeAcquisition.exclude = [];
|
||||
}
|
||||
}
|
||||
this.typingOptions = newTypingOptions;
|
||||
this.typeAcquisition = newTypeAcquisition;
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
-7
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Declaration module describing the TypeScript Server protocol
|
||||
*/
|
||||
namespace ts.server.protocol {
|
||||
@@ -417,7 +417,7 @@ namespace ts.server.protocol {
|
||||
startOffset: number;
|
||||
|
||||
/**
|
||||
* Position (can be specified instead of line/offset pair)
|
||||
* Position (can be specified instead of line/offset pair)
|
||||
*/
|
||||
/* @internal */
|
||||
startPosition?: number;
|
||||
@@ -433,7 +433,7 @@ namespace ts.server.protocol {
|
||||
endOffset: number;
|
||||
|
||||
/**
|
||||
* Position (can be specified instead of line/offset pair)
|
||||
* Position (can be specified instead of line/offset pair)
|
||||
*/
|
||||
/* @internal */
|
||||
endPosition?: number;
|
||||
@@ -445,7 +445,7 @@ namespace ts.server.protocol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for GetCodeFixes request.
|
||||
* Response for GetCodeFixes request.
|
||||
*/
|
||||
export interface GetCodeFixesResponse extends Response {
|
||||
body?: CodeAction[];
|
||||
@@ -861,9 +861,13 @@ namespace ts.server.protocol {
|
||||
*/
|
||||
options: ExternalProjectCompilerOptions;
|
||||
/**
|
||||
* Explicitly specified typing options for the project
|
||||
* @deprecated typingOptions. Use typeAcquisition instead
|
||||
*/
|
||||
typingOptions?: TypingOptions;
|
||||
typingOptions?: TypeAcquisition;
|
||||
/**
|
||||
* Explicitly specified type acquisition for the project
|
||||
*/
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
}
|
||||
|
||||
export interface CompileOnSaveMixin {
|
||||
@@ -900,6 +904,11 @@ namespace ts.server.protocol {
|
||||
* Current set of compiler options for project
|
||||
*/
|
||||
options: ts.CompilerOptions;
|
||||
|
||||
/**
|
||||
* true if project language service is disabled
|
||||
*/
|
||||
languageServiceDisabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -914,6 +923,10 @@ namespace ts.server.protocol {
|
||||
* List of removed files
|
||||
*/
|
||||
removed: string[];
|
||||
/**
|
||||
* List of updated files
|
||||
*/
|
||||
updated: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -986,6 +999,11 @@ namespace ts.server.protocol {
|
||||
* The format options to use during formatting and other code editing features.
|
||||
*/
|
||||
formatOptions?: FormatCodeSettings;
|
||||
|
||||
/**
|
||||
* The host's additional supported file extensions
|
||||
*/
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2113,6 +2131,40 @@ namespace ts.server.protocol {
|
||||
typingsInstallerVersion: string;
|
||||
}
|
||||
|
||||
export type BeginInstallTypesEventName = "beginInstallTypes";
|
||||
export type EndInstallTypesEventName = "endInstallTypes";
|
||||
|
||||
export interface BeginInstallTypesEvent extends Event {
|
||||
event: BeginInstallTypesEventName;
|
||||
body: BeginInstallTypesEventBody;
|
||||
}
|
||||
|
||||
export interface EndInstallTypesEvent extends Event {
|
||||
event: EndInstallTypesEventName;
|
||||
body: EndInstallTypesEventBody;
|
||||
}
|
||||
|
||||
export interface InstallTypesEventBody {
|
||||
/**
|
||||
* correlation id to match begin and end events
|
||||
*/
|
||||
eventId: number;
|
||||
/**
|
||||
* list of packages to install
|
||||
*/
|
||||
packages: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export interface BeginInstallTypesEventBody extends InstallTypesEventBody {
|
||||
}
|
||||
|
||||
export interface EndInstallTypesEventBody extends InstallTypesEventBody {
|
||||
/**
|
||||
* true if installation succeeded, otherwise false
|
||||
*/
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface NavBarResponse extends Response {
|
||||
body?: NavigationBarItem[];
|
||||
}
|
||||
@@ -2142,12 +2194,14 @@ namespace ts.server.protocol {
|
||||
insertSpaceAfterCommaDelimiter?: boolean;
|
||||
insertSpaceAfterSemicolonInForStatements?: boolean;
|
||||
insertSpaceBeforeAndAfterBinaryOperators?: boolean;
|
||||
insertSpaceAfterConstructor?: boolean;
|
||||
insertSpaceAfterKeywordsInControlFlowStatements?: boolean;
|
||||
insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean;
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
|
||||
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
|
||||
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
|
||||
insertSpaceBeforeFunctionParenthesis?: boolean;
|
||||
placeOpenBraceOnNewLineForFunctions?: boolean;
|
||||
placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
}
|
||||
@@ -2218,10 +2272,11 @@ namespace ts.server.protocol {
|
||||
export namespace JsxEmit {
|
||||
export type None = "None";
|
||||
export type Preserve = "Preserve";
|
||||
export type ReactNative = "ReactNative";
|
||||
export type React = "React";
|
||||
}
|
||||
|
||||
export type JsxEmit = JsxEmit.None | JsxEmit.Preserve | JsxEmit.React;
|
||||
export type JsxEmit = JsxEmit.None | JsxEmit.Preserve | JsxEmit.React | JsxEmit.ReactNative;
|
||||
|
||||
export namespace ModuleKind {
|
||||
export type None = "None";
|
||||
|
||||
+202
-36
@@ -2,6 +2,161 @@
|
||||
|
||||
namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
export class TextStorage {
|
||||
private svc: ScriptVersionCache | undefined;
|
||||
private svcVersion = 0;
|
||||
|
||||
private text: string;
|
||||
private lineMap: number[];
|
||||
private textVersion = 0;
|
||||
|
||||
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath) {
|
||||
}
|
||||
|
||||
public getVersion() {
|
||||
return this.svc
|
||||
? `SVC-${this.svcVersion}-${this.svc.getSnapshot().version}`
|
||||
: `Text-${this.textVersion}`;
|
||||
}
|
||||
|
||||
public hasScriptVersionCache() {
|
||||
return this.svc !== undefined;
|
||||
}
|
||||
|
||||
public useScriptVersionCache(newText?: string) {
|
||||
this.switchToScriptVersionCache(newText);
|
||||
}
|
||||
|
||||
public useText(newText?: string) {
|
||||
this.svc = undefined;
|
||||
this.setText(newText);
|
||||
}
|
||||
|
||||
public edit(start: number, end: number, newText: string) {
|
||||
this.switchToScriptVersionCache().edit(start, end - start, newText);
|
||||
}
|
||||
|
||||
public reload(text: string) {
|
||||
if (this.svc) {
|
||||
this.svc.reload(text);
|
||||
}
|
||||
else {
|
||||
this.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
public reloadFromFile(tempFileName?: string) {
|
||||
if (this.svc || (tempFileName !== this.fileName)) {
|
||||
this.reload(this.getFileText(tempFileName))
|
||||
}
|
||||
else {
|
||||
this.setText(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public getSnapshot(): IScriptSnapshot {
|
||||
return this.svc
|
||||
? this.svc.getSnapshot()
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
|
||||
public getLineInfo(line: number) {
|
||||
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
|
||||
}
|
||||
/**
|
||||
* @param line 0 based index
|
||||
*/
|
||||
lineToTextSpan(line: number) {
|
||||
if (!this.svc) {
|
||||
const lineMap = this.getLineMap();
|
||||
const start = lineMap[line]; // -1 since line is 1-based
|
||||
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
|
||||
return ts.createTextSpanFromBounds(start, end);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineInfo = index.lineNumberToInfo(line + 1);
|
||||
let len: number;
|
||||
if (lineInfo.leaf) {
|
||||
len = lineInfo.leaf.text.length;
|
||||
}
|
||||
else {
|
||||
const nextLineInfo = index.lineNumberToInfo(line + 2);
|
||||
len = nextLineInfo.offset - lineInfo.offset;
|
||||
}
|
||||
return ts.createTextSpan(lineInfo.offset, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1 based index
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
if (!this.svc) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
|
||||
const lineInfo = index.lineNumberToInfo(line);
|
||||
// TODO: assert this offset is actually on the line
|
||||
return (lineInfo.offset + offset - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
if (!this.svc) {
|
||||
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
|
||||
return { line: line + 1, offset: character + 1 };
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineOffset = index.charOffsetToLineNumberAndPos(position);
|
||||
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
|
||||
}
|
||||
|
||||
private getFileText(tempFileName?: string) {
|
||||
return this.host.readFile(tempFileName || this.fileName) || "";
|
||||
}
|
||||
|
||||
private ensureNoScriptVersionCache() {
|
||||
Debug.assert(!this.svc, "ScriptVersionCache should not be set");
|
||||
}
|
||||
|
||||
private switchToScriptVersionCache(newText?: string): ScriptVersionCache {
|
||||
if (!this.svc) {
|
||||
this.svc = ScriptVersionCache.fromString(this.host, newText !== undefined ? newText : this.getOrLoadText());
|
||||
this.svcVersion++;
|
||||
this.text = undefined;
|
||||
}
|
||||
return this.svc;
|
||||
}
|
||||
|
||||
private getOrLoadText() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (this.text === undefined) {
|
||||
this.setText(this.getFileText());
|
||||
}
|
||||
return this.text;
|
||||
}
|
||||
|
||||
private getLineMap() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
return this.lineMap || (this.lineMap = computeLineStarts(this.getOrLoadText()));
|
||||
}
|
||||
|
||||
private setText(newText: string) {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (newText === undefined || this.text !== newText) {
|
||||
this.text = newText;
|
||||
this.lineMap = undefined;
|
||||
this.textVersion++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ScriptInfo {
|
||||
/**
|
||||
* All projects that include this file
|
||||
@@ -11,24 +166,46 @@ namespace ts.server {
|
||||
readonly path: Path;
|
||||
|
||||
private fileWatcher: FileWatcher;
|
||||
private svc: ScriptVersionCache;
|
||||
private textStorage: TextStorage;
|
||||
|
||||
private isOpen: boolean;
|
||||
|
||||
// TODO: allow to update hasMixedContent from the outside
|
||||
constructor(
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
content: string,
|
||||
readonly scriptKind: ScriptKind,
|
||||
public isOpen = false,
|
||||
public hasMixedContent = false) {
|
||||
|
||||
this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames));
|
||||
this.svc = ScriptVersionCache.fromString(host, content);
|
||||
this.textStorage = new TextStorage(host, fileName);
|
||||
if (hasMixedContent) {
|
||||
this.textStorage.reload("");
|
||||
}
|
||||
this.scriptKind = scriptKind
|
||||
? scriptKind
|
||||
: getScriptKindFromFileName(fileName);
|
||||
}
|
||||
|
||||
public isScriptOpen() {
|
||||
return this.isOpen;
|
||||
}
|
||||
|
||||
public open(newText: string) {
|
||||
this.isOpen = true;
|
||||
this.textStorage.useScriptVersionCache(newText);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isOpen = false;
|
||||
this.textStorage.useText(this.hasMixedContent ? "" : undefined);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
public getSnapshot() {
|
||||
return this.textStorage.getSnapshot();
|
||||
}
|
||||
|
||||
getFormatCodeSettings() {
|
||||
return this.formatCodeSettings;
|
||||
}
|
||||
@@ -90,12 +267,18 @@ namespace ts.server {
|
||||
return this.containingProjects[0];
|
||||
}
|
||||
|
||||
registerFileUpdate(): void {
|
||||
for (const p of this.containingProjects) {
|
||||
p.registerFileUpdate(this.path);
|
||||
}
|
||||
}
|
||||
|
||||
setFormatOptions(formatSettings: FormatCodeSettings): void {
|
||||
if (formatSettings) {
|
||||
if (!this.formatCodeSettings) {
|
||||
this.formatCodeSettings = getDefaultFormatCodeSettings(this.host);
|
||||
}
|
||||
mergeMaps(this.formatCodeSettings, formatSettings);
|
||||
mergeMapLikes(this.formatCodeSettings, formatSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,16 +295,16 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getLatestVersion() {
|
||||
return this.svc.latestVersion().toString();
|
||||
return this.textStorage.getVersion();
|
||||
}
|
||||
|
||||
reload(script: string) {
|
||||
this.svc.reload(script);
|
||||
this.textStorage.reload(script);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
saveTo(fileName: string) {
|
||||
const snap = this.snap();
|
||||
const snap = this.textStorage.getSnapshot();
|
||||
this.host.writeFile(fileName, snap.getText(0, snap.getLength()));
|
||||
}
|
||||
|
||||
@@ -130,22 +313,17 @@ namespace ts.server {
|
||||
this.reload("");
|
||||
}
|
||||
else {
|
||||
this.svc.reloadFromFile(tempFileName || this.fileName);
|
||||
this.textStorage.reloadFromFile(tempFileName);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
snap() {
|
||||
return this.svc.getSnapshot();
|
||||
}
|
||||
|
||||
getLineInfo(line: number) {
|
||||
const snap = this.snap();
|
||||
return snap.index.lineNumberToInfo(line);
|
||||
return this.textStorage.getLineInfo(line);
|
||||
}
|
||||
|
||||
editContent(start: number, end: number, newText: string): void {
|
||||
this.svc.edit(start, end - start, newText);
|
||||
this.textStorage.edit(start, end, newText);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
@@ -159,17 +337,7 @@ namespace ts.server {
|
||||
* @param line 1 based index
|
||||
*/
|
||||
lineToTextSpan(line: number) {
|
||||
const index = this.snap().index;
|
||||
const lineInfo = index.lineNumberToInfo(line + 1);
|
||||
let len: number;
|
||||
if (lineInfo.leaf) {
|
||||
len = lineInfo.leaf.text.length;
|
||||
}
|
||||
else {
|
||||
const nextLineInfo = index.lineNumberToInfo(line + 2);
|
||||
len = nextLineInfo.offset - lineInfo.offset;
|
||||
}
|
||||
return ts.createTextSpan(lineInfo.offset, len);
|
||||
return this.textStorage.lineToTextSpan(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,11 +345,7 @@ namespace ts.server {
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
const index = this.snap().index;
|
||||
|
||||
const lineInfo = index.lineNumberToInfo(line);
|
||||
// TODO: assert this offset is actually on the line
|
||||
return (lineInfo.offset + offset - 1);
|
||||
return this.textStorage.lineOffsetToPosition(line, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,9 +353,11 @@ namespace ts.server {
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
const index = this.snap().index;
|
||||
const lineOffset = index.charOffsetToLineNumberAndPos(position);
|
||||
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
|
||||
return this.textStorage.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
public isJavaScript() {
|
||||
return this.scriptKind === ScriptKind.JS || this.scriptKind === ScriptKind.JSX;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ namespace ts.server {
|
||||
if (len > 1) {
|
||||
let insertedNodes = <LineCollection[]>new Array(len - 1);
|
||||
let startNode = <LineCollection>leafNode;
|
||||
for (let i = 1, len = lines.length; i < len; i++) {
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
insertedNodes[i - 1] = new LineLeaf(lines[i]);
|
||||
}
|
||||
let pathIndex = this.startPath.length - 2;
|
||||
@@ -341,8 +341,7 @@ namespace ts.server {
|
||||
let snap = this.versions[this.currentVersionToIndex()];
|
||||
if (this.changes.length > 0) {
|
||||
let snapIndex = snap.index;
|
||||
for (let i = 0, len = this.changes.length; i < len; i++) {
|
||||
const change = this.changes[i];
|
||||
for (const change of this.changes) {
|
||||
snapIndex = snapIndex.edit(change.pos, change.deleteLen, change.insertedText);
|
||||
}
|
||||
snap = new LineIndexSnapshot(this.currentVersion + 1, this);
|
||||
@@ -366,8 +365,7 @@ namespace ts.server {
|
||||
const textChangeRanges: ts.TextChangeRange[] = [];
|
||||
for (let i = oldVersion + 1; i <= newVersion; i++) {
|
||||
const snap = this.versions[this.versionToIndex(i)];
|
||||
for (let j = 0, len = snap.changesSincePreviousVersion.length; j < len; j++) {
|
||||
const textChange = snap.changesSincePreviousVersion[j];
|
||||
for (const textChange of snap.changesSincePreviousVersion) {
|
||||
textChangeRanges[textChangeRanges.length] = textChange.getTextChangeRange();
|
||||
}
|
||||
}
|
||||
@@ -398,7 +396,7 @@ namespace ts.server {
|
||||
index: LineIndex;
|
||||
changesSincePreviousVersion: TextChange[] = [];
|
||||
|
||||
constructor(public version: number, public cache: ScriptVersionCache) {
|
||||
constructor(readonly version: number, readonly cache: ScriptVersionCache) {
|
||||
}
|
||||
|
||||
getText(rangeStart: number, rangeEnd: number) {
|
||||
@@ -438,8 +436,9 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
|
||||
const oldSnap = <LineIndexSnapshot>oldSnapshot;
|
||||
return this.getTextChangeRangeSinceVersion(oldSnap.version);
|
||||
if (oldSnapshot instanceof LineIndexSnapshot && this.cache === oldSnapshot.cache) {
|
||||
return this.getTextChangeRangeSinceVersion(oldSnapshot.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +469,7 @@ namespace ts.server {
|
||||
load(lines: string[]) {
|
||||
if (lines.length > 0) {
|
||||
const leaves: LineLeaf[] = [];
|
||||
for (let i = 0, len = lines.length; i < len; i++) {
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
leaves[i] = new LineLeaf(lines[i]);
|
||||
}
|
||||
this.root = LineIndex.buildTreeFromBottom(leaves);
|
||||
@@ -642,8 +641,7 @@ namespace ts.server {
|
||||
updateCounts() {
|
||||
this.totalChars = 0;
|
||||
this.totalLines = 0;
|
||||
for (let i = 0, len = this.children.length; i < len; i++) {
|
||||
const child = this.children[i];
|
||||
for (const child of this.children) {
|
||||
this.totalChars += child.charCount();
|
||||
this.totalLines += child.lineCount();
|
||||
}
|
||||
|
||||
+69
-28
@@ -20,33 +20,41 @@ namespace ts.server {
|
||||
} = require("os");
|
||||
|
||||
function getGlobalTypingsCacheLocation() {
|
||||
let basePath: string;
|
||||
switch (process.platform) {
|
||||
case "win32":
|
||||
basePath = process.env.LOCALAPPDATA ||
|
||||
case "win32": {
|
||||
const basePath = process.env.LOCALAPPDATA ||
|
||||
process.env.APPDATA ||
|
||||
(os.homedir && os.homedir()) ||
|
||||
process.env.USERPROFILE ||
|
||||
(process.env.HOMEDRIVE && process.env.HOMEPATH && normalizeSlashes(process.env.HOMEDRIVE + process.env.HOMEPATH)) ||
|
||||
os.tmpdir();
|
||||
break;
|
||||
case "linux":
|
||||
basePath = (os.homedir && os.homedir()) ||
|
||||
process.env.HOME ||
|
||||
((process.env.LOGNAME || process.env.USER) && `/home/${process.env.LOGNAME || process.env.USER}`) ||
|
||||
os.tmpdir();
|
||||
break;
|
||||
return combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
|
||||
}
|
||||
case "darwin":
|
||||
const homeDir = (os.homedir && os.homedir()) ||
|
||||
process.env.HOME ||
|
||||
((process.env.LOGNAME || process.env.USER) && `/Users/${process.env.LOGNAME || process.env.USER}`) ||
|
||||
os.tmpdir();
|
||||
basePath = combinePaths(homeDir, "Library/Application Support/");
|
||||
break;
|
||||
case "linux":
|
||||
case "android": {
|
||||
const cacheLocation = getNonWindowsCacheLocation(process.platform === "darwin");
|
||||
return combinePaths(cacheLocation, "typescript");
|
||||
}
|
||||
default:
|
||||
Debug.fail(`unsupported platform '${process.platform}'`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.assert(basePath !== undefined);
|
||||
return combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
|
||||
function getNonWindowsCacheLocation(platformIsDarwin: boolean) {
|
||||
if (process.env.XDG_CACHE_HOME) {
|
||||
return process.env.XDG_CACHE_HOME;
|
||||
}
|
||||
const usersDir = platformIsDarwin ? "Users" : "home"
|
||||
const homePath = (os.homedir && os.homedir()) ||
|
||||
process.env.HOME ||
|
||||
((process.env.LOGNAME || process.env.USER) && `/${usersDir}/${process.env.LOGNAME || process.env.USER}`) ||
|
||||
os.tmpdir();
|
||||
const cacheFolder = platformIsDarwin
|
||||
? "Library/Caches"
|
||||
: ".cache"
|
||||
return combinePaths(normalizeSlashes(homePath), cacheFolder);
|
||||
}
|
||||
|
||||
interface NodeChildProcess {
|
||||
@@ -197,7 +205,7 @@ namespace ts.server {
|
||||
private socket: NodeSocket;
|
||||
private projectService: ProjectService;
|
||||
private throttledOperations: ThrottledOperations;
|
||||
private telemetrySender: EventSender;
|
||||
private eventSender: EventSender;
|
||||
|
||||
constructor(
|
||||
private readonly telemetryEnabled: boolean,
|
||||
@@ -230,7 +238,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
setTelemetrySender(telemetrySender: EventSender) {
|
||||
this.telemetrySender = telemetrySender;
|
||||
this.eventSender = telemetrySender;
|
||||
}
|
||||
|
||||
attach(projectService: ProjectService) {
|
||||
@@ -275,8 +283,8 @@ namespace ts.server {
|
||||
this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" });
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray<string>): void {
|
||||
const request = createInstallTypingsRequest(project, typingOptions, unresolvedImports);
|
||||
enqueueInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>): void {
|
||||
const request = createInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Scheduling throttled operation: ${JSON.stringify(request)}`);
|
||||
@@ -290,12 +298,30 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
private handleMessage(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent) {
|
||||
private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes) {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Received response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
if (response.kind === EventInstall) {
|
||||
if (this.telemetrySender) {
|
||||
|
||||
if (response.kind === EventBeginInstallTypes) {
|
||||
if (!this.eventSender) {
|
||||
return;
|
||||
}
|
||||
const body: protocol.BeginInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
};
|
||||
const eventName: protocol.BeginInstallTypesEventName = "beginInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.kind === EventEndInstallTypes) {
|
||||
if (!this.eventSender) {
|
||||
return;
|
||||
}
|
||||
if (this.telemetryEnabled) {
|
||||
const body: protocol.TypingsInstalledTelemetryEventBody = {
|
||||
telemetryEventName: "typingsInstalled",
|
||||
payload: {
|
||||
@@ -305,10 +331,19 @@ namespace ts.server {
|
||||
}
|
||||
};
|
||||
const eventName: protocol.TelemetryEventName = "telemetry";
|
||||
this.telemetrySender.event(body, eventName);
|
||||
this.eventSender.event(body, eventName);
|
||||
}
|
||||
|
||||
const body: protocol.EndInstallTypesEventBody = {
|
||||
eventId: response.eventId,
|
||||
packages: response.packagesToInstall,
|
||||
success: response.installSuccess,
|
||||
};
|
||||
const eventName: protocol.EndInstallTypesEventName = "endInstallTypes";
|
||||
this.eventSender.event(body, eventName);
|
||||
return;
|
||||
}
|
||||
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
if (response.kind == ActionSet && this.socket) {
|
||||
this.sendEvent(0, "setTypings", response);
|
||||
@@ -374,7 +409,8 @@ namespace ts.server {
|
||||
function parseLoggingEnvironmentString(logEnvStr: string): LogOptions {
|
||||
const logEnv: LogOptions = { logToFile: true };
|
||||
const args = logEnvStr.split(" ");
|
||||
for (let i = 0, len = args.length; i < (len - 1); i += 2) {
|
||||
const len = args.length - 1;
|
||||
for (let i = 0; i < len; i += 2) {
|
||||
const option = args[i];
|
||||
const value = args[i + 1];
|
||||
if (option && value) {
|
||||
@@ -577,6 +613,11 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
const localeStr = findArgument("--locale");
|
||||
if (localeStr) {
|
||||
validateLocaleAndSetLanguage(localeStr, sys);
|
||||
}
|
||||
|
||||
const useSingleInferredProject = hasArgument("--useSingleInferredProject");
|
||||
const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition");
|
||||
const telemetryEnabled = hasArgument(Arguments.EnableTelemetry);
|
||||
@@ -598,4 +639,4 @@ namespace ts.server {
|
||||
(process as any).noAsar = true;
|
||||
// Start listening
|
||||
ioSession.listen();
|
||||
}
|
||||
}
|
||||
|
||||
+36
-11
@@ -88,50 +88,65 @@ namespace ts.server {
|
||||
|
||||
export namespace CommandNames {
|
||||
export const Brace: protocol.CommandTypes.Brace = "brace";
|
||||
/* @internal */
|
||||
export const BraceFull: protocol.CommandTypes.BraceFull = "brace-full";
|
||||
export const BraceCompletion: protocol.CommandTypes.BraceCompletion = "braceCompletion";
|
||||
export const Change: protocol.CommandTypes.Change = "change";
|
||||
export const Close: protocol.CommandTypes.Close = "close";
|
||||
export const Completions: protocol.CommandTypes.Completions = "completions";
|
||||
/* @internal */
|
||||
export const CompletionsFull: protocol.CommandTypes.CompletionsFull = "completions-full";
|
||||
export const CompletionDetails: protocol.CommandTypes.CompletionDetails = "completionEntryDetails";
|
||||
export const CompileOnSaveAffectedFileList: protocol.CommandTypes.CompileOnSaveAffectedFileList = "compileOnSaveAffectedFileList";
|
||||
export const CompileOnSaveEmitFile: protocol.CommandTypes.CompileOnSaveEmitFile = "compileOnSaveEmitFile";
|
||||
export const Configure: protocol.CommandTypes.Configure = "configure";
|
||||
export const Definition: protocol.CommandTypes.Definition = "definition";
|
||||
/* @internal */
|
||||
export const DefinitionFull: protocol.CommandTypes.DefinitionFull = "definition-full";
|
||||
export const Exit: protocol.CommandTypes.Exit = "exit";
|
||||
export const Format: protocol.CommandTypes.Format = "format";
|
||||
export const Formatonkey: protocol.CommandTypes.Formatonkey = "formatonkey";
|
||||
/* @internal */
|
||||
export const FormatFull: protocol.CommandTypes.FormatFull = "format-full";
|
||||
/* @internal */
|
||||
export const FormatonkeyFull: protocol.CommandTypes.FormatonkeyFull = "formatonkey-full";
|
||||
/* @internal */
|
||||
export const FormatRangeFull: protocol.CommandTypes.FormatRangeFull = "formatRange-full";
|
||||
export const Geterr: protocol.CommandTypes.Geterr = "geterr";
|
||||
export const GeterrForProject: protocol.CommandTypes.GeterrForProject = "geterrForProject";
|
||||
export const Implementation: protocol.CommandTypes.Implementation = "implementation";
|
||||
/* @internal */
|
||||
export const ImplementationFull: protocol.CommandTypes.ImplementationFull = "implementation-full";
|
||||
export const SemanticDiagnosticsSync: protocol.CommandTypes.SemanticDiagnosticsSync = "semanticDiagnosticsSync";
|
||||
export const SyntacticDiagnosticsSync: protocol.CommandTypes.SyntacticDiagnosticsSync = "syntacticDiagnosticsSync";
|
||||
export const NavBar: protocol.CommandTypes.NavBar = "navbar";
|
||||
/* @internal */
|
||||
export const NavBarFull: protocol.CommandTypes.NavBarFull = "navbar-full";
|
||||
export const NavTree: protocol.CommandTypes.NavTree = "navtree";
|
||||
export const NavTreeFull: protocol.CommandTypes.NavTreeFull = "navtree-full";
|
||||
export const Navto: protocol.CommandTypes.Navto = "navto";
|
||||
/* @internal */
|
||||
export const NavtoFull: protocol.CommandTypes.NavtoFull = "navto-full";
|
||||
export const Occurrences: protocol.CommandTypes.Occurrences = "occurrences";
|
||||
export const DocumentHighlights: protocol.CommandTypes.DocumentHighlights = "documentHighlights";
|
||||
/* @internal */
|
||||
export const DocumentHighlightsFull: protocol.CommandTypes.DocumentHighlightsFull = "documentHighlights-full";
|
||||
export const Open: protocol.CommandTypes.Open = "open";
|
||||
export const Quickinfo: protocol.CommandTypes.Quickinfo = "quickinfo";
|
||||
/* @internal */
|
||||
export const QuickinfoFull: protocol.CommandTypes.QuickinfoFull = "quickinfo-full";
|
||||
export const References: protocol.CommandTypes.References = "references";
|
||||
/* @internal */
|
||||
export const ReferencesFull: protocol.CommandTypes.ReferencesFull = "references-full";
|
||||
export const Reload: protocol.CommandTypes.Reload = "reload";
|
||||
export const Rename: protocol.CommandTypes.Rename = "rename";
|
||||
/* @internal */
|
||||
export const RenameInfoFull: protocol.CommandTypes.RenameInfoFull = "rename-full";
|
||||
/* @internal */
|
||||
export const RenameLocationsFull: protocol.CommandTypes.RenameLocationsFull = "renameLocations-full";
|
||||
export const Saveto: protocol.CommandTypes.Saveto = "saveto";
|
||||
export const SignatureHelp: protocol.CommandTypes.SignatureHelp = "signatureHelp";
|
||||
/* @internal */
|
||||
export const SignatureHelpFull: protocol.CommandTypes.SignatureHelpFull = "signatureHelp-full";
|
||||
export const TypeDefinition: protocol.CommandTypes.TypeDefinition = "typeDefinition";
|
||||
export const ProjectInfo: protocol.CommandTypes.ProjectInfo = "projectInfo";
|
||||
@@ -140,19 +155,28 @@ namespace ts.server {
|
||||
export const OpenExternalProject: protocol.CommandTypes.OpenExternalProject = "openExternalProject";
|
||||
export const OpenExternalProjects: protocol.CommandTypes.OpenExternalProjects = "openExternalProjects";
|
||||
export const CloseExternalProject: protocol.CommandTypes.CloseExternalProject = "closeExternalProject";
|
||||
/* @internal */
|
||||
export const SynchronizeProjectList: protocol.CommandTypes.SynchronizeProjectList = "synchronizeProjectList";
|
||||
/* @internal */
|
||||
export const ApplyChangedToOpenFiles: protocol.CommandTypes.ApplyChangedToOpenFiles = "applyChangedToOpenFiles";
|
||||
/* @internal */
|
||||
export const EncodedSemanticClassificationsFull: protocol.CommandTypes.EncodedSemanticClassificationsFull = "encodedSemanticClassifications-full";
|
||||
/* @internal */
|
||||
export const Cleanup: protocol.CommandTypes.Cleanup = "cleanup";
|
||||
/* @internal */
|
||||
export const OutliningSpans: protocol.CommandTypes.OutliningSpans = "outliningSpans";
|
||||
export const TodoComments: protocol.CommandTypes.TodoComments = "todoComments";
|
||||
export const Indentation: protocol.CommandTypes.Indentation = "indentation";
|
||||
export const DocCommentTemplate: protocol.CommandTypes.DocCommentTemplate = "docCommentTemplate";
|
||||
/* @internal */
|
||||
export const CompilerOptionsDiagnosticsFull: protocol.CommandTypes.CompilerOptionsDiagnosticsFull = "compilerOptionsDiagnostics-full";
|
||||
/* @internal */
|
||||
export const NameOrDottedNameSpan: protocol.CommandTypes.NameOrDottedNameSpan = "nameOrDottedNameSpan";
|
||||
/* @internal */
|
||||
export const BreakpointStatement: protocol.CommandTypes.BreakpointStatement = "breakpointStatement";
|
||||
export const CompilerOptionsForInferredProjects: protocol.CommandTypes.CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects";
|
||||
export const GetCodeFixes: protocol.CommandTypes.GetCodeFixes = "getCodeFixes";
|
||||
/* @internal */
|
||||
export const GetCodeFixesFull: protocol.CommandTypes.GetCodeFixesFull = "getCodeFixes-full";
|
||||
export const GetSupportedCodeFixes: protocol.CommandTypes.GetSupportedCodeFixes = "getSupportedCodeFixes";
|
||||
}
|
||||
@@ -709,7 +733,7 @@ namespace ts.server {
|
||||
const displayString = ts.displayPartsToString(nameInfo.displayParts);
|
||||
const nameSpan = nameInfo.textSpan;
|
||||
const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset;
|
||||
const nameText = scriptInfo.snap().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const refs = combineProjectOutput<protocol.ReferencesResponseItem>(
|
||||
projects,
|
||||
(project: Project) => {
|
||||
@@ -722,7 +746,7 @@ namespace ts.server {
|
||||
const refScriptInfo = project.getScriptInfo(ref.fileName);
|
||||
const start = refScriptInfo.positionToLineOffset(ref.textSpan.start);
|
||||
const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1);
|
||||
const lineText = refScriptInfo.snap().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: ref.fileName,
|
||||
start: start,
|
||||
@@ -1026,6 +1050,9 @@ namespace ts.server {
|
||||
if (!project) {
|
||||
Errors.ThrowNoProject();
|
||||
}
|
||||
if (!project.languageServiceEnabled) {
|
||||
return false;
|
||||
}
|
||||
const scriptInfo = project.getScriptInfo(file);
|
||||
return project.builder.emitFile(scriptInfo, (path, data, writeByteOrderMark) => this.host.writeFile(path, data, writeByteOrderMark));
|
||||
}
|
||||
@@ -1326,7 +1353,7 @@ namespace ts.server {
|
||||
highPriorityFiles.push(fileNameInProject);
|
||||
else {
|
||||
const info = this.projectService.getScriptInfo(fileNameInProject);
|
||||
if (!info.isOpen) {
|
||||
if (!info.isScriptOpen()) {
|
||||
if (fileNameInProject.indexOf(".d.ts") > 0)
|
||||
veryLowPriorityFiles.push(fileNameInProject);
|
||||
else
|
||||
@@ -1363,16 +1390,14 @@ namespace ts.server {
|
||||
return { response, responseRequired: true };
|
||||
}
|
||||
|
||||
private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
|
||||
private handlers = createMapFromTemplate<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
|
||||
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
|
||||
this.projectService.openExternalProject(request.arguments);
|
||||
this.projectService.openExternalProject(request.arguments, /*suppressRefreshOfInferredProjects*/ false);
|
||||
// TODO: report errors
|
||||
return this.requiredResponse(true);
|
||||
},
|
||||
[CommandNames.OpenExternalProjects]: (request: protocol.OpenExternalProjectsRequest) => {
|
||||
for (const proj of request.arguments.projects) {
|
||||
this.projectService.openExternalProject(proj);
|
||||
}
|
||||
this.projectService.openExternalProjects(request.arguments.projects);
|
||||
// TODO: report errors
|
||||
return this.requiredResponse(true);
|
||||
},
|
||||
@@ -1609,14 +1634,14 @@ namespace ts.server {
|
||||
});
|
||||
|
||||
public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) {
|
||||
if (command in this.handlers) {
|
||||
if (this.handlers.has(command)) {
|
||||
throw new Error(`Protocol handler already exists for command "${command}"`);
|
||||
}
|
||||
this.handlers[command] = handler;
|
||||
this.handlers.set(command, handler);
|
||||
}
|
||||
|
||||
public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } {
|
||||
const handler = this.handlers[request.command];
|
||||
const handler = this.handlers.get(request.command);
|
||||
if (handler) {
|
||||
return handler(request);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/// <reference path="types.d.ts" />
|
||||
/// <reference path="types.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
export const ActionSet: ActionSet = "action::set";
|
||||
export const ActionInvalidate: ActionInvalidate = "action::invalidate";
|
||||
export const EventInstall: EventInstall = "event::install";
|
||||
export const EventBeginInstallTypes: EventBeginInstallTypes = "event::beginInstallTypes";
|
||||
export const EventEndInstallTypes: EventEndInstallTypes = "event::endInstallTypes";
|
||||
|
||||
export namespace Arguments {
|
||||
export const GlobalCacheLocation = "--globalTypingsCacheLocation";
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"outFile": "../../built/local/tsserver.js",
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../services/shims.ts",
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"noImplicitThis": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"outFile": "../../built/local/tsserverlibrary.js",
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"declaration": true,
|
||||
"types": [],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
"noUnusedParameters": true,
|
||||
"declaration": true
|
||||
},
|
||||
"files": [
|
||||
"../services/shims.ts",
|
||||
"../services/utilities.ts",
|
||||
"shared.ts",
|
||||
"utilities.ts",
|
||||
"scriptVersionCache.ts",
|
||||
"scriptInfo.ts",
|
||||
"lshost.ts",
|
||||
"typingsCache.ts",
|
||||
"project.ts",
|
||||
"editorServices.ts",
|
||||
"protocol.d.ts",
|
||||
"session.ts"
|
||||
"lsHost.ts",
|
||||
"project.ts",
|
||||
"protocol.ts",
|
||||
"scriptInfo.ts",
|
||||
"scriptVersionCache.ts",
|
||||
"session.ts",
|
||||
"shared.ts",
|
||||
"types.ts",
|
||||
"typingsCache.ts",
|
||||
"utilities.ts",
|
||||
"../services/shims.ts",
|
||||
"../services/utilities.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ declare namespace ts.server {
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: ts.Path;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typingOptions: ts.TypingOptions;
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly cachePath?: string;
|
||||
readonly kind: "discover";
|
||||
@@ -43,10 +43,11 @@ declare namespace ts.server {
|
||||
|
||||
export type ActionSet = "action::set";
|
||||
export type ActionInvalidate = "action::invalidate";
|
||||
export type EventInstall = "event::install";
|
||||
export type EventBeginInstallTypes = "event::beginInstallTypes";
|
||||
export type EventEndInstallTypes = "event::endInstallTypes";
|
||||
|
||||
export interface TypingInstallerResponse {
|
||||
readonly kind: ActionSet | ActionInvalidate | EventInstall;
|
||||
readonly kind: ActionSet | ActionInvalidate | EventBeginInstallTypes | EventEndInstallTypes;
|
||||
}
|
||||
|
||||
export interface ProjectResponse extends TypingInstallerResponse {
|
||||
@@ -54,7 +55,7 @@ declare namespace ts.server {
|
||||
}
|
||||
|
||||
export interface SetTypings extends ProjectResponse {
|
||||
readonly typingOptions: ts.TypingOptions;
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typings: string[];
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
@@ -65,13 +66,23 @@ declare namespace ts.server {
|
||||
readonly kind: ActionInvalidate;
|
||||
}
|
||||
|
||||
export interface TypingsInstallEvent extends TypingInstallerResponse {
|
||||
readonly packagesToInstall: ReadonlyArray<string>;
|
||||
readonly kind: EventInstall;
|
||||
readonly installSuccess: boolean;
|
||||
export interface InstallTypes extends ProjectResponse {
|
||||
readonly kind: EventBeginInstallTypes | EventEndInstallTypes;
|
||||
readonly eventId: number;
|
||||
readonly typingsInstallerVersion: string;
|
||||
readonly packagesToInstall: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export interface BeginInstallTypes extends InstallTypes {
|
||||
readonly kind: EventBeginInstallTypes;
|
||||
}
|
||||
|
||||
export interface EndInstallTypes extends InstallTypes {
|
||||
readonly kind: EventEndInstallTypes;
|
||||
readonly installSuccess: boolean;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface InstallTypingHost extends JsTyping.TypingResolutionHost {
|
||||
writeFile(path: string, content: string): void;
|
||||
createDirectory(path: string): void;
|
||||
+24
-23
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace ts.server {
|
||||
export interface ITypingsInstaller {
|
||||
enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray<string>): void;
|
||||
enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>): void;
|
||||
attach(projectService: ProjectService): void;
|
||||
onProjectClosed(p: Project): void;
|
||||
readonly globalTypingsCacheLocation: string;
|
||||
@@ -16,7 +16,7 @@ namespace ts.server {
|
||||
};
|
||||
|
||||
class TypingsCacheEntry {
|
||||
readonly typingOptions: TypingOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typings: SortedReadonlyArray<string>;
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
@@ -35,25 +35,26 @@ namespace ts.server {
|
||||
let unique = 0;
|
||||
|
||||
for (const v of arr1) {
|
||||
if (set[v] !== true) {
|
||||
set[v] = true;
|
||||
if (set.get(v) !== true) {
|
||||
set.set(v, true);
|
||||
unique++;
|
||||
}
|
||||
}
|
||||
for (const v of arr2) {
|
||||
if (!hasProperty(set, v)) {
|
||||
const isSet = set.get(v);
|
||||
if (isSet === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (set[v] === true) {
|
||||
set[v] = false;
|
||||
if (isSet === true) {
|
||||
set.set(v, false);
|
||||
unique--;
|
||||
}
|
||||
}
|
||||
return unique === 0;
|
||||
}
|
||||
|
||||
function typingOptionsChanged(opt1: TypingOptions, opt2: TypingOptions): boolean {
|
||||
return opt1.enableAutoDiscovery !== opt2.enableAutoDiscovery ||
|
||||
function typeAcquisitionChanged(opt1: TypeAcquisition, opt2: TypeAcquisition): boolean {
|
||||
return opt1.enable !== opt2.enable ||
|
||||
!setIsEqualTo(opt1.include, opt2.include) ||
|
||||
!setIsEqualTo(opt1.exclude, opt2.exclude);
|
||||
}
|
||||
@@ -77,50 +78,50 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray<string>, forceRefresh: boolean): SortedReadonlyArray<string> {
|
||||
const typingOptions = project.getTypingOptions();
|
||||
const typeAcquisition = project.getTypeAcquisition();
|
||||
|
||||
if (!typingOptions || !typingOptions.enableAutoDiscovery) {
|
||||
if (!typeAcquisition || !typeAcquisition.enable) {
|
||||
return <any>emptyArray;
|
||||
}
|
||||
|
||||
const entry = this.perProjectCache[project.getProjectName()];
|
||||
const entry = this.perProjectCache.get(project.getProjectName());
|
||||
const result: SortedReadonlyArray<string> = entry ? entry.typings : <any>emptyArray;
|
||||
if (forceRefresh ||
|
||||
!entry ||
|
||||
typingOptionsChanged(typingOptions, entry.typingOptions) ||
|
||||
typeAcquisitionChanged(typeAcquisition, entry.typeAcquisition) ||
|
||||
compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions) ||
|
||||
unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) {
|
||||
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
|
||||
// instead it acts as a placeholder to prevent issuing multiple requests
|
||||
this.perProjectCache[project.getProjectName()] = {
|
||||
this.perProjectCache.set(project.getProjectName(), {
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typingOptions,
|
||||
typeAcquisition,
|
||||
typings: result,
|
||||
unresolvedImports,
|
||||
poisoned: true
|
||||
};
|
||||
});
|
||||
// something has been changed, issue a request to update typings
|
||||
this.installer.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports);
|
||||
this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray<string>, newTypings: string[]) {
|
||||
this.perProjectCache[projectName] = {
|
||||
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>, newTypings: string[]) {
|
||||
this.perProjectCache.set(projectName, {
|
||||
compilerOptions,
|
||||
typingOptions,
|
||||
typeAcquisition,
|
||||
typings: toSortedReadonlyArray(newTypings),
|
||||
unresolvedImports,
|
||||
poisoned: false
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
deleteTypingsForProject(projectName: string) {
|
||||
delete this.perProjectCache[projectName];
|
||||
this.perProjectCache.delete(projectName);
|
||||
}
|
||||
|
||||
onProjectClosed(project: Project) {
|
||||
delete this.perProjectCache[project.getProjectName()];
|
||||
this.perProjectCache.delete(project.getProjectName());
|
||||
this.installer.onProjectClosed(project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
try {
|
||||
const content = <TypesRegistryFile>JSON.parse(host.readFile(typesRegistryFilePath));
|
||||
return createMap<void>(content.entries);
|
||||
return createMapFromTemplate<void>(content.entries);
|
||||
}
|
||||
catch (e) {
|
||||
if (log.isEnabled()) {
|
||||
@@ -70,13 +70,12 @@ namespace ts.server.typingsInstaller {
|
||||
private readonly npmPath: string;
|
||||
readonly typesRegistry: Map<void>;
|
||||
|
||||
constructor(globalTypingsCacheLocation: string, throttleLimit: number, telemetryEnabled: boolean, log: Log) {
|
||||
constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) {
|
||||
super(
|
||||
sys,
|
||||
globalTypingsCacheLocation,
|
||||
toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
|
||||
throttleLimit,
|
||||
telemetryEnabled,
|
||||
log);
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Process id: ${process.pid}`);
|
||||
@@ -113,7 +112,7 @@ namespace ts.server.typingsInstaller {
|
||||
});
|
||||
}
|
||||
|
||||
protected sendResponse(response: SetTypings | InvalidateCachedTypings) {
|
||||
protected sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
@@ -127,7 +126,7 @@ namespace ts.server.typingsInstaller {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(args)}'.`);
|
||||
}
|
||||
const command = `${this.npmPath} install ${args.join(" ")} --save-dev`;
|
||||
const command = `${this.npmPath} install ${args.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
|
||||
const start = Date.now();
|
||||
let stdout: Buffer;
|
||||
let stderr: Buffer;
|
||||
@@ -149,7 +148,6 @@ namespace ts.server.typingsInstaller {
|
||||
|
||||
const logFilePath = findArgument(server.Arguments.LogFile);
|
||||
const globalTypingsCacheLocation = findArgument(server.Arguments.GlobalCacheLocation);
|
||||
const telemetryEnabled = hasArgument(server.Arguments.EnableTelemetry);
|
||||
|
||||
const log = new FileLog(logFilePath);
|
||||
if (log.isEnabled()) {
|
||||
@@ -163,6 +161,6 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, telemetryEnabled, log);
|
||||
const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, log);
|
||||
installer.listen();
|
||||
}
|
||||
@@ -1,22 +1,14 @@
|
||||
{
|
||||
"extends": "../../tsconfig-base",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"outFile": "../../../built/local/typingsInstaller.js",
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../types.d.ts",
|
||||
"../types.ts",
|
||||
"../shared.ts",
|
||||
"typingsInstaller.ts",
|
||||
"nodeTypingsInstaller.ts"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// <reference path="../../compiler/core.ts" />
|
||||
/// <reference path="../../compiler/moduleNameResolver.ts" />
|
||||
/// <reference path="../../services/jsTyping.ts"/>
|
||||
/// <reference path="../types.d.ts"/>
|
||||
/// <reference path="../types.ts"/>
|
||||
/// <reference path="../shared.ts"/>
|
||||
|
||||
namespace ts.server.typingsInstaller {
|
||||
@@ -97,7 +97,6 @@ namespace ts.server.typingsInstaller {
|
||||
readonly globalCachePath: string,
|
||||
readonly safeListPath: Path,
|
||||
readonly throttleLimit: number,
|
||||
readonly telemetryEnabled: boolean,
|
||||
protected readonly log = nullLog) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`);
|
||||
@@ -113,7 +112,7 @@ namespace ts.server.typingsInstaller {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
|
||||
}
|
||||
const watchers = this.projectWatchers[projectName];
|
||||
const watchers = this.projectWatchers.get(projectName);
|
||||
if (!watchers) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
|
||||
@@ -124,7 +123,7 @@ namespace ts.server.typingsInstaller {
|
||||
w.close();
|
||||
}
|
||||
|
||||
delete this.projectWatchers[projectName];
|
||||
this.projectWatchers.delete(projectName);
|
||||
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
|
||||
@@ -150,7 +149,7 @@ namespace ts.server.typingsInstaller {
|
||||
req.projectRootPath,
|
||||
this.safeListPath,
|
||||
this.packageNameToTypingLocation,
|
||||
req.typingOptions,
|
||||
req.typeAcquisition,
|
||||
req.unresolvedImports);
|
||||
|
||||
if (this.log.isEnabled()) {
|
||||
@@ -178,7 +177,7 @@ namespace ts.server.typingsInstaller {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Processing cache location '${cacheLocation}'`);
|
||||
}
|
||||
if (this.knownCachesSet[cacheLocation]) {
|
||||
if (this.knownCachesSet.get(cacheLocation)) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Cache location was already processed...`);
|
||||
}
|
||||
@@ -202,10 +201,10 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
const typingFile = typingToFileName(cacheLocation, packageName, this.installTypingHost, this.log);
|
||||
if (!typingFile) {
|
||||
this.missingTypingsSet[packageName] = true;
|
||||
this.missingTypingsSet.set(packageName, true);
|
||||
continue;
|
||||
}
|
||||
const existingTypingFile = this.packageNameToTypingLocation[packageName];
|
||||
const existingTypingFile = this.packageNameToTypingLocation.get(packageName);
|
||||
if (existingTypingFile === typingFile) {
|
||||
continue;
|
||||
}
|
||||
@@ -217,14 +216,14 @@ namespace ts.server.typingsInstaller {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
|
||||
}
|
||||
this.packageNameToTypingLocation[packageName] = typingFile;
|
||||
this.packageNameToTypingLocation.set(packageName, typingFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Finished processing cache location '${cacheLocation}'`);
|
||||
}
|
||||
this.knownCachesSet[cacheLocation] = true;
|
||||
this.knownCachesSet.set(cacheLocation, true);
|
||||
}
|
||||
|
||||
private filterTypings(typingsToInstall: string[]) {
|
||||
@@ -233,12 +232,12 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
const result: string[] = [];
|
||||
for (const typing of typingsToInstall) {
|
||||
if (this.missingTypingsSet[typing] || this.packageNameToTypingLocation[typing]) {
|
||||
if (this.missingTypingsSet.get(typing) || this.packageNameToTypingLocation.get(typing)) {
|
||||
continue;
|
||||
}
|
||||
const validationResult = validatePackageName(typing);
|
||||
if (validationResult === PackageNameValidationResult.Ok) {
|
||||
if (typing in this.typesRegistry) {
|
||||
if (this.typesRegistry.has(typing)) {
|
||||
result.push(typing);
|
||||
}
|
||||
else {
|
||||
@@ -249,7 +248,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
else {
|
||||
// add typing name to missing set so we won't process it again
|
||||
this.missingTypingsSet[typing] = true;
|
||||
this.missingTypingsSet.set(typing, true);
|
||||
if (this.log.isEnabled()) {
|
||||
switch (validationResult) {
|
||||
case PackageNameValidationResult.EmptyName:
|
||||
@@ -309,47 +308,58 @@ namespace ts.server.typingsInstaller {
|
||||
const requestId = this.installRunCount;
|
||||
this.installRunCount++;
|
||||
|
||||
// send progress event
|
||||
this.sendResponse(<BeginInstallTypes>{
|
||||
kind: EventBeginInstallTypes,
|
||||
eventId: requestId,
|
||||
typingsInstallerVersion: ts.version, // qualified explicitly to prevent occasional shadowing
|
||||
projectName: req.projectName
|
||||
});
|
||||
|
||||
this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => {
|
||||
if (this.telemetryEnabled) {
|
||||
this.sendResponse(<TypingsInstallEvent>{
|
||||
kind: EventInstall,
|
||||
try {
|
||||
if (!ok) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`install request failed, marking packages as missing to prevent repeated requests: ${JSON.stringify(filteredTypings)}`);
|
||||
}
|
||||
for (const typing of filteredTypings) {
|
||||
this.missingTypingsSet.set(typing, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: watch project directory
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Installed typings ${JSON.stringify(scopedTypings)}`);
|
||||
}
|
||||
const installedTypingFiles: string[] = [];
|
||||
for (const packageName of filteredTypings) {
|
||||
const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost, this.log);
|
||||
if (!typingFile) {
|
||||
this.missingTypingsSet.set(packageName, true);
|
||||
continue;
|
||||
}
|
||||
if (!this.packageNameToTypingLocation.has(packageName)) {
|
||||
this.packageNameToTypingLocation.set(packageName, typingFile);
|
||||
}
|
||||
installedTypingFiles.push(typingFile);
|
||||
}
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`);
|
||||
}
|
||||
|
||||
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
|
||||
}
|
||||
finally {
|
||||
this.sendResponse(<EndInstallTypes>{
|
||||
kind: EventEndInstallTypes,
|
||||
eventId: requestId,
|
||||
projectName: req.projectName,
|
||||
packagesToInstall: scopedTypings,
|
||||
installSuccess: ok,
|
||||
typingsInstallerVersion: ts.version // qualified explicitly to prevent occasional shadowing
|
||||
});
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`install request failed, marking packages as missing to prevent repeated requests: ${JSON.stringify(filteredTypings)}`);
|
||||
}
|
||||
for (const typing of filteredTypings) {
|
||||
this.missingTypingsSet[typing] = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: watch project directory
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Installed typings ${JSON.stringify(scopedTypings)}`);
|
||||
}
|
||||
const installedTypingFiles: string[] = [];
|
||||
for (const packageName of filteredTypings) {
|
||||
const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost, this.log);
|
||||
if (!typingFile) {
|
||||
this.missingTypingsSet[packageName] = true;
|
||||
continue;
|
||||
}
|
||||
if (!this.packageNameToTypingLocation[packageName]) {
|
||||
this.packageNameToTypingLocation[packageName] = typingFile;
|
||||
}
|
||||
installedTypingFiles.push(typingFile);
|
||||
}
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`);
|
||||
}
|
||||
|
||||
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -385,13 +395,13 @@ namespace ts.server.typingsInstaller {
|
||||
}, /*pollingInterval*/ 2000);
|
||||
watchers.push(w);
|
||||
}
|
||||
this.projectWatchers[projectName] = watchers;
|
||||
this.projectWatchers.set(projectName, watchers);
|
||||
}
|
||||
|
||||
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {
|
||||
return {
|
||||
projectName: request.projectName,
|
||||
typingOptions: request.typingOptions,
|
||||
typeAcquisition: request.typeAcquisition,
|
||||
compilerOptions: request.compilerOptions,
|
||||
typings,
|
||||
unresolvedImports: request.unresolvedImports,
|
||||
@@ -417,6 +427,6 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
protected abstract installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
|
||||
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent): void;
|
||||
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void;
|
||||
}
|
||||
}
|
||||
+18
-14
@@ -1,4 +1,4 @@
|
||||
/// <reference path="types.d.ts" />
|
||||
/// <reference path="types.ts" />
|
||||
/// <reference path="shared.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
@@ -46,12 +46,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray<string>, cachePath?: string): DiscoverTypings {
|
||||
export function createInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>, cachePath?: string): DiscoverTypings {
|
||||
return {
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true),
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typingOptions,
|
||||
typeAcquisition,
|
||||
unresolvedImports,
|
||||
projectRootPath: getProjectRootPath(project),
|
||||
cachePath,
|
||||
@@ -78,6 +78,7 @@ namespace ts.server {
|
||||
newLineCharacter: host.newLine || "\n",
|
||||
convertTabsToSpaces: true,
|
||||
indentStyle: ts.IndentStyle.Smart,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
insertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
@@ -85,14 +86,16 @@ namespace ts.server {
|
||||
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
||||
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
|
||||
insertSpaceBeforeFunctionParenthesis: false,
|
||||
placeOpenBraceOnNewLineForFunctions: false,
|
||||
placeOpenBraceOnNewLineForControlBlocks: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeMaps(target: MapLike<any>, source: MapLike <any>): void {
|
||||
export function mergeMapLikes(target: MapLike<any>, source: MapLike <any>): void {
|
||||
for (const key in source) {
|
||||
if (hasProperty(source, key)) {
|
||||
target[key] = source[key];
|
||||
@@ -142,20 +145,20 @@ namespace ts.server {
|
||||
|
||||
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
const map: Map<T> = Object.create(null);
|
||||
const map = createMap<T>();
|
||||
/* tslint:enable:no-null-keyword */
|
||||
return {
|
||||
get(path) {
|
||||
return map[path];
|
||||
return map.get(path);
|
||||
},
|
||||
set(path, value) {
|
||||
map[path] = value;
|
||||
map.set(path, value);
|
||||
},
|
||||
contains(path) {
|
||||
return hasProperty(map, path);
|
||||
return map.has(path);
|
||||
},
|
||||
remove(path) {
|
||||
delete map[path];
|
||||
map.delete(path);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -171,7 +174,7 @@ namespace ts.server {
|
||||
files?: string[];
|
||||
wildcardDirectories?: Map<WatchDirectoryFlags>;
|
||||
compilerOptions?: CompilerOptions;
|
||||
typingOptions?: TypingOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
compileOnSave?: boolean;
|
||||
}
|
||||
|
||||
@@ -195,16 +198,17 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
public schedule(operationId: string, delay: number, cb: () => void) {
|
||||
if (hasProperty(this.pendingTimeouts, operationId)) {
|
||||
const pendingTimeout = this.pendingTimeouts.get(operationId);
|
||||
if (pendingTimeout) {
|
||||
// another operation was already scheduled for this id - cancel it
|
||||
this.host.clearTimeout(this.pendingTimeouts[operationId]);
|
||||
this.host.clearTimeout(pendingTimeout);
|
||||
}
|
||||
// schedule new operation, pass arguments
|
||||
this.pendingTimeouts[operationId] = this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb);
|
||||
this.pendingTimeouts.set(operationId, this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb));
|
||||
}
|
||||
|
||||
private static run(self: ThrottledOperations, operationId: string, cb: () => void) {
|
||||
delete self.pendingTimeouts[operationId];
|
||||
self.pendingTimeouts.delete(operationId);
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,11 +560,11 @@ namespace ts.BreakpointResolver {
|
||||
function spanInOpenBraceToken(node: Node): TextSpan {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
let enumDeclaration = <EnumDeclaration>node.parent;
|
||||
const enumDeclaration = <EnumDeclaration>node.parent;
|
||||
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), enumDeclaration.members.length ? enumDeclaration.members[0] : enumDeclaration.getLastToken(sourceFile));
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
let classDeclaration = <ClassDeclaration>node.parent;
|
||||
const classDeclaration = <ClassDeclaration>node.parent;
|
||||
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), classDeclaration.members.length ? classDeclaration.members[0] : classDeclaration.getLastToken(sourceFile));
|
||||
|
||||
case SyntaxKind.CaseBlock:
|
||||
@@ -600,8 +600,8 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.CaseBlock:
|
||||
// breakpoint in last statement of the last clause
|
||||
let caseBlock = <CaseBlock>node.parent;
|
||||
let lastClause = lastOrUndefined(caseBlock.clauses);
|
||||
const caseBlock = <CaseBlock>node.parent;
|
||||
const lastClause = lastOrUndefined(caseBlock.clauses);
|
||||
if (lastClause) {
|
||||
return spanInNode(lastOrUndefined(lastClause.statements));
|
||||
}
|
||||
@@ -609,7 +609,7 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let bindingPattern = <BindingPattern>node.parent;
|
||||
const bindingPattern = <BindingPattern>node.parent;
|
||||
return spanInNode(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
||||
|
||||
// Default to parent node
|
||||
@@ -627,7 +627,7 @@ namespace ts.BreakpointResolver {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let bindingPattern = <BindingPattern>node.parent;
|
||||
const bindingPattern = <BindingPattern>node.parent;
|
||||
return textSpan(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
||||
|
||||
default:
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace ts {
|
||||
const dense = classifications.spans;
|
||||
let lastEnd = 0;
|
||||
|
||||
for (let i = 0, n = dense.length; i < n; i += 3) {
|
||||
for (let i = 0; i < dense.length; i += 3) {
|
||||
const start = dense[i];
|
||||
const length = dense[i + 1];
|
||||
const type = <ClassificationType>dense[i + 2];
|
||||
@@ -557,7 +557,7 @@ namespace ts {
|
||||
// Only bother calling into the typechecker if this is an identifier that
|
||||
// could possibly resolve to a type name. This makes classification run
|
||||
// in a third of the time it would normally take.
|
||||
if (classifiableNames[identifier.text]) {
|
||||
if (classifiableNames.get(identifier.text)) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const type = classifySymbol(symbol, getMeaningFromLocation(node));
|
||||
@@ -605,7 +605,7 @@ namespace ts {
|
||||
Debug.assert(classifications.spans.length % 3 === 0);
|
||||
const dense = classifications.spans;
|
||||
const result: ClassifiedSpan[] = [];
|
||||
for (let i = 0, n = dense.length; i < n; i += 3) {
|
||||
for (let i = 0; i < dense.length; i += 3) {
|
||||
result.push({
|
||||
textSpan: createTextSpan(dense[i], dense[i + 1]),
|
||||
classificationType: getClassificationTypeName(dense[i + 2])
|
||||
@@ -972,9 +972,7 @@ namespace ts {
|
||||
if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) {
|
||||
checkForClassificationCancellation(cancellationToken, element.kind);
|
||||
|
||||
const children = element.getChildren(sourceFile);
|
||||
for (let i = 0, n = children.length; i < n; i++) {
|
||||
const child = children[i];
|
||||
for (const child of element.getChildren(sourceFile)) {
|
||||
if (!tryClassifyNode(child)) {
|
||||
// Recurse into our child nodes.
|
||||
processElement(child);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export namespace codefix {
|
||||
const codeFixes = createMap<CodeFix[]>();
|
||||
const codeFixes: CodeFix[][] = [];
|
||||
|
||||
export function registerCodeFix(action: CodeFix) {
|
||||
forEach(action.errorCodes, error => {
|
||||
@@ -0,0 +1,59 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code],
|
||||
getCodeActions: getActionForClassLikeMissingAbstractMember
|
||||
});
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code],
|
||||
getCodeActions: getActionForClassLikeMissingAbstractMember
|
||||
});
|
||||
|
||||
function getActionForClassLikeMissingAbstractMember(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
// This is the identifier in the case of a class declaration
|
||||
// or the class keyword token in the case of a class expression.
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
if (isClassLike(token.parent)) {
|
||||
const classDecl = token.parent as ClassLikeDeclaration;
|
||||
const startPos = classDecl.members.pos;
|
||||
|
||||
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
|
||||
const instantiatedExtendsType = checker.getBaseTypes(classType)[0];
|
||||
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType);
|
||||
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
|
||||
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
|
||||
|
||||
if (insertion.length) {
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
||||
|
||||
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
|
||||
const decls = symbol.getDeclarations();
|
||||
Debug.assert(!!(decls && decls.length > 0));
|
||||
const flags = getModifierFlags(decls[0]);
|
||||
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Class_0_incorrectly_implements_interface_1.code],
|
||||
getCodeActions: getActionForClassLikeIncorrectImplementsInterface
|
||||
});
|
||||
|
||||
function getActionForClassLikeIncorrectImplementsInterface(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const classDecl = getContainingClass(token);
|
||||
if (!classDecl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const startPos: number = classDecl.members.pos;
|
||||
const classType = checker.getTypeAtLocation(classDecl);
|
||||
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
|
||||
|
||||
const hasNumericIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.Number);
|
||||
const hasStringIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.String);
|
||||
|
||||
const result: CodeAction[] = [];
|
||||
for (const implementedTypeNode of implementedTypeNodes) {
|
||||
const implementedType = checker.getTypeFromTypeNode(implementedTypeNode) as InterfaceType;
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
const implementedTypeSymbols = checker.getPropertiesOfType(implementedType);
|
||||
const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private));
|
||||
|
||||
let insertion = getMissingIndexSignatureInsertion(implementedType, IndexKind.Number, classDecl, hasNumericIndexSignature);
|
||||
insertion += getMissingIndexSignatureInsertion(implementedType, IndexKind.String, classDecl, hasStringIndexSignature);
|
||||
insertion += getMissingMembersInsertion(classDecl, nonPrivateMembers, checker, context.newLineCharacter);
|
||||
|
||||
const message = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]);
|
||||
if (insertion) {
|
||||
pushAction(result, insertion, message);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function getMissingIndexSignatureInsertion(type: InterfaceType, kind: IndexKind, enclosingDeclaration: ClassLikeDeclaration, hasIndexSigOfKind: boolean) {
|
||||
if (!hasIndexSigOfKind) {
|
||||
const IndexInfoOfKind = checker.getIndexInfoOfType(type, kind);
|
||||
if (IndexInfoOfKind) {
|
||||
const writer = getSingleLineStringWriter();
|
||||
checker.getSymbolDisplayBuilder().buildIndexSignatureDisplay(IndexInfoOfKind, writer, kind, enclosingDeclaration);
|
||||
const result = writer.string();
|
||||
releaseStringWriter(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function pushAction(result: CodeAction[], insertion: string, description: string): void {
|
||||
const newAction: CodeAction = {
|
||||
description: description,
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
}]
|
||||
};
|
||||
result.push(newAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
-23
@@ -1,28 +1,5 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) {
|
||||
// First token is the open curly, this is where we want to put the 'super' call.
|
||||
return constructor.body.getFirstToken(sourceFile).getEnd();
|
||||
}
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
|
||||
if (token.kind !== SyntaxKind.ConstructorKeyword) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>token.parent, sourceFile);
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call),
|
||||
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }]
|
||||
}];
|
||||
}
|
||||
});
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
@@ -0,0 +1,20 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
|
||||
if (token.kind !== SyntaxKind.ConstructorKeyword) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>token.parent, sourceFile);
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call),
|
||||
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }]
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
const classDeclNode = getContainingClass(token);
|
||||
if (!(token.kind === SyntaxKind.Identifier && isClassLike(classDeclNode))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const heritageClauses = classDeclNode.heritageClauses;
|
||||
if (!(heritageClauses && heritageClauses.length > 0)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const extendsToken = heritageClauses[0].getFirstToken();
|
||||
if (!(extendsToken && extendsToken.kind === SyntaxKind.ExtendsKeyword)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let changeStart = extendsToken.getStart(sourceFile);
|
||||
let changeEnd = extendsToken.getEnd();
|
||||
const textChanges: TextChange[] = [{ newText: " implements", span: { start: changeStart, length: changeEnd - changeStart } }];
|
||||
|
||||
// We replace existing keywords with commas.
|
||||
for (let i = 1; i < heritageClauses.length; i++) {
|
||||
const keywordToken = heritageClauses[i].getFirstToken();
|
||||
if (keywordToken) {
|
||||
changeStart = keywordToken.getStart(sourceFile);
|
||||
changeEnd = keywordToken.getEnd();
|
||||
textChanges.push({ newText: ",", span: { start: changeStart, length: changeEnd - changeStart } });
|
||||
}
|
||||
}
|
||||
|
||||
const result = [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: textChanges
|
||||
}]
|
||||
}];
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
///<reference path='superFixes.ts' />
|
||||
///<reference path='importFixes.ts' />
|
||||
///<reference path='unusedIdentifierFixes.ts' />
|
||||
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
|
||||
/// <reference path="fixClassDoesntImplementInheritedAbstractMember.ts" />
|
||||
/// <reference path="fixClassSuperMustPrecedeThisAccess.ts" />
|
||||
/// <reference path="fixConstructorForDerivedNeedSuperCall.ts" />
|
||||
/// <reference path="fixExtendsInterfaceBecomesImplements.ts" />
|
||||
/// <reference path='unusedIdentifierFixes.ts' />
|
||||
/// <reference path='importFixes.ts' />
|
||||
/// <reference path='helpers.ts' />
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
|
||||
/**
|
||||
* Finds members of the resolved type that are missing in the class pointed to by class decl
|
||||
* and generates source code for the missing members.
|
||||
* @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for.
|
||||
* @returns Empty string iff there are no member insertions.
|
||||
*/
|
||||
export function getMissingMembersInsertion(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker, newlineChar: string): string {
|
||||
const classMembers = classDeclaration.symbol.members;
|
||||
const missingMembers = possiblyMissingSymbols.filter(symbol => !classMembers.has(symbol.getName()));
|
||||
|
||||
let insertion = "";
|
||||
|
||||
for (const symbol of missingMembers) {
|
||||
insertion = insertion.concat(getInsertionForMemberSymbol(symbol, classDeclaration, checker, newlineChar));
|
||||
}
|
||||
return insertion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`.
|
||||
*/
|
||||
function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string {
|
||||
// const name = symbol.getName();
|
||||
const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
|
||||
const declarations = symbol.getDeclarations();
|
||||
if (!(declarations && declarations.length)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const declaration = declarations[0] as Declaration;
|
||||
const name = declaration.name ? declaration.name.getText() : undefined;
|
||||
const visibility = getVisibilityPrefix(getModifierFlags(declaration));
|
||||
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
const typeString = checker.typeToString(type, enclosingDeclaration, TypeFormatFlags.None);
|
||||
return `${visibility}${name}: ${typeString};${newlineChar}`;
|
||||
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// The signature for the implementation appears as an entry in `signatures` iff
|
||||
// there is only one signature.
|
||||
// If there are overloads and an implementation signature, it appears as an
|
||||
// extra declaration that isn't a signature for `type`.
|
||||
// If there is more than one overload but no implementation signature
|
||||
// (eg: an abstract method or interface declaration), there is a 1-1
|
||||
// correspondence of declarations and signatures.
|
||||
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
|
||||
if (!(signatures && signatures.length > 0)) {
|
||||
return "";
|
||||
}
|
||||
if (declarations.length === 1) {
|
||||
Debug.assert(signatures.length === 1);
|
||||
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
}
|
||||
|
||||
let result = "";
|
||||
for (let i = 0; i < signatures.length; i++) {
|
||||
const sigString = checker.signatureToString(signatures[i], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString};${newlineChar}`;
|
||||
}
|
||||
|
||||
// If there is a declaration with a body, it is the last declaration,
|
||||
// and it isn't caught by `getSignaturesOfType`.
|
||||
let bodySig: Signature | undefined = undefined;
|
||||
if (declarations.length > signatures.length) {
|
||||
bodySig = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration);
|
||||
}
|
||||
else {
|
||||
Debug.assert(declarations.length === signatures.length);
|
||||
bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker);
|
||||
}
|
||||
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
|
||||
return result;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function createBodySignatureWithAnyTypes(signatures: Signature[], enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Signature {
|
||||
const newSignatureDeclaration = createNode(SyntaxKind.CallSignature) as SignatureDeclaration;
|
||||
newSignatureDeclaration.parent = enclosingDeclaration;
|
||||
newSignatureDeclaration.name = signatures[0].getDeclaration().name;
|
||||
|
||||
let maxNonRestArgs = -1;
|
||||
let maxArgsIndex = 0;
|
||||
let minArgumentCount = signatures[0].minArgumentCount;
|
||||
let hasRestParameter = false;
|
||||
for (let i = 0; i < signatures.length; i++) {
|
||||
const sig = signatures[i];
|
||||
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
|
||||
hasRestParameter = hasRestParameter || sig.hasRestParameter;
|
||||
const nonRestLength = sig.parameters.length - (sig.hasRestParameter ? 1 : 0);
|
||||
if (nonRestLength > maxNonRestArgs) {
|
||||
maxNonRestArgs = nonRestLength;
|
||||
maxArgsIndex = i;
|
||||
}
|
||||
}
|
||||
const maxArgsParameterSymbolNames = signatures[maxArgsIndex].getParameters().map(symbol => symbol.getName());
|
||||
|
||||
const optionalToken = createToken(SyntaxKind.QuestionToken);
|
||||
|
||||
newSignatureDeclaration.parameters = createNodeArray<ParameterDeclaration>();
|
||||
for (let i = 0; i < maxNonRestArgs; i++) {
|
||||
const newParameter = createParameterDeclarationWithoutType(i, minArgumentCount, newSignatureDeclaration);
|
||||
newSignatureDeclaration.parameters.push(newParameter);
|
||||
}
|
||||
|
||||
if (hasRestParameter) {
|
||||
const restParameter = createParameterDeclarationWithoutType(maxNonRestArgs, minArgumentCount, newSignatureDeclaration);
|
||||
restParameter.dotDotDotToken = createToken(SyntaxKind.DotDotDotToken);
|
||||
newSignatureDeclaration.parameters.push(restParameter);
|
||||
}
|
||||
|
||||
return checker.getSignatureFromDeclaration(newSignatureDeclaration);
|
||||
|
||||
function createParameterDeclarationWithoutType(index: number, minArgCount: number, enclosingSignatureDeclaration: SignatureDeclaration): ParameterDeclaration {
|
||||
const newParameter = createNode(SyntaxKind.Parameter) as ParameterDeclaration;
|
||||
|
||||
newParameter.symbol = new SymbolConstructor(SymbolFlags.FunctionScopedVariable, maxArgsParameterSymbolNames[index] || "rest");
|
||||
newParameter.symbol.valueDeclaration = newParameter;
|
||||
newParameter.symbol.declarations = [newParameter];
|
||||
newParameter.parent = enclosingSignatureDeclaration;
|
||||
if (index >= minArgCount) {
|
||||
newParameter.questionToken = optionalToken;
|
||||
}
|
||||
|
||||
return newParameter;
|
||||
}
|
||||
}
|
||||
|
||||
function getMethodBodyStub(newLineChar: string) {
|
||||
return ` {${newLineChar}throw new Error('Method not implemented.');${newLineChar}}${newLineChar}`;
|
||||
}
|
||||
|
||||
function getVisibilityPrefix(flags: ModifierFlags): string {
|
||||
if (flags & ModifierFlags.Public) {
|
||||
return "public ";
|
||||
}
|
||||
else if (flags & ModifierFlags.Protected) {
|
||||
return "protected ";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
const SymbolConstructor = objectAllocator.getSymbolConstructor();
|
||||
}
|
||||
@@ -14,20 +14,21 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
class ImportCodeActionMap {
|
||||
private symbolIdToActionMap = createMap<ImportCodeAction[]>();
|
||||
private symbolIdToActionMap: ImportCodeAction[][] = [];
|
||||
|
||||
addAction(symbolId: number, newAction: ImportCodeAction) {
|
||||
if (!newAction) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.symbolIdToActionMap[symbolId]) {
|
||||
const actions = this.symbolIdToActionMap[symbolId];
|
||||
if (!actions) {
|
||||
this.symbolIdToActionMap[symbolId] = [newAction];
|
||||
return;
|
||||
}
|
||||
|
||||
if (newAction.kind === "CodeChange") {
|
||||
this.symbolIdToActionMap[symbolId].push(newAction);
|
||||
actions.push(newAction);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ namespace ts.codefix {
|
||||
// than another existing one. For example, you may have new imports from "./foo/bar"
|
||||
// and "bar", when the new one is "bar/bar2" and the current one is "./foo/bar". The new
|
||||
// one and the current one are not comparable (one relative path and one absolute path),
|
||||
// but the new one is worse than the other one, so should not add to the list.
|
||||
// but the new one is worse than the other one, so should not add to the list.
|
||||
updatedNewImports.push(existingAction);
|
||||
break;
|
||||
case ModuleSpecifierComparison.Worse:
|
||||
@@ -73,8 +74,8 @@ namespace ts.codefix {
|
||||
|
||||
getAllActions() {
|
||||
let result: ImportCodeAction[] = [];
|
||||
for (const symbolId in this.symbolIdToActionMap) {
|
||||
result = concatenate(result, this.symbolIdToActionMap[symbolId]);
|
||||
for (const key in this.symbolIdToActionMap) {
|
||||
result = concatenate(result, this.symbolIdToActionMap[key])
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -112,7 +113,10 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Cannot_find_name_0.code],
|
||||
errorCodes: [
|
||||
Diagnostics.Cannot_find_name_0.code,
|
||||
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
||||
],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const checker = context.program.getTypeChecker();
|
||||
@@ -124,9 +128,15 @@ namespace ts.codefix {
|
||||
const symbolIdActionMap = new ImportCodeActionMap();
|
||||
|
||||
// this is a module id -> module import declaration map
|
||||
const cachedImportDeclarations = createMap<(ImportDeclaration | ImportEqualsDeclaration)[]>();
|
||||
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
||||
let cachedNewImportInsertPosition: number;
|
||||
|
||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
|
||||
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
|
||||
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
|
||||
}
|
||||
|
||||
const allPotentialModules = checker.getAmbientModules();
|
||||
for (const otherSourceFile of allSourceFiles) {
|
||||
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
|
||||
@@ -134,7 +144,6 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||
for (const moduleSymbol of allPotentialModules) {
|
||||
context.cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@@ -145,7 +154,7 @@ namespace ts.codefix {
|
||||
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||
// check if this symbol is already used
|
||||
const symbolId = getUniqueSymbolId(localSymbol);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefaultExport*/ true));
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,8 +171,9 @@ namespace ts.codefix {
|
||||
function getImportDeclarations(moduleSymbol: Symbol) {
|
||||
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
|
||||
|
||||
if (cachedImportDeclarations[moduleSymbolId]) {
|
||||
return cachedImportDeclarations[moduleSymbolId];
|
||||
const cached = cachedImportDeclarations[moduleSymbolId];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
||||
@@ -203,7 +213,7 @@ namespace ts.codefix {
|
||||
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
|
||||
}
|
||||
|
||||
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean): ImportCodeAction[] {
|
||||
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
|
||||
const existingDeclarations = getImportDeclarations(moduleSymbol);
|
||||
if (existingDeclarations.length > 0) {
|
||||
// With an existing import statement, there are more than one actions the user can do.
|
||||
@@ -213,8 +223,6 @@ namespace ts.codefix {
|
||||
return [getCodeActionForNewImport()];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
|
||||
const actions: ImportCodeAction[] = [];
|
||||
|
||||
@@ -262,7 +270,7 @@ namespace ts.codefix {
|
||||
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
|
||||
}
|
||||
|
||||
if (namedImportDeclaration && namedImportDeclaration.importClause &&
|
||||
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
|
||||
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
|
||||
/**
|
||||
* If the existing import declaration already has a named import list, just
|
||||
@@ -386,7 +394,9 @@ namespace ts.codefix {
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
|
||||
const importStatementText = isDefault
|
||||
? `import ${name} from "${moduleSpecifierWithoutQuotes}"`
|
||||
: `import { ${name} } from "${moduleSpecifierWithoutQuotes}"`;
|
||||
: isNamespaceImport
|
||||
? `import * as ${name} from "${moduleSpecifierWithoutQuotes}"`
|
||||
: `import { ${name} } from "${moduleSpecifierWithoutQuotes}"`;
|
||||
|
||||
// if this file doesn't have any import statements, insert an import statement and then insert a new line
|
||||
// between the only import statement and user code. Otherwise just insert the statement because chances
|
||||
@@ -412,10 +422,10 @@ namespace ts.codefix {
|
||||
const options = context.program.getCompilerOptions();
|
||||
|
||||
return tryGetModuleNameFromAmbientModule() ||
|
||||
tryGetModuleNameFromBaseUrl() ||
|
||||
tryGetModuleNameFromRootDirs() ||
|
||||
tryGetModuleNameFromTypeRoots() ||
|
||||
tryGetModuleNameAsNodeModule() ||
|
||||
tryGetModuleNameFromBaseUrl() ||
|
||||
tryGetModuleNameFromRootDirs() ||
|
||||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
|
||||
|
||||
function tryGetModuleNameFromAmbientModule(): string {
|
||||
@@ -435,6 +445,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const relativeNameWithIndex = removeFileExtension(relativeName);
|
||||
relativeName = removeExtensionAndIndexPostFix(relativeName);
|
||||
|
||||
if (options.paths) {
|
||||
@@ -454,7 +465,7 @@ namespace ts.codefix {
|
||||
return key.replace("\*", matchedStar);
|
||||
}
|
||||
}
|
||||
else if (pattern === relativeName) {
|
||||
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -483,7 +494,7 @@ namespace ts.codefix {
|
||||
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
|
||||
for (const typeRoot of normalizedTypeRoots) {
|
||||
if (startsWith(moduleFileName, typeRoot)) {
|
||||
let relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
||||
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
||||
return removeExtensionAndIndexPostFix(relativeFileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace ts.codefix {
|
||||
|
||||
function createCodeFix(newText: string, start: number, length: number): CodeAction[] {
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Remove_unused_identifiers),
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), { 0: token.getText() }),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{ newText, span: { start, length } }]
|
||||
|
||||
+640
-641
File diff suppressed because it is too large
Load Diff
+579
-599
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
namespace ts {
|
||||
namespace ts {
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
@@ -113,16 +113,16 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let bucket = buckets[key];
|
||||
let bucket = buckets.get(key);
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
|
||||
buckets.set(key, bucket = createFileMap<DocumentRegistryEntry>());
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
function reportStats() {
|
||||
const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets[name];
|
||||
const bucketInfoArray = arrayFrom(buckets.keys()).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets.get(name);
|
||||
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
entries.forEachValue((key, entry) => {
|
||||
sourceFiles.push({
|
||||
|
||||
+1084
-1066
File diff suppressed because it is too large
Load Diff
@@ -488,18 +488,32 @@ namespace ts.formatting {
|
||||
// open and close brace, 'else' and 'while' (in do statement) tokens has indentation of the parent
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.CloseParenToken:
|
||||
case SyntaxKind.ElseKeyword:
|
||||
case SyntaxKind.WhileKeyword:
|
||||
case SyntaxKind.AtToken:
|
||||
return indentation;
|
||||
default:
|
||||
// if token line equals to the line of containing node (this is a first token in the node) - use node indentation
|
||||
return nodeStartLine !== line ? indentation + getEffectiveDelta(delta, container) : indentation;
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.GreaterThanToken: {
|
||||
if (container.kind === SyntaxKind.JsxOpeningElement ||
|
||||
container.kind === SyntaxKind.JsxClosingElement ||
|
||||
container.kind === SyntaxKind.JsxSelfClosingElement
|
||||
) {
|
||||
return indentation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.CloseBracketToken: {
|
||||
if (container.kind !== SyntaxKind.MappedType) {
|
||||
return indentation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if token line equals to the line of containing node (this is a first token in the node) - use node indentation
|
||||
return nodeStartLine !== line ? indentation + getEffectiveDelta(delta, container) : indentation;
|
||||
},
|
||||
getIndentation: () => indentation,
|
||||
getDelta: child => getEffectiveDelta(delta, child),
|
||||
@@ -566,7 +580,7 @@ namespace ts.formatting {
|
||||
if (tokenInfo.token.end > node.end) {
|
||||
break;
|
||||
}
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation, node);
|
||||
}
|
||||
|
||||
function processChildNode(
|
||||
@@ -617,7 +631,7 @@ namespace ts.formatting {
|
||||
break;
|
||||
}
|
||||
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation, node);
|
||||
}
|
||||
|
||||
if (!formattingScanner.isOnToken()) {
|
||||
@@ -673,11 +687,11 @@ namespace ts.formatting {
|
||||
computeIndentation(tokenInfo.token, startLine, Constants.Unknown, parent, parentDynamicIndentation, parentStartLine);
|
||||
|
||||
listDynamicIndentation = getDynamicIndentation(parent, parentStartLine, indentation.indentation, indentation.delta);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation, parent);
|
||||
}
|
||||
else {
|
||||
// consume any tokens that precede the list as child elements of 'node' using its indentation scope
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, parentDynamicIndentation);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, parentDynamicIndentation, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -697,13 +711,13 @@ namespace ts.formatting {
|
||||
// without this check close paren will be interpreted as list end token for function expression which is wrong
|
||||
if (tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) {
|
||||
// consume list end token
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function consumeTokenAndAdvanceScanner(currentTokenInfo: TokenInfo, parent: Node, dynamicIndentation: DynamicIndentation, container?: Node): void {
|
||||
function consumeTokenAndAdvanceScanner(currentTokenInfo: TokenInfo, parent: Node, dynamicIndentation: DynamicIndentation, container: Node): void {
|
||||
Debug.assert(rangeContainsRange(parent, currentTokenInfo.token));
|
||||
|
||||
const lastTriviaWasNewLine = formattingScanner.lastTrailingTriviaWasNewLine();
|
||||
@@ -886,12 +900,25 @@ namespace ts.formatting {
|
||||
else {
|
||||
const tokenStart = sourceFile.getLineAndCharacterOfPosition(pos);
|
||||
const startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
|
||||
if (indentation !== tokenStart.character || indentationIsDifferent(indentationString, startLinePosition)) {
|
||||
if (indentation !== characterToColumn(startLinePosition, tokenStart.character) || indentationIsDifferent(indentationString, startLinePosition)) {
|
||||
recordReplace(startLinePosition, tokenStart.character, indentationString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function characterToColumn(startLinePosition: number, characterInLine: number): number {
|
||||
let column = 0;
|
||||
for (let i = 0; i < characterInLine; i++) {
|
||||
if (sourceFile.text.charCodeAt(startLinePosition + i) === CharacterCodes.tab) {
|
||||
column += options.tabSize - column % options.tabSize;
|
||||
}
|
||||
else {
|
||||
column++;
|
||||
}
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
function indentationIsDifferent(indentationString: string, startLinePosition: number): boolean {
|
||||
return indentationString !== sourceFile.text.substr(startLinePosition, indentationString.length);
|
||||
}
|
||||
@@ -937,7 +964,7 @@ namespace ts.formatting {
|
||||
|
||||
// shift all parts on the delta size
|
||||
const delta = indentation - nonWhitespaceColumnInFirstPart.column;
|
||||
for (let i = startIndex, len = parts.length; i < len; i++ , startLine++) {
|
||||
for (let i = startIndex; i < parts.length; i++ , startLine++) {
|
||||
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
|
||||
const nonWhitespaceCharacterAndColumn =
|
||||
i === 0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user