diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f33a86ec89..093c5186d7c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2220,7 +2220,22 @@ namespace ts { } if (moduleNotFoundError) { - // report errors only if it was requested + // For relative paths, see if this was possibly a projectReference redirect + if (pathIsRelative(moduleReference)) { + const sourceFile = getSourceFileOfNode(location); + const redirects = sourceFile.redirectedReferences; + if (redirects) { + const normalizedTargetPath = getNormalizedAbsolutePath(moduleReference, getDirectoryPath(sourceFile.fileName)); + for (const ext of [Extension.Ts, Extension.Tsx]) { + const probePath = normalizedTargetPath + ext; + if (redirects.indexOf(probePath) >= 0) { + error(errorNode, Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, moduleReference, probePath); + return undefined; + } + } + } + } + if (resolutionDiagnostic) { error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } @@ -5641,6 +5656,10 @@ namespace ts { const symbol = type.symbol; const members = getMembersOfSymbol(symbol); (type).declaredProperties = getNamedMembers(members); + // Start with signatures at empty array in case of recursive types + (type).declaredCallSignatures = emptyArray; + (type).declaredConstructSignatures = emptyArray; + (type).declaredCallSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); (type).declaredConstructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); @@ -7033,7 +7052,7 @@ namespace ts { for (let i = numTypeArguments; i < numTypeParameters; i++) { const mapper = createTypeMapper(typeParameters, typeArguments); let defaultType = getDefaultFromTypeParameter(typeParameters[i]); - if (defaultType && isTypeIdenticalTo(defaultType, emptyObjectType) && isJavaScriptImplicitAny) { + if (isJavaScriptImplicitAny && defaultType && isTypeIdenticalTo(defaultType, emptyObjectType)) { defaultType = anyType; } typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScriptImplicitAny); @@ -10740,7 +10759,7 @@ namespace ts { } } const constraint = getConstraintForRelation(source); - if (!constraint || constraint.flags & TypeFlags.Any) { + if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { // A type variable with no constraint is not related to the non-primitive object type. if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { errorInfo = saveErrorInfo; @@ -22260,10 +22279,6 @@ namespace ts { function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray, addDiagnostic: AddUnusedDiagnostic) { for (const node of potentiallyUnusedIdentifiers) { switch (node.kind) { - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - checkUnusedModuleMembers(node, addDiagnostic); - break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: checkUnusedClassMembers(node, addDiagnostic); @@ -22272,6 +22287,8 @@ namespace ts { case SyntaxKind.InterfaceDeclaration: checkUnusedTypeParameters(node, addDiagnostic); break; + case SyntaxKind.SourceFile: + case SyntaxKind.ModuleDeclaration: case SyntaxKind.Block: case SyntaxKind.CaseBlock: case SyntaxKind.ForStatement: @@ -22305,35 +22322,6 @@ namespace ts { } } - function checkUnusedLocalsAndParameters(node: Node, addDiagnostic: AddUnusedDiagnostic): void { - if (!(node.flags & NodeFlags.Ambient)) { - node.locals.forEach(local => { - // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. - // If it's a type parameter merged with a parameter, check if the parameter-side is used. - if (local.flags & SymbolFlags.TypeParameter ? (local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : !local.isReferenced) { - if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { - const parameter = getRootDeclaration(local.valueDeclaration); - const name = getNameOfDeclaration(local.valueDeclaration); - if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) { - addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); - } - } - else { - forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local), addDiagnostic)); - } - } - }); - } - } - - function isRemovedPropertyFromObjectSpread(node: Node) { - if (isBindingElement(node) && isObjectBindingPattern(node.parent)) { - const lastElement = lastOrUndefined(node.parent.elements); - return lastElement !== node && !!lastElement.dotDotDotToken; - } - return false; - } - function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) { const node = getNameOfDeclaration(declaration) || declaration; if (isIdentifierThatStartsWithUnderScore(node)) { @@ -22344,10 +22332,8 @@ namespace ts { } } - if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) { - const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read; - addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name)); - } + const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read; + addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name)); } function parameterNameStartsWithUnderscore(parameterName: DeclarationName) { @@ -22409,44 +22395,86 @@ namespace ts { } } - function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile, addDiagnostic: AddUnusedDiagnostic): void { - if (!(node.flags & NodeFlags.Ambient)) { - // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value. - const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>(); - node.locals.forEach(local => { - if (local.isReferenced || local.exportSymbol) return; - for (const declaration of local.declarations) { - if (isAmbientModule(declaration)) continue; - if (isImportedDeclaration(declaration)) { - const importClause = importClauseFromImported(declaration); - const key = String(getNodeId(importClause)); - const group = unusedImports.get(key); - if (group) { - group[1].push(declaration); - } - else { - unusedImports.set(key, [importClause, [declaration]]); + function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { + const keyString = String(getKey(key)); + const group = map.get(keyString); + if (group) { + group[1].push(value); + } + else { + map.set(keyString, [key, [value]]); + } + } + + function tryGetRootParameterDeclaration(node: Node): ParameterDeclaration | undefined { + return tryCast(getRootDeclaration(node), isParameter); + } + + function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void { + if (nodeWithLocals.flags & NodeFlags.Ambient) return; + + // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value. + const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>(); + const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>(); + nodeWithLocals.locals.forEach(local => { + // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. + // If it's a type parameter merged with a parameter, check if the parameter-side is used. + if (local.flags & SymbolFlags.TypeParameter ? !(local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : local.isReferenced || local.exportSymbol) { + return; + } + + for (const declaration of local.declarations) { + if (isAmbientModule(declaration)) continue; + if (isImportedDeclaration(declaration)) { + addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); + } + else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { + // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. + const lastElement = last(declaration.parent.elements); + if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) { + addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); + } + } + else { + const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration); + if (parameter) { + const name = getNameOfDeclaration(local.valueDeclaration); + if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) { + addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); } } else { errorUnusedLocal(declaration, symbolName(local), addDiagnostic); } } - }); - - unusedImports.forEach(([importClause, unuseds]) => { - const importDecl = importClause.parent; - if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) { - for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic); + } + }); + unusedImports.forEach(([importClause, unuseds]) => { + const importDecl = importClause.parent; + if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) { + for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic); + } + else if (unuseds.length === 1) { + addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name))); + } + else { + addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused)); + } + }); + unusedDestructures.forEach(([bindingPattern, bindingElements]) => { + const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local; + if (!bindingPattern.elements.every(e => contains(bindingElements, e))) { + for (const e of bindingElements) { + addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(e))); } - else if (unuseds.length === 1) { - addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name))); - } - else { - addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl))); - } - }); - } + } + else if (bindingElements.length === 1) { + addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(first(bindingElements)))); + } + else { + addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused)); + } + }); } type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport; @@ -28026,7 +28054,7 @@ namespace ts { function checkGrammarImportCallExpression(node: ImportCall): boolean { if (moduleKind === ModuleKind.ES2015) { - return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules); + return grammarErrorOnNode(node, Diagnostics.Dynamic_import_is_only_supported_when_module_flag_is_commonjs_or_esNext); } if (node.typeArguments) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 5e776086773..4788361a9d3 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -248,6 +248,13 @@ namespace ts { category: Diagnostics.Basic_Options, description: Diagnostics.Specify_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir, }, + { + name: "composite", + type: "boolean", + isTSConfigOnly: true, + category: Diagnostics.Basic_Options, + description: Diagnostics.Enable_project_compilation, + }, { name: "removeComments", type: "boolean", @@ -827,12 +834,14 @@ namespace ts { export function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine { const options: CompilerOptions = {}; const fileNames: string[] = []; + const projectReferences: ProjectReference[] | undefined = undefined; const errors: Diagnostic[] = []; parseStrings(commandLine); return { options, fileNames, + projectReferences, errors }; @@ -946,6 +955,49 @@ namespace ts { return optionNameMap.get(optionName); } + + export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + export interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { + let configFileText: string; + try { + configFileText = host.readFile(configFileName); + } + catch (e) { + const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; + } + if (!configFileText) { + const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; + } + + const result = parseJsonText(configFileName, configFileText); + const cwd = host.getCurrentDirectory(); + return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); + } + /** * Read tsconfig.json file * @param fileName The path to the config file @@ -1021,6 +1073,14 @@ namespace ts { name: "extends", type: "string" }, + { + name: "references", + type: "list", + element: { + name: "references", + type: "object" + } + }, { name: "files", type: "list", @@ -1428,7 +1488,7 @@ namespace ts { for (let i = 0; i < nameColumn.length; i++) { const optionName = nameColumn[i]; const description = descriptionColumn[i]; - result.push(optionName && `${tab}${tab}${optionName}${ description && (makePadding(marginLength - optionName.length + 2) + description)}`); + result.push(optionName && `${tab}${tab}${optionName}${description && (makePadding(marginLength - optionName.length + 2) + description)}`); } if (fileNames.length) { result.push(`${tab}},`); @@ -1512,12 +1572,13 @@ namespace ts { const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors); const { raw } = parsedConfig; const options = extend(existingOptions, parsedConfig.options || {}); - options.configFilePath = configFileName; + options.configFilePath = configFileName && normalizeSlashes(configFileName); setConfigFileInOptions(options, sourceFile); - const { fileNames, wildcardDirectories, spec } = getFileNames(); + const { fileNames, wildcardDirectories, spec, projectReferences } = getFileNames(); return { options, fileNames, + projectReferences, typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), raw, errors, @@ -1571,10 +1632,33 @@ namespace ts { } const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile); - if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) { + if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0 && !hasProperty(raw, "references")) { errors.push(getErrorForNoInputFiles(result.spec, configFileName)); } + if (hasProperty(raw, "references") && !isNullOrUndefined(raw.references)) { + if (isArray(raw.references)) { + const references: ProjectReference[] = []; + for (const ref of raw.references) { + if (typeof ref.path !== "string") { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); + } + else { + references.push({ + path: getNormalizedAbsolutePath(ref.path, basePath), + originalPath: ref.path, + prepend: ref.prepend, + circular: ref.circular + }); + } + } + result.projectReferences = references; + } + else { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "references", "Array"); + } + } + return result; } @@ -1863,6 +1947,9 @@ namespace ts { const options = getDefaultCompilerOptions(configFileName); convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors); + if (configFileName) { + options.configFilePath = normalizeSlashes(configFileName); + } return options; } @@ -2061,7 +2148,7 @@ namespace ts { // new entries in these paths. const wildcardDirectories = getWildcardDirectories(validatedIncludeSpecs, validatedExcludeSpecs, basePath, host.useCaseSensitiveFileNames); - const spec: ConfigFileSpecs = { filesSpecs, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories }; + const spec: ConfigFileSpecs = { filesSpecs, referencesSpecs: undefined, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories }; return getFileNamesFromConfigSpecs(spec, basePath, options, host, extraFileExtensions); } @@ -2132,8 +2219,16 @@ namespace ts { const literalFiles = arrayFrom(literalFileMap.values()); const wildcardFiles = arrayFrom(wildcardFileMap.values()); + const projectReferences = spec.referencesSpecs && spec.referencesSpecs.map((r): ProjectReference => { + return { + ...r, + path: getNormalizedAbsolutePath(r.path, basePath) + }; + }); + return { fileNames: literalFiles.concat(wildcardFiles), + projectReferences, wildcardDirectories, spec }; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 06660bacc85..ec95d6be17e 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2068,6 +2068,10 @@ namespace ts { : moduleKind === ModuleKind.System; } + export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean { + return !!(compilerOptions.declaration || compilerOptions.composite); + } + export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict"; export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { @@ -2328,6 +2332,7 @@ namespace ts { const reduced = [components[0]]; for (let i = 1; i < components.length; i++) { const component = components[i]; + if (!component) continue; if (component === ".") continue; if (component === "..") { if (reduced.length > 1) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7b3a9733599..783579e2232 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -895,7 +895,7 @@ "category": "Error", "code": 1322 }, - "Dynamic import cannot be used when targeting ECMAScript 2015 modules.": { + "Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'.": { "category": "Error", "code": 1323 }, @@ -3559,6 +3559,48 @@ "category": "Message", "code": 6197 }, + "All destructured elements are unused.": { + "category": "Error", + "code": 6198, + "reportsUnnecessary": true + }, + + "Projects to reference": { + "category": "Message", + "code": 6300 + }, + "Enable project compilation": { + "category": "Message", + "code": 6302 + }, + "Project references may not form a circular graph. Cycle detected: {0}": { + "category": "Error", + "code": 6202 + }, + "Composite projects may not disable declaration emit.": { + "category": "Error", + "code": 6304 + }, + "Output file '{0}' has not been built from source file '{1}'.": { + "category": "Error", + "code": 6305 + }, + "Referenced project '{0}' must have setting \"composite\": true.": { + "category": "Error", + "code": 6306 + }, + "File '{0}' is not in project file list. Projects must list all files or use an 'include' pattern.": { + "category": "Error", + "code": 6307 + }, + "Cannot prepend project '{0}' because it does not have 'outFile' set": { + "category": "Error", + "code": 6308 + }, + "Output file '{0}' from project '{1}' does not exist": { + "category": "Error", + "code": 6309 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", @@ -3956,6 +3998,10 @@ "category": "Message", "code": 90008 }, + "Remove destructuring": { + "category": "Message", + "code": 90009 + }, "Import '{0}' from module \"{1}\"": { "category": "Message", "code": 90013 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 0786e849495..b4ff2617a0d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,4 +1,5 @@ namespace ts { + const infoExtension = ".tsbundleinfo"; const brackets = createBracketsMap(); /*@internal*/ @@ -15,12 +16,11 @@ namespace ts { host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) => T, sourceFilesOrTargetSourceFile?: ReadonlyArray | SourceFile, emitOnlyDtsFiles?: boolean) { - const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); const options = host.getCompilerOptions(); if (options.outFile || options.out) { if (sourceFiles.length) { - const bundle = createBundle(sourceFiles); + const bundle = createBundle(sourceFiles, host.getPrependNodes()); const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle); if (result) { return result; @@ -45,7 +45,8 @@ namespace ts { const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = (forceDtsPaths || options.declaration) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined; const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }; + const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + infoExtension); + return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }; } else { const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); @@ -54,7 +55,7 @@ namespace ts { const isJs = isSourceFileJavaScript(sourceFile); const declarationFilePath = ((forceDtsPaths || options.declaration) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }; + return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath: undefined }; } } @@ -62,6 +63,13 @@ namespace ts { return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined; } + function createDefaultBundleInfo(): BundleInfo { + return { + originalOffset: -1, + totalLength: -1 + }; + } + // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve @@ -87,7 +95,7 @@ namespace ts { /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature - export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { + export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[], declarationTransformers?: TransformerFactory[]): EmitResult { const compilerOptions = host.getCompilerOptions(); const sourceMapDataList: SourceMapData[] = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; @@ -103,6 +111,7 @@ namespace ts { // Explicitly do not passthru either `inline` option }); + let bundleInfo: BundleInfo = createDefaultBundleInfo(); let emitSkipped = false; // Emit each output file @@ -118,8 +127,8 @@ namespace ts { sourceMaps: sourceMapDataList }; - function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { - emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath); + function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, bundleInfoPath); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath); if (!emitSkipped && emittedFilesList) { @@ -132,11 +141,13 @@ namespace ts { if (declarationFilePath) { emittedFilesList.push(declarationFilePath); } + if (bundleInfoPath) { + emittedFilesList.push(bundleInfoPath); + } } } - function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string) { - const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; + function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined) { // Make sure not to write js file and source map file if any of them cannot be written if (host.isEmitBlocked(jsFilePath) || compilerOptions.noEmit || compilerOptions.emitDeclarationOnly) { emitSkipped = true; @@ -146,7 +157,7 @@ namespace ts { return; } // Transform the source files - const transform = transformNodes(resolver, host, compilerOptions, sourceFiles, transformers, /*allowDtsFiles*/ false); + const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], transformers, /*allowDtsFiles*/ false); // Create a printer to print the nodes const printer = createPrinter({ ...compilerOptions, noEmitHelpers: compilerOptions.noEmitHelpers } as PrinterOptions, { @@ -166,7 +177,8 @@ namespace ts { onSetSourceFile: setSourceFile, }); - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, isSourceFile(sourceFileOrBundle) ? transform.transformed[0] : createBundle(transform.transformed), printer, sourceMap); + Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform"); + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform.transformed[0], bundleInfoPath, printer, sourceMap); // Clean up emit nodes on parse tree transform.dispose(); @@ -179,8 +191,8 @@ namespace ts { const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; // Setup and perform the transformation to retrieve declarations from the input files const nonJsFiles = filter(sourceFiles, isSourceFileNotJavaScript); - const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles)] : nonJsFiles; - const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, [transformDeclarations], /*allowDtsFiles*/ false); + const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : nonJsFiles; + const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, concatenate([transformDeclarations], declarationTransformers), /*allowDtsFiles*/ false); if (length(declarationTransform.diagnostics)) { for (const diagnostic of declarationTransform.diagnostics) { emitterDiagnostics.add(diagnostic); @@ -203,19 +215,20 @@ namespace ts { const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit; emitSkipped = emitSkipped || declBlocked; if (!declBlocked || emitOnlyDtsFiles) { - printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], declarationPrinter, declarationSourceMap); + Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform"); + printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], /* bundleInfopath*/ undefined, declarationPrinter, declarationSourceMap); } declarationTransform.dispose(); } - function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapRecorder: SourceMapWriter) { + function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, bundleInfoPath: string | undefined, printer: Printer, mapRecorder: SourceMapWriter) { const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile]; mapRecorder.initialize(jsFilePath, sourceMapFilePath || "", sourceFileOrBundle, sourceMapDataList); if (bundle) { - printer.writeBundle(bundle, writer); + printer.writeBundle(bundle, writer, bundleInfo); } else { printer.writeFile(sourceFile, writer); @@ -236,9 +249,17 @@ namespace ts { // Write the output file writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM, sourceFiles); + // Write bundled offset information if applicable + if (bundleInfoPath) { + bundleInfo.totalLength = writer.getTextPos(); + writeFile(host, emitterDiagnostics, bundleInfoPath, JSON.stringify(bundleInfo, undefined, 2), /*writeByteOrderMark*/ false); + } + // Reset state mapRecorder.reset(); writer.clear(); + + bundleInfo = createDefaultBundleInfo(); } function setSourceFile(node: SourceFile) { @@ -335,6 +356,7 @@ namespace ts { switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); case SyntaxKind.Bundle: return printBundle(node); + case SyntaxKind.UnparsedSource: return printUnparsedSource(node); } writeNode(hint, node, sourceFile, beginPrint()); return endPrint(); @@ -355,6 +377,11 @@ namespace ts { return endPrint(); } + function printUnparsedSource(unparsed: UnparsedSource): string { + writeUnparsedSource(unparsed, beginPrint()); + return endPrint(); + } + /** * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. */ @@ -379,7 +406,7 @@ namespace ts { writer = previousWriter; } - function writeBundle(bundle: Bundle, output: EmitTextWriter) { + function writeBundle(bundle: Bundle, output: EmitTextWriter, bundleInfo?: BundleInfo) { isOwnFileEmit = false; const previousWriter = writer; setWriter(output); @@ -387,6 +414,16 @@ namespace ts { emitPrologueDirectivesIfNeeded(bundle); emitHelpers(bundle); emitSyntheticTripleSlashReferencesIfNeeded(bundle); + + for (const prepend of bundle.prepends) { + print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); + writeLine(); + } + + if (bundleInfo) { + bundleInfo.originalOffset = writer.getTextPos(); + } + for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } @@ -394,6 +431,14 @@ namespace ts { writer = previousWriter; } + function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + print(EmitHint.Unspecified, unparsed, /*sourceFile*/ undefined); + reset(); + writer = previousWriter; + } + function writeFile(sourceFile: SourceFile, output: EmitTextWriter) { isOwnFileEmit = true; const previousWriter = writer; @@ -534,6 +579,9 @@ namespace ts { case SyntaxKind.TemplateTail: return emitLiteral(node); + case SyntaxKind.UnparsedSource: + return emitUnparsedSource(node); + // Identifiers case SyntaxKind.Identifier: return emitIdentifier(node); @@ -978,6 +1026,11 @@ namespace ts { } } + // SyntaxKind.UnparsedSource + function emitUnparsedSource(unparsed: UnparsedSource) { + write(unparsed.text); + } + // // Identifiers // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d0e07e514d6..5069b0b0695 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2564,15 +2564,29 @@ namespace ts { : node; } - export function createBundle(sourceFiles: ReadonlyArray) { + export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { const node = createNode(SyntaxKind.Bundle); + node.prepends = prepends; node.sourceFiles = sourceFiles; return node; } - export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray) { - if (node.sourceFiles !== sourceFiles) { - return createBundle(sourceFiles); + export function createUnparsedSourceFile(text: string): UnparsedSource { + const node = createNode(SyntaxKind.UnparsedSource); + node.text = text; + return node; + } + + export function createInputFiles(javascript: string, declaration: string): InputFiles { + const node = createNode(SyntaxKind.InputFiles); + node.javascriptText = javascript; + node.declarationText = declaration; + return node; + } + + export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { + if (node.sourceFiles !== sourceFiles || node.prepends !== prepends) { + return createBundle(sourceFiles, prepends); } return node; } diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index ca282da18b8..8bb65e57616 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -119,7 +119,12 @@ namespace ts { export function readJson(path: string, host: { readFile(fileName: string): string | undefined }): object { try { const jsonText = host.readFile(path); - return jsonText ? JSON.parse(jsonText) : {}; + if (!jsonText) return {}; + const result = parseConfigFileTextToJson(path, jsonText); + if (result.error) { + return {}; + } + return result.config; } catch (e) { // gracefully handle if readFile fails or returns not JSON diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c7fa4a9c0eb..c04ae1f6f6c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -503,7 +503,13 @@ namespace ts { export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { performance.mark("beforeParse"); - const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); + let result: SourceFile; + if (languageVersion === ScriptTarget.JSON) { + result = Parser.parseJsonText(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); + } + else { + result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); + } performance.mark("afterParse"); performance.measure("Parse", "beforeParse", "afterParse"); return result; @@ -3566,7 +3572,7 @@ namespace ts { // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] if (token() === SyntaxKind.AsyncKeyword) { nextToken(); - // If the "async" is followed by "=>" token then it is not a begining of an async arrow-function + // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" if (scanner.hasPrecedingLineBreak() || token() === SyntaxKind.EqualsGreaterThanToken) { return Tristate.False; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 560a5f91370..8cdf5faa9de 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -188,7 +188,8 @@ namespace ts { directoryExists: directoryName => sys.directoryExists(directoryName), getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "", getDirectories: (path: string) => sys.getDirectories(path), - realpath + realpath, + readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth) }; } @@ -328,14 +329,14 @@ namespace ts { output += formatColorAndReset(relativeFileName, ForegroundColorEscapeSequences.Cyan); output += ":"; - output += formatColorAndReset(`${ firstLine + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += formatColorAndReset(`${firstLine + 1}`, ForegroundColorEscapeSequences.Yellow); output += ":"; - output += formatColorAndReset(`${ firstLineChar + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += formatColorAndReset(`${firstLineChar + 1}`, ForegroundColorEscapeSequences.Yellow); output += " - "; } output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category)); - output += formatColorAndReset(` TS${ diagnostic.code }: `, ForegroundColorEscapeSequences.Grey); + output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey); output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); if (diagnostic.file) { @@ -477,6 +478,16 @@ namespace ts { ); } + function createCreateProgramOptions(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): CreateProgramOptions { + return { + rootNames, + options, + host, + oldProgram, + configFileParsingDiagnostics + }; + } + /** * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' * that represent a compilation unit. @@ -491,7 +502,13 @@ namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ - export function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program { + export function createProgram(createProgramOptions: CreateProgramOptions): Program; + export function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; + export function createProgram(rootNamesOrOptions: ReadonlyArray | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: ReadonlyArray): Program { + const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; + const { rootNames, options, configFileParsingDiagnostics, projectReferences } = createProgramOptions; + let { host, oldProgram } = createProgramOptions; + let program: Program; let processingDefaultLibFiles: SourceFile[] | undefined; let processingOtherFiles: SourceFile[] | undefined; @@ -528,6 +545,7 @@ namespace ts { performance.mark("beforeProgram"); host = host || createCompilerHost(options); + const configParsingHost = parseConfigHostFromCompilerHost(host); let skipDefaultLib = options.noLib; const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options)); @@ -539,6 +557,7 @@ namespace ts { // Map storing if there is emit blocking diagnostics for given input const hasEmitBlockingDiagnostics = createMap(); let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression; + let _referencesArrayLiteralSyntax: ArrayLiteralExpression; let moduleResolutionCache: ModuleResolutionCache; let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModuleFull[]; @@ -583,6 +602,23 @@ namespace ts { // used to track cases when two file names differ only in casing const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; + // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files + const resolvedProjectReferences: (ResolvedProjectReference | undefined)[] | undefined = projectReferences ? [] : undefined; + const projectReferenceRedirects: Map = createMap(); + if (projectReferences) { + for (const ref of projectReferences) { + const parsedRef = parseProjectReferenceConfigFile(ref); + resolvedProjectReferences.push(parsedRef); + if (parsedRef) { + if (parsedRef.commandLine.options.outFile) { + const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts"); + processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined); + } + addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects); + } + } + } + const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); const structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { @@ -676,6 +712,7 @@ namespace ts { isEmittedFile, getConfigFileParsingDiagnostics, getResolvedModuleWithFailedLookupLocationsFromCache, + getProjectReferences }; verifyCompilerOptions(); @@ -711,12 +748,18 @@ namespace ts { if (commonSourceDirectory === undefined) { 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 + // If a rootDir is specified use it as the commonSourceDirectory commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); } + else if (options.composite) { + // Project compilations never infer their root from the input source paths + commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); + checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory); + } else { commonSourceDirectory = computeCommonSourceDirectory(emittedFiles); } + if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) { // Make sure directory path ends with directory separator so this string can directly // used to replace with "" to get the relative path of the source file and the relative path doesn't @@ -762,7 +805,7 @@ namespace ts { // We only set `file.resolvedModules` via work from the current function, // so it is defined iff we already called the current function on `file`. // That call happened no later than the creation of the `file` object, - // which per above occured during the current program creation. + // which per above occurred during the current program creation. // Since we assume the filesystem does not change during program creation, // it is safe to reuse resolutions from the earlier call. const result: ResolvedModuleFull[] = []; @@ -918,6 +961,35 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Not; } + // Check if any referenced project tsconfig files are different + const oldRefs = oldProgram.getProjectReferences(); + if (projectReferences) { + if (!oldRefs) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + for (let i = 0; i < projectReferences.length; i++) { + const oldRef = oldRefs[i]; + if (oldRef) { + const newRef = parseProjectReferenceConfigFile(projectReferences[i]); + if (!newRef || newRef.sourceFile !== oldRef.sourceFile) { + // Resolved project reference has gone missing or changed + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } + else { + // A previously-unresolved reference may be resolved now + if (parseProjectReferenceConfigFile(projectReferences[i]) !== undefined) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } + } + } + else { + if (oldRefs) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } + // check if program source files has changed in the way that can affect structure of the program const newSourceFiles: SourceFile[] = []; const filePaths: Path[] = []; @@ -1110,6 +1182,7 @@ namespace ts { function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { return { + getPrependNodes, getCanonicalFileName, getCommonSourceDirectory: program.getCommonSourceDirectory, getCompilerOptions: program.getCompilerOptions, @@ -1125,6 +1198,35 @@ namespace ts { }; } + function getProjectReferences() { + if (!resolvedProjectReferences) return; + + return resolvedProjectReferences; + } + + function getPrependNodes(): InputFiles[] { + if (!projectReferences) { + return emptyArray; + } + + const nodes: InputFiles[] = []; + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRefOpts = resolvedProjectReferences[i].commandLine; + if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { + // Upstream project didn't have outFile set -- skip (error will have been issued earlier) + if (!resolvedRefOpts.options.outFile) continue; + + const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts"); + const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`; + const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; + const node = createInputFiles(js, dts); + nodes.push(node); + } + } + return nodes; + } + function isSourceFileFromExternalLibrary(file: SourceFile): boolean { return sourceFilesFoundSearchingNodeModules.get(file.path); } @@ -1221,7 +1323,9 @@ namespace ts { getEmitHost(writeFileCallback), sourceFile, emitOnlyDtsFiles, - transformers); + transformers, + customTransformers && customTransformers.afterDeclarations + ); performance.mark("afterEmit"); performance.measure("Emit", "beforeEmit", "afterEmit"); @@ -1750,7 +1854,13 @@ namespace ts { const sourceFile = getSourceFile(fileName); if (fail) { if (!sourceFile) { - fail(Diagnostics.File_0_not_found, fileName); + const redirect = getProjectReferenceRedirect(fileName); + if (redirect) { + fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName); + } + else { + fail(Diagnostics.File_0_not_found, fileName); + } } else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) { fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); @@ -1848,6 +1958,21 @@ namespace ts { return file; } + let redirectedPath: string | undefined; + if (refFile) { + const redirect = getProjectReferenceRedirect(fileName); + if (redirect) { + ((refFile.redirectedReferences || (refFile.redirectedReferences = [])) as string[]).push(fileName); + fileName = redirect; + // Once we start redirecting to a file, we can potentially come back to it + // via a back-reference from another file in the .d.ts folder. If that happens we'll + // end up trying to add it to the program *again* because we were tracking it via its + // original (un-redirected) name. So we have to map both the original path and the redirected path + // to the source file we're about to find/create + redirectedPath = toPath(redirect); + } + } + // We haven't looked for this file, do so now and cache result const file = host.getSourceFile(fileName, options.target, hostErrorMessage => { if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { @@ -1880,6 +2005,10 @@ namespace ts { } filesByName.set(path, file); + if (redirectedPath) { + filesByName.set(redirectedPath, file); + } + if (file) { sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.path = path; @@ -1919,6 +2048,23 @@ namespace ts { return file; } + function getProjectReferenceRedirect(fileName: string): string | undefined { + const path = toPath(fileName); + // If this file is produced by a referenced project, we need to rewrite it to + // look in the output folder of the referenced project rather than the input + const normalized = getNormalizedAbsolutePath(fileName, path); + let result: string | undefined; + projectReferenceRedirects.forEach((v, k) => { + if (result !== undefined) { + return undefined; + } + if (normalized.indexOf(k) === 0) { + result = changeExtension(fileName.replace(k, v), ".d.ts"); + } + }); + return result; + } + function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { forEach(file.referencedFiles, ref => { const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName); @@ -1929,6 +2075,10 @@ namespace ts { function processTypeReferenceDirectives(file: SourceFile) { // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase()); + if (!typeDirectives) { + return; + } + const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName); for (let i = 0; i < typeDirectives.length; i++) { @@ -2109,6 +2259,31 @@ namespace ts { return allFilesBelongToPath; } + function parseProjectReferenceConfigFile(ref: ProjectReference): { commandLine: ParsedCommandLine, sourceFile: SourceFile } | undefined { + // The actual filename (i.e. add "/tsconfig.json" if necessary) + const refPath = resolveProjectReferencePath(host, ref); + // An absolute path pointing to the containing directory of the config file + const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); + const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile; + if (sourceFile === undefined) { + return undefined; + } + + const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath); + return { commandLine, sourceFile }; + } + + function addProjectReferenceRedirects(referencedProject: ParsedCommandLine, target: Map) { + const rootDir = normalizePath(referencedProject.options.rootDir || getDirectoryPath(referencedProject.options.configFilePath)); + target.set(rootDir, getDeclarationOutputDirectory(referencedProject)); + } + + function getDeclarationOutputDirectory(proj: ParsedCommandLine) { + return proj.options.declarationDir || + proj.options.outDir || + getDirectoryPath(proj.options.configFilePath); + } + function verifyCompilerOptions() { if (options.strictPropertyInitialization && !options.strictNullChecks) { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks"); @@ -2145,6 +2320,47 @@ namespace ts { createDiagnosticForOptionName(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option, "paths"); } + if (options.composite) { + if (options.declaration === false) { + createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration"); + } + } + + if (projectReferences) { + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRefOpts = resolvedProjectReferences[i] && resolvedProjectReferences[i].commandLine.options; + if (resolvedRefOpts === undefined) { + createDiagnosticForReference(i, Diagnostics.File_0_does_not_exist, ref.path); + continue; + } + if (!resolvedRefOpts.composite) { + createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); + } + if (ref.prepend) { + if (resolvedRefOpts.outFile) { + if (!host.fileExists(resolvedRefOpts.outFile)) { + createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, resolvedRefOpts.outFile, ref.path); + } + } + else { + createDiagnosticForReference(i, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path); + } + } + } + } + + // List of collected files is complete; validate exhautiveness if this is a project with a file list + if (options.composite && rootNames.length < files.length) { + const normalizedRootNames = rootNames.map(r => normalizePath(r).toLowerCase()); + const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePath(f.path).toLowerCase()); + for (const file of sourceFiles) { + if (normalizedRootNames.every(r => r !== file)) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file)); + } + } + } + if (options.paths) { for (const key in options.paths) { if (!hasProperty(options.paths, key)) { @@ -2380,12 +2596,16 @@ namespace ts { } } - function getOptionPathsSyntax() { + function getOptionsSyntaxByName(name: string): object | undefined { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); if (compilerOptionsObjectLiteralSyntax) { - return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, "paths"); + return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); } - return emptyArray; + return undefined; + } + + function getOptionPathsSyntax(): PropertyAssignment[] { + return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray; } function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) { @@ -2396,6 +2616,16 @@ namespace ts { createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0); } + function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) { + const referencesSyntax = getProjectReferencesSyntax(); + if (referencesSyntax) { + if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) { + return; + } + } + programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1)); + } + function createDiagnosticForOption(onKey: boolean, option1: string, option2: string, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax || @@ -2406,6 +2636,22 @@ namespace ts { } } + function getProjectReferencesSyntax(): ArrayLiteralExpression | null { + if (_referencesArrayLiteralSyntax === undefined) { + _referencesArrayLiteralSyntax = null; // tslint:disable-line:no-null-keyword + if (options.configFile) { + const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); + for (const prop of getPropertyAssignment(jsonObjectLiteral, "references")) { + if (isArrayLiteralExpression(prop.initializer)) { + _referencesArrayLiteralSyntax = prop.initializer; + break; + } + } + } + } + return _referencesArrayLiteralSyntax; + } + function getCompilerOptionsObjectLiteralSyntax() { if (_compilerOptionsObjectLiteralSyntax === undefined) { _compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword @@ -2430,6 +2676,14 @@ namespace ts { return !!props.length; } + function createOptionDiagnosticInArrayLiteralSyntax(arrayLiteral: ArrayLiteralExpression, index: number, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean { + if (arrayLiteral.elements.length <= index) { + // Out-of-bounds + return false; + } + programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile, arrayLiteral.elements[index], message, arg0, arg1, arg2)); + } + function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) { hasEmitBlockingDiagnostics.set(toPath(emitFileName), true); programDiagnostics.add(diag); @@ -2471,6 +2725,28 @@ namespace ts { } } + /* @internal */ + export function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { + return { + fileExists: f => host.fileExists(f), + readDirectory: (root, extensions, includes, depth?) => host.readDirectory ? host.readDirectory(root, extensions, includes, depth) : [], + readFile: f => host.readFile(f), + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), + getCurrentDirectory: () => host.getCurrentDirectory(), + onUnRecoverableConfigFileDiagnostic: () => undefined + }; + } + + /** + * Returns the target config filename of a project reference + */ + function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined { + if (!host.fileExists(ref.path)) { + return combinePaths(ref.path, "tsconfig.json"); + } + return ref.path; + } + /* @internal */ /** * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 35e056546c0..9e0f3712ab9 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -59,12 +59,15 @@ namespace ts { watcher: FileWatcher; /** ref count keeping this directory watch alive */ refCount: number; + /** map of refcount for the subDirectory */ + subDirectoryMap?: Map; } interface DirectoryOfFailedLookupWatch { dir: string; dirPath: Path; ignore?: true; + subDirectory?: Path; } export const maxNumberOfFilesToIterateForInvalidation = 256; @@ -393,18 +396,20 @@ namespace ts { } // Use some ancestor of the root directory + let subDirectory: Path | undefined; if (rootPath !== undefined) { while (!isInDirectoryPath(dirPath, rootPath)) { const parentPath = getDirectoryPath(dirPath); if (parentPath === dirPath) { break; } + subDirectory = dirPath.slice(parentPath.length + directorySeparator.length) as Path; dirPath = parentPath; dir = getDirectoryPath(dir); } } - return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath); + return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath); } function isPathWithDefaultFailedLookupExtension(path: Path) { @@ -427,7 +432,7 @@ namespace ts { let setAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { // If the failed lookup location path is not one of the supported extensions, // store it in the custom path @@ -439,7 +444,7 @@ namespace ts { setAtRoot = true; } else { - setDirectoryWatcher(dir, dirPath); + setDirectoryWatcher(dir, dirPath, subDirectory); } } } @@ -449,13 +454,20 @@ namespace ts { } } - function setDirectoryWatcher(dir: string, dirPath: Path) { - const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); + function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) { + let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); if (dirWatcher) { dirWatcher.refCount++; } else { - directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 }); + dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 }; + directoryWatchesOfFailedLookups.set(dirPath, dirWatcher); + } + + if (subDirectory) { + const subDirectoryMap = dirWatcher.subDirectoryMap || (dirWatcher.subDirectoryMap = createMap()); + const existing = subDirectoryMap.get(subDirectory) || 0; + subDirectoryMap.set(subDirectory, existing + 1); } } @@ -473,7 +485,7 @@ namespace ts { let removeAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { const refCount = customFailedLookupPaths.get(failedLookupLocationPath); if (refCount) { @@ -490,7 +502,7 @@ namespace ts { removeAtRoot = true; } else { - removeDirectoryWatcher(dirPath); + removeDirectoryWatcher(dirPath, subDirectory); } } } @@ -499,24 +511,42 @@ namespace ts { } } - function removeDirectoryWatcher(dirPath: string) { + function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) { const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); + if (subDirectory) { + const existing = dirWatcher.subDirectoryMap.get(subDirectory); + if (existing === 1) { + dirWatcher.subDirectoryMap.delete(subDirectory); + } + else { + dirWatcher.subDirectoryMap.set(subDirectory, existing - 1); + } + } // Do not close the watcher yet since it might be needed by other failed lookup locations. dirWatcher.refCount--; } + function inWatchedSubdirectory(dirPath: Path, fileOrDirectoryPath: Path) { + const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); + if (!dirWatcher || !dirWatcher.subDirectoryMap) return false; + return forEachKey(dirWatcher.subDirectoryMap, subDirectory => { + const fullSubDirectory = `${dirPath}/${subDirectory}` as Path; + return fullSubDirectory === fileOrDirectoryPath || isInDirectoryPath(fullSubDirectory, fileOrDirectoryPath); + }); + } + function createDirectoryWatcher(directory: string, dirPath: Path) { return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => { const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory); if (cachedDirectoryStructureHost) { - // Since the file existance changed, update the sourceFiles cache + // Since the file existence changed, update the sourceFiles cache cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } // If the files are added to project root or node_modules directory, always run through the invalidation process // Otherwise run through invalidation only if adding to the immediate directory if (!allFilesHaveInvalidatedResolution && - dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath) { + (dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) { if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) { resolutionHost.onInvalidatedResolution(); } @@ -660,7 +690,7 @@ namespace ts { return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => { const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory); if (cachedDirectoryStructureHost) { - // Since the file existance changed, update the sourceFiles cache + // Since the file existence changed, update the sourceFiles cache cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 8cadba453d8..16a6912757f 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts { - function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory { + function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory { switch (moduleKind) { case ModuleKind.ESNext: case ModuleKind.ES2015: @@ -28,7 +28,7 @@ namespace ts { const jsx = compilerOptions.jsx; const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); - const transformers: TransformerFactory[] = []; + const transformers: TransformerFactory[] = []; addRange(transformers, customTransformers && customTransformers.before); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 66b8d696cf9..d0f3ddc9d89 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -163,7 +163,11 @@ namespace ts { const updated = visitNodes(sourceFile.statements, visitDeclarationStatements); return updateSourceFileNode(sourceFile, filterCandidateImports(updated), /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ [], /*hasNoDefaultLib*/ false, /*libReferences*/ []); } - )); + ), mapDefined(node.prepends, prepend => { + if (prepend.kind === SyntaxKind.InputFiles) { + return createUnparsedSourceFile(prepend.declarationText); + } + })); bundle.syntheticFileReferences = []; bundle.syntheticTypeReferences = getFileReferencesForUsedTypeReferences(); bundle.hasNoDefaultLib = hasNoDefaultLib; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 0a607494bb5..76a7219b758 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -295,7 +295,7 @@ namespace ts { */ let enabledSubstitutions: ES2015SubstitutionFlags; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es2016.ts b/src/compiler/transformers/es2016.ts index 22b2f11b612..93b5d45714f 100644 --- a/src/compiler/transformers/es2016.ts +++ b/src/compiler/transformers/es2016.ts @@ -3,7 +3,7 @@ namespace ts { export function transformES2016(context: TransformationContext) { const { hoistVariableDeclaration } = context; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 6689f83ed5b..c61809b64e0 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -40,7 +40,7 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index 92ecce6a15c..bace315e953 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -24,7 +24,7 @@ namespace ts { context.onSubstituteNode = onSubstituteNode; context.enableSubstitution(SyntaxKind.PropertyAccessExpression); context.enableSubstitution(SyntaxKind.PropertyAssignment); - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms an ES5 source file to ES3. diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 68861a5bb43..eec1463433a 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -26,7 +26,7 @@ namespace ts { let enclosingFunctionFlags: FunctionFlags; let enclosingSuperContainerFlags: NodeCheckFlags = 0; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { @@ -118,21 +118,38 @@ namespace ts { } function visitYieldExpression(node: YieldExpression) { - if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator && node.asteriskToken) { - const expression = visitNode(node.expression, visitor, isExpression); + if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { + if (node.asteriskToken) { + const expression = visitNode(node.expression, visitor, isExpression); + + return setOriginalNode( + setTextRange( + createYield( + createAwaitHelper(context, + updateYield( + node, + node.asteriskToken, + createAsyncDelegatorHelper( + context, + createAsyncValuesHelper(context, expression, expression), + expression + ) + ) + ) + ), + node + ), + node + ); + } + return setOriginalNode( setTextRange( createYield( - createAwaitHelper(context, - updateYield( - node, - node.asteriskToken, - createAsyncDelegatorHelper( - context, - createAsyncValuesHelper(context, expression, expression), - expression - ) - ) + createDownlevelAwait( + node.expression + ? visitNode(node.expression, visitor, isExpression) + : createVoidZero() ) ), node @@ -140,6 +157,7 @@ namespace ts { node ); } + return visitEachChild(node, visitor, context); } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 15f0d2d0f81..a1d610280c0 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -289,7 +289,7 @@ namespace ts { let currentExceptionBlock: ExceptionBlock; // The current exception block. let withBlockStack: WithBlock[]; // A stack containing `with` blocks. - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile || (node.transformFlags & TransformFlags.ContainsGenerator) === 0) { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 4acdf63b275..48d3bee42d6 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -4,7 +4,7 @@ namespace ts { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transform JSX-specific syntax in a SourceFile. diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index a3315852632..070980df5de 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -10,7 +10,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); let currentSourceFile: SourceFile; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index d87712d451d..503d662ad58 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -45,7 +45,7 @@ namespace ts { let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. let needUMDDynamicImportHelper: boolean; - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms the module aspects of a SourceFile. diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 5cde69e2ef6..047b552415d 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -39,7 +39,7 @@ namespace ts { let enclosingBlockScopedContainer: Node; let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms the module aspects of a SourceFile. diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 7c561e7d696..862b79dbf24 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -88,7 +88,23 @@ namespace ts { */ let pendingExpressions: Expression[] | undefined; - return transformSourceFile; + return transformSourceFileOrBundle; + + function transformSourceFileOrBundle(node: SourceFile | Bundle) { + if (node.kind === SyntaxKind.Bundle) { + return transformBundle(node); + } + return transformSourceFile(node); + } + + function transformBundle(node: Bundle) { + return createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => { + if (prepend.kind === SyntaxKind.InputFiles) { + return createUnparsedSourceFile(prepend.javascriptText); + } + return prepend; + })); + } /** * Transform TypeScript-specific syntax in a SourceFile. diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index 13d2dff895a..f8a6a6a2795 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -25,6 +25,18 @@ namespace ts { return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default; } + export function chainBundle(transformSourceFile: (x: SourceFile) => SourceFile): (x: SourceFile | Bundle) => SourceFile | Bundle { + return transformSourceFileOrBundle; + + function transformSourceFileOrBundle(node: SourceFile | Bundle) { + return node.kind === SyntaxKind.SourceFile ? transformSourceFile(node) : transformBundle(node); + } + + function transformBundle(node: Bundle) { + return createBundle(map(node.sourceFiles, transformSourceFile), node.prepends); + } + } + export function getImportNeedsImportStarHelper(node: ImportDeclaration) { if (!!getNamespaceDeclarationNode(node)) { return true; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index e45a95f7989..7617fc62cb6 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -120,7 +120,7 @@ namespace ts { createWatchOfConfigFile(configParseResult, commandLineOptions); } else { - performCompilation(configParseResult.fileNames, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult)); + performCompilation(configParseResult.fileNames, configParseResult.projectReferences, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult)); } } else { @@ -130,7 +130,7 @@ namespace ts { createWatchOfFilesAndCompilerOptions(commandLine.fileNames, commandLineOptions); } else { - performCompilation(commandLine.fileNames, commandLineOptions); + performCompilation(commandLine.fileNames, /*references*/ undefined, commandLineOptions); } } } @@ -142,11 +142,18 @@ namespace ts { } } - function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray) { - const compilerHost = createCompilerHost(compilerOptions); - enableStatistics(compilerOptions); + function performCompilation(rootNames: string[], projectReferences: ReadonlyArray | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray) { + const host = createCompilerHost(options); + enableStatistics(options); - const program = createProgram(rootFileNames, compilerOptions, compilerHost, /*oldProgram*/ undefined, configFileParsingDiagnostics); + const programOptions: CreateProgramOptions = { + rootNames, + options, + projectReferences, + host, + configFileParsingDiagnostics + }; + const program = createProgram(programOptions); const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine)); reportStatistics(program); return sys.exit(exitStatus); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a60c12f6c23..0ef9f4e5bea 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -397,6 +397,8 @@ namespace ts { // Top-level nodes SourceFile, Bundle, + UnparsedSource, + InputFiles, // JSDoc nodes JSDocTypeExpression, @@ -1877,7 +1879,7 @@ namespace ts { } /** - * A list of comma-seperated expressions. This node is only created by transformations. + * A list of comma-separated expressions. This node is only created by transformations. */ export interface CommaListExpression extends Expression { kind: SyntaxKind.CommaListExpression; @@ -2602,6 +2604,12 @@ namespace ts { /* @internal */ resolvedModules: Map; /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: ReadonlyArray; + /** + * When a file's references are redirected due to project reference directives, + * the original names of the references are stored in this array + */ + /* @internal*/ + redirectedReferences?: ReadonlyArray; // Identifier only if `declare global` /* @internal */ moduleAugmentations: ReadonlyArray; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; @@ -2615,12 +2623,24 @@ namespace ts { export interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; /* @internal */ hasNoDefaultLib?: boolean; } + export interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + declarationText: string; + } + + export interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } + export interface JsonSourceFile extends SourceFile { statements: NodeArray; } @@ -2752,6 +2772,13 @@ namespace ts { /* @internal */ isEmittedFile(file: string): boolean; /* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined; + + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + + export interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } /* @internal */ @@ -2762,10 +2789,12 @@ namespace ts { } export interface CustomTransformers { - /** Custom transformers to evaluate before built-in transformations. */ + /** Custom transformers to evaluate before built-in .js transformations. */ before?: TransformerFactory[]; - /** Custom transformers to evaluate after built-in transformations. */ + /** Custom transformers to evaluate after built-in .js transformations. */ after?: TransformerFactory[]; + /** Custom transformers to evaluate after built-in .d.ts transformations. */ + afterDeclarations?: TransformerFactory[]; } export interface SourceMapSpan { @@ -4176,7 +4205,18 @@ namespace ts { name: string; } - export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + export interface ProjectReference { + /** A normalized path on disk */ + path: string; + /** The path as the user originally wrote it */ + originalPath?: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ + prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; + } + + export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; export interface CompilerOptions { /*@internal*/ all?: boolean; @@ -4248,6 +4288,7 @@ namespace ts { /* @internal */ pretty?: boolean; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -4341,12 +4382,13 @@ namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = ESNext, } export const enum LanguageVariant { Standard, - JSX, + JSX } /** Either a parsed command line or a parsed tsconfig.json */ @@ -4354,6 +4396,7 @@ namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -4369,6 +4412,7 @@ namespace ts { /* @internal */ export interface ConfigFileSpecs { filesSpecs: ReadonlyArray; + referencesSpecs: ReadonlyArray | undefined; /** * Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching */ @@ -4384,10 +4428,20 @@ namespace ts { export interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; /* @internal */ spec: ConfigFileSpecs; } + export interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } + /* @internal */ export interface CommandLineOptionBase { name: string; @@ -4674,6 +4728,7 @@ namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; /* * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of @@ -4912,6 +4967,8 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; + getPrependNodes(): ReadonlyArray; + writeFile: WriteFileCallback; } @@ -5063,7 +5120,23 @@ namespace ts { /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; /*@internal*/ writeList(format: ListFormat, list: NodeArray, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; - /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void; + /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter, info?: BundleInfo): void; + } + + /** + * When a bundle contains prepended content, we store a file on disk indicating where the non-prepended + * content of that file starts. On a subsequent build where there are no upstream .d.ts changes, we + * read the bundle info file and the original .js file to quickly re-use portion of the file + * that didn't originate in prepended content. + */ + /* @internal */ + export interface BundleInfo { + // The offset (in characters, i.e. suitable for .substr) at which the + // non-prepended portion of the emitted file starts. + originalOffset: number; + // The total length of this bundle. Used to ensure we're pulling from + // the same source as we originally wrote. + totalLength: number; } export interface PrintHandlers { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3f7affcc044..7afc862bba4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6,13 +6,6 @@ namespace ts { export const externalHelpersModuleNameText = "tslib"; - export interface ReferencePathMatchResult { - fileReference?: FileReference; - diagnosticMessage?: DiagnosticMessage; - isNoDefaultLib?: boolean; - isTypeReferenceDirective?: boolean; - } - export function getDeclarationOfKind(symbol: Symbol, kind: T["kind"]): T { const declarations = symbol.declarations; if (declarations) { @@ -2109,8 +2102,14 @@ namespace ts { if (isDeclaration(name.parent)) { return name.parent.name === name; } - const binExp = name.parent.parent; - return isBinaryExpression(binExp) && getSpecialPropertyAssignmentKind(binExp) !== SpecialPropertyAssignmentKind.None && getNameOfDeclaration(binExp) === name; + else if (isQualifiedName(name.parent)) { + const tag = name.parent.parent; + return isJSDocParameterTag(tag) && tag.name === name.parent; + } + else { + const binExp = name.parent.parent; + return isBinaryExpression(binExp) && getSpecialPropertyAssignmentKind(binExp) !== SpecialPropertyAssignmentKind.None && getNameOfDeclaration(binExp) === name; + } default: return false; } @@ -2916,6 +2915,7 @@ namespace ts { sourceMapFilePath: string | undefined; declarationFilePath: string | undefined; declarationMapPath: string | undefined; + bundleInfoPath: string | undefined; } /** diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 6b3adfcf3a4..3c5646cc48b 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -86,13 +86,6 @@ namespace ts { }; } - /** - * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors - */ - export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { - getCurrentDirectory(): string; - } - /** Parses config file using System interface */ export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) { const host: ParseConfigFileHost = system; @@ -102,30 +95,6 @@ namespace ts { return result; } - /** - * Reads the config file, reports errors if any and exits if the config file cannot be found - */ - export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { - let configFileText: string; - try { - configFileText = host.readFile(configFileName); - } - catch (e) { - const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); - host.onUnRecoverableConfigFileDiagnostic(error); - return undefined; - } - if (!configFileText) { - const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - host.onUnRecoverableConfigFileDiagnostic(error); - return undefined; - } - - const result = parseJsonText(configFileName, configFileText); - const cwd = host.getCurrentDirectory(); - return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); - } - /** * Program structure needed to emit the files and report diagnostics */ @@ -297,7 +266,6 @@ namespace ts { } namespace ts { - export type DiagnosticReporter = (diagnostic: Diagnostic) => void; export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void; /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ export type CreateProgram = (rootNames: ReadonlyArray | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray) => T; @@ -382,16 +350,6 @@ namespace ts { options: CompilerOptions; } - /** - * Reports config file diagnostics - */ - export interface ConfigFileDiagnosticsReporter { - /** - * Reports unrecoverable error when parsing config file - */ - onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; - } - /** * Host to create watch with config file */ diff --git a/src/harness/compiler.ts b/src/harness/compiler.ts index 59d636303f9..2900c8edbc8 100644 --- a/src/harness/compiler.ts +++ b/src/harness/compiler.ts @@ -10,8 +10,7 @@ namespace compiler { export function readProject(host: fakes.ParseConfigHost, project: string | undefined, existingOptions?: ts.CompilerOptions): Project | undefined { if (project) { - project = host.vfs.stringComparer(vpath.basename(project), "tsconfig.json") === 0 ? project : - vpath.combine(project, "tsconfig.json"); + project = vpath.isTsConfigFile(project) ? project : vpath.combine(project, "tsconfig.json"); } else { [project] = host.vfs.scanSync(".", "ancestors-or-self", { diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts index c6d955e8215..79d3503cb80 100644 --- a/src/harness/fakes.ts +++ b/src/harness/fakes.ts @@ -39,9 +39,7 @@ namespace fakes { public readFile(path: string) { try { const content = this.vfs.readFileSync(path, "utf8"); - return content === undefined ? undefined : - vpath.extname(path) === ".json" ? utils.removeComments(utils.removeByteOrderMark(content), utils.CommentRemoval.leadingAndTrailing) : - utils.removeByteOrderMark(content); + return content === undefined ? undefined : utils.removeByteOrderMark(content); } catch { return undefined; @@ -203,6 +201,7 @@ namespace fakes { public readonly sys: System; public readonly defaultLibLocation: string; public readonly outputs: documents.TextDocument[] = []; + private readonly _outputsMap: collections.SortedMap; public readonly traces: string[] = []; public readonly shouldAssertInvariants = !Harness.lightMode; @@ -218,6 +217,7 @@ namespace fakes { this._newLine = ts.getNewLineCharacter(options, () => this.sys.newLine); this._sourceFiles = new collections.SortedMap({ comparer: sys.vfs.stringComparer, sort: "insertion" }); this._setParentNodes = setParentNodes; + this._outputsMap = new collections.SortedMap(this.vfs.stringComparer); } public get vfs() { @@ -256,6 +256,10 @@ namespace fakes { return this.sys.getDirectories(path); } + public readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { + return this.sys.readDirectory(path, extensions, exclude, include, depth); + } + public readFile(path: string): string | undefined { return this.sys.readFile(path); } @@ -267,13 +271,11 @@ namespace fakes { const document = new documents.TextDocument(fileName, content); document.meta.set("fileName", fileName); this.vfs.filemeta(fileName).set("document", document); - const index = this.outputs.findIndex(output => this.vfs.stringComparer(document.file, output.file) === 0); - if (index < 0) { + if (!this._outputsMap.has(document.file)) { + this._outputsMap.set(document.file, this.outputs.length); this.outputs.push(document); } - else { - this.outputs[index] = document; - } + this.outputs[this._outputsMap.get(document.file)] = document; } public trace(s: string): void { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 513c0fdd8f4..5e7dede93ee 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -957,7 +957,14 @@ namespace FourSlash { } public verifyCompletionsAt(markerName: string | ReadonlyArray, expected: ReadonlyArray, options?: FourSlashInterface.CompletionsAtOptions) { - this.verifyCompletions({ marker: markerName, exact: expected, isNewIdentifierLocation: options && options.isNewIdentifierLocation, preferences: options, triggerCharacter: options && options.triggerCharacter }); + this.verifyCompletions({ + marker: markerName, + exact: expected, + isNewIdentifierLocation: options && options.isNewIdentifierLocation, + preferences: options, + // TODO: GH#20090 + triggerCharacter: (options && options.triggerCharacter) as ts.CompletionsTriggerCharacter | undefined, + }); } public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, options?: FourSlashInterface.VerifyCompletionListContainsOptions) { @@ -1142,32 +1149,6 @@ namespace FourSlash { } } - private verifyReferencesAre(expectedReferences: Range[]) { - const actualReferences = this.getReferencesAtCaret() || []; - - if (actualReferences.length > expectedReferences.length) { - // Find the unaccounted-for reference. - for (const actual of actualReferences) { - if (!ts.forEach(expectedReferences, r => r.pos === actual.textSpan.start)) { - this.raiseError(`A reference ${stringify(actual)} is unaccounted for.`); - } - } - // Probably will never reach here. - this.raiseError(`There are ${actualReferences.length} references but only ${expectedReferences.length} were expected.`); - } - - for (const reference of expectedReferences) { - const { fileName, pos, end } = reference; - if (reference.marker && reference.marker.data) { - const { isWriteAccess, isDefinition } = reference.marker.data as { isWriteAccess?: boolean, isDefinition?: boolean }; - this.verifyReferencesWorker(actualReferences, fileName, pos, end, isWriteAccess, isDefinition); - } - else { - this.verifyReferencesWorker(actualReferences, fileName, pos, end); - } - } - } - private verifyDocumentHighlightsRespectFilesList(files: ReadonlyArray): void { const startFile = this.activeFile.fileName; for (const fileName of files) { @@ -1179,20 +1160,6 @@ namespace FourSlash { } } - public verifyReferencesOf(range: Range, references: Range[]) { - this.goToRangeStart(range); - this.verifyDocumentHighlightsRespectFilesList(unique(references, e => e.fileName)); - this.verifyReferencesAre(references); - } - - public verifyRangesReferenceEachOther(ranges?: Range[]) { - ranges = ranges || this.getRanges(); - assert(ranges.length); - for (const range of ranges) { - this.verifyReferencesOf(range, ranges); - } - } - public verifyReferenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: ReadonlyArray | undefined): void { interface ReferenceGroupJson { definition: string | { text: string, range: ts.TextSpan }; @@ -1250,6 +1217,12 @@ namespace FourSlash { } } + // Necessary to have this function since `findReferences` isn't implemented in `client.ts` + public verifyGetReferencesForServerTest(expected: ReadonlyArray): void { + const refs = this.getReferencesAtCaret(); + assert.deepEqual(refs, expected); + } + public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[]) { ranges = ranges || this.getRanges(); this.verifyReferenceGroups(ranges, [{ definition, ranges }]); @@ -1314,23 +1287,6 @@ Actual: ${stringify(fullActual)}`); TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts")); } - private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { - for (const reference of references) { - if (reference && reference.fileName === fileName && reference.textSpan.start === start && ts.textSpanEnd(reference.textSpan) === end) { - if (typeof isWriteAccess !== "undefined" && reference.isWriteAccess !== isWriteAccess) { - this.raiseError(`verifyReferencesAtPositionListContains failed - item isWriteAccess value does not match, actual: ${reference.isWriteAccess}, expected: ${isWriteAccess}.`); - } - if (typeof isDefinition !== "undefined" && reference.isDefinition !== isDefinition) { - this.raiseError(`verifyReferencesAtPositionListContains failed - item isDefinition value does not match, actual: ${reference.isDefinition}, expected: ${isDefinition}.`); - } - return; - } - } - - const missingItem = { fileName, start, end, isWriteAccess, isDefinition }; - this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`); - } - private getCompletionListAtCaret(options?: ts.GetCompletionsAtPositionOptions): ts.CompletionInfo { return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options); } @@ -2470,7 +2426,7 @@ Actual: ${stringify(fullActual)}`); Harness.IO.log(stringify(spans)); } - public verifyOutliningSpans(spans: Range[], kind?: "comment" | "region" | "code") { + public verifyOutliningSpans(spans: Range[], kind?: "comment" | "region" | "code" | "imports") { const actual = this.languageService.getOutliningSpans(this.activeFile.fileName); if (actual.length !== spans.length) { @@ -4210,10 +4166,6 @@ namespace FourSlashInterface { this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected); } - public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) { - this.state.verifyReferencesOf(start, references); - } - public referenceGroups(starts: Many | Many, parts: ReferenceGroup[]) { this.state.verifyReferenceGroups(starts, parts); } @@ -4222,12 +4174,12 @@ namespace FourSlashInterface { this.state.verifyNoReferences(markerNameOrRange); } - public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[]) { - this.state.verifySingleReferenceGroup(definition, ranges); + public getReferencesForServerTest(expected: ReadonlyArray) { + this.state.verifyGetReferencesForServerTest(expected); } - public rangesReferenceEachOther(ranges?: FourSlash.Range[]) { - this.state.verifyRangesReferenceEachOther(ranges); + public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[]) { + this.state.verifySingleReferenceGroup(definition, ranges); } public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) { @@ -4302,7 +4254,7 @@ namespace FourSlashInterface { this.state.verifyCurrentNameOrDottedNameSpanText(text); } - public outliningSpansInCurrentFile(spans: FourSlash.Range[], kind?: "comment" | "region" | "code") { + public outliningSpansInCurrentFile(spans: FourSlash.Range[], kind?: "comment" | "region" | "code" | "imports") { this.state.verifyOutliningSpans(spans, kind); } @@ -4774,7 +4726,7 @@ namespace FourSlashInterface { readonly sourceDisplay?: string; }; export interface CompletionsAtOptions extends Partial { - triggerCharacter?: string; + triggerCharacter?: ts.CompletionsTriggerCharacter; isNewIdentifierLocation?: boolean; } @@ -4785,13 +4737,13 @@ namespace FourSlashInterface { readonly includes?: Many; readonly excludes?: Many; readonly preferences: ts.UserPreferences; - readonly triggerCharacter?: string; + readonly triggerCharacter?: ts.CompletionsTriggerCharacter; } export type Many = T | ReadonlyArray; export interface VerifyCompletionListContainsOptions extends ts.UserPreferences { - triggerCharacter?: string; + triggerCharacter?: ts.CompletionsTriggerCharacter; sourceDisplay: string; isRecommended?: true; insertText?: string; diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index bfd0153466c..cc1e5466115 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -21,10 +21,6 @@ namespace RWC { } } - function isTsConfigFile(file: { path: string }): boolean { - return file.path.indexOf("tsconfig") !== -1 && file.path.indexOf("json") !== -1; - } - export function runRWCTest(jsonPath: string) { describe("Testing a rwc project: " + jsonPath, () => { let inputFiles: Harness.Compiler.TestFile[] = []; @@ -69,11 +65,10 @@ namespace RWC { // we will set noEmitOnError flag to be false. opts.options.noEmitOnError = false; }); + let fileNames = opts.fileNames; - runWithIOLog(ioLog, oldIO => { - let fileNames = opts.fileNames; - - const tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined); + runWithIOLog(ioLog, () => { + const tsconfigFile = ts.forEach(ioLog.filesRead, f => vpath.isTsConfigFile(f.path) ? f : undefined); if (tsconfigFile) { const tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path); tsconfigFiles.push({ unitName: tsconfigFile.path, content: tsconfigFileContents.content }); @@ -103,55 +98,40 @@ namespace RWC { } // Add files to compilation - const isInInputList = (resolvedPath: string) => (inputFile: { unitName: string; content: string; }) => inputFile.unitName === resolvedPath; for (const fileRead of ioLog.filesRead) { - // Check if the file is already added into the set of input files. - const resolvedPath = ts.normalizeSlashes(Harness.IO.resolvePath(fileRead.path)); - const inInputList = ts.forEach(inputFiles, isInInputList(resolvedPath)); - - if (isTsConfigFile(fileRead)) { - continue; - } - - if (!Harness.isDefaultLibraryFile(fileRead.path)) { - if (inInputList) { - continue; - } + const normalized = ts.normalizeSlashes(fileRead.path); + if (!uniqueNames.has(normalized) && !Harness.isDefaultLibraryFile(fileRead.path)) { + uniqueNames.set(normalized, true); otherFiles.push(getHarnessCompilerInputUnit(fileRead.path)); } - else if (!opts.options.noLib && Harness.isDefaultLibraryFile(fileRead.path)) { - if (!inInputList) { - // If useCustomLibraryFile is true, we will use lib.d.ts from json object - // otherwise use the lib.d.ts from built/local - // Majority of RWC code will be using built/local/lib.d.ts instead of - // lib.d.ts inside json file. However, some RWC cases will still use - // their own version of lib.d.ts because they have customized lib.d.ts - if (useCustomLibraryFile) { - inputFiles.push(getHarnessCompilerInputUnit(fileRead.path)); - } - else { - // set the flag to put default library to the beginning of the list - inputFiles.unshift(Harness.getDefaultLibraryFile(fileRead.path, oldIO)); - } - } + else if (!opts.options.noLib && Harness.isDefaultLibraryFile(fileRead.path) && !uniqueNames.has(normalized) && useCustomLibraryFile) { + // If useCustomLibraryFile is true, we will use lib.d.ts from json object + // otherwise use the lib.d.ts from built/local + // Majority of RWC code will be using built/local/lib.d.ts instead of + // lib.d.ts inside json file. However, some RWC cases will still use + // their own version of lib.d.ts because they have customized lib.d.ts + uniqueNames.set(normalized, true); + inputFiles.push(getHarnessCompilerInputUnit(fileRead.path)); } } + }); + if (useCustomLibraryFile) { // do not use lib since we already read it in above opts.options.lib = undefined; opts.options.noLib = true; + } - // Emit the results - compilerResult = Harness.Compiler.compileFiles( - inputFiles, - otherFiles, - /* harnessOptions */ undefined, - opts.options, - // Since each RWC json file specifies its current directory in its json file, we need - // to pass this information in explicitly instead of acquiring it from the process. - currentDirectory); - compilerOptions = compilerResult.options; - }); + // Emit the results + compilerResult = Harness.Compiler.compileFiles( + inputFiles, + otherFiles, + /* harnessOptions */ undefined, + opts.options, + // Since each RWC json file specifies its current directory in its json file, we need + // to pass this information in explicitly instead of acquiring it from the process. + currentDirectory); + compilerOptions = compilerResult.options; function getHarnessCompilerInputUnit(fileName: string): Harness.Compiler.TestFile { const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName)); diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts index 0efd8662cf1..df73c575b15 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -11,7 +11,7 @@ namespace ts.projectSystem { } describe("CompileOnSave affected list", () => { - function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) { + function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: File[] }[]) { const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[]; const actualResult = response.sort((list1, list2) => compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName)); expectedFileList = expectedFileList.sort((list1, list2) => compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName)); @@ -47,12 +47,12 @@ namespace ts.projectSystem { } describe("for configured projects", () => { - let moduleFile1: FileOrFolder; - let file1Consumer1: FileOrFolder; - let file1Consumer2: FileOrFolder; - let moduleFile2: FileOrFolder; - let globalFile3: FileOrFolder; - let configFile: FileOrFolder; + let moduleFile1: File; + let file1Consumer1: File; + let file1Consumer2: File; + let moduleFile2: File; + let globalFile3: File; + let configFile: File; let changeModuleFile1ShapeRequest1: server.protocol.Request; let changeModuleFile1InternalRequest1: server.protocol.Request; // A compile on save affected file request using file1 @@ -225,7 +225,7 @@ namespace ts.projectSystem { openFilesForSession([moduleFile1], session); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]); - const file1Consumer3: FileOrFolder = { + const file1Consumer3: File = { path: "/a/b/file1Consumer3.ts", content: `import {Foo} from "./moduleFile1"; let y = Foo();` }; @@ -330,7 +330,7 @@ namespace ts.projectSystem { }` }; - const configFile2: FileOrFolder = { + const configFile2: File = { path: "/a/tsconfig.json", content: `{ "compileOnSave": true @@ -403,7 +403,7 @@ namespace ts.projectSystem { }); it("should return cascaded affected file list", () => { - const file1Consumer1Consumer1: FileOrFolder = { + const file1Consumer1Consumer1: File = { path: "/a/b/file1Consumer1Consumer1.ts", content: `import {y} from "./file1Consumer1";` }; @@ -428,13 +428,13 @@ namespace ts.projectSystem { }); it("should work fine for files with circular references", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: ` /// export var t1 = 10;` }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/file2.ts", content: ` /// @@ -450,11 +450,11 @@ namespace ts.projectSystem { }); it("should return results for all projects if not specifying projectFileName", () => { - const file1: FileOrFolder = { path: "/a/b/file1.ts", content: "export var t = 10;" }; - const file2: FileOrFolder = { path: "/a/b/file2.ts", content: `import {t} from "./file1"; var t2 = 11;` }; - const file3: FileOrFolder = { path: "/a/c/file2.ts", content: `import {t} from "../b/file1"; var t3 = 11;` }; - const configFile1: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{ "compileOnSave": true }` }; - const configFile2: FileOrFolder = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` }; + const file1: File = { path: "/a/b/file1.ts", content: "export var t = 10;" }; + const file2: File = { path: "/a/b/file2.ts", content: `import {t} from "./file1"; var t2 = 11;` }; + const file3: File = { path: "/a/c/file2.ts", content: `import {t} from "../b/file1"; var t3 = 11;` }; + const configFile1: File = { path: "/a/b/tsconfig.json", content: `{ "compileOnSave": true }` }; + const configFile2: File = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` }; const host = createServerHost([file1, file2, file3, configFile1, configFile2]); const session = createSession(host); @@ -469,7 +469,7 @@ namespace ts.projectSystem { }); it("should detect removed code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// @@ -490,7 +490,7 @@ namespace ts.projectSystem { }); it("should detect non-existing code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index 84225a364cb..c120bfae8f9 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -14,7 +14,7 @@ namespace ts { const { options: actualCompilerOptions, errors: actualErrors} = convertCompilerOptionsFromJson(json.compilerOptions, "/apath/", configFileName); const parsedCompilerOptions = JSON.stringify(actualCompilerOptions); - const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions); + const expectedCompilerOptions = JSON.stringify({ ...expectedResult.compilerOptions, configFilePath: configFileName }); assert.equal(parsedCompilerOptions, expectedCompilerOptions); const expectedErrors = expectedResult.errors; diff --git a/src/harness/unittests/organizeImports.ts b/src/harness/unittests/organizeImports.ts index 4471600b1c1..ebd5a7925a2 100644 --- a/src/harness/unittests/organizeImports.ts +++ b/src/harness/unittests/organizeImports.ts @@ -446,11 +446,11 @@ import { React, Other } from "react"; }, reactLibFile); - function testOrganizeImports(testName: string, testFile: TestFSWithWatch.FileOrFolder, ...otherFiles: TestFSWithWatch.FileOrFolder[]) { + function testOrganizeImports(testName: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) { it(testName, () => runBaseline(`organizeImports/${testName}.ts`, testFile, ...otherFiles)); } - function runBaseline(baselinePath: string, testFile: TestFSWithWatch.FileOrFolder, ...otherFiles: TestFSWithWatch.FileOrFolder[]) { + function runBaseline(baselinePath: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) { const { path: testPath, content: testContent } = testFile; const languageService = makeLanguageService(testFile, ...otherFiles); const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, defaultPreferences); @@ -468,7 +468,7 @@ import { React, Other } from "react"; }); } - function makeLanguageService(...files: TestFSWithWatch.FileOrFolder[]) { + function makeLanguageService(...files: TestFSWithWatch.File[]) { const host = projectSystem.createServerHost(files); const projectService = projectSystem.createProjectService(host, { useSingleInferredProject: true }); projectService.setCompilerOptionsForInferredProjects({ jsx: files.some(f => f.path.endsWith("x")) ? JsxEmit.React : JsxEmit.None }); @@ -555,4 +555,4 @@ import { React, Other } from "react"; } } }); -} \ No newline at end of file +} diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts new file mode 100644 index 00000000000..76ddd5fc29e --- /dev/null +++ b/src/harness/unittests/projectReferences.ts @@ -0,0 +1,291 @@ +/// +/// + +namespace ts { + interface TestProjectSpecification { + configFileName?: string; + references?: ReadonlyArray; + files: { [fileName: string]: string }; + outputFiles?: { [fileName: string]: string }; + config?: object; + options?: Partial; + } + interface TestSpecification { + [path: string]: TestProjectSpecification; + } + + function assertHasError(message: string, errors: ReadonlyArray, diag: DiagnosticMessage) { + if (!errors.some(e => e.code === diag.code)) { + const errorString = errors.map(e => ` ${e.file ? e.file.fileName : "[global]"}: ${e.messageText}`).join("\r\n"); + assert(false, `${message}: Did not find any diagnostic for ${diag.message} in:\r\n${errorString}`); + } + } + + function assertNoErrors(message: string, errors: ReadonlyArray) { + if (errors && errors.length > 0) { + assert(false, `${message}: Expected no errors, but found:\r\n${errors.map(e => ` ${e.messageText}`).join("\r\n")}`); + } + } + + function combineAllPaths(...paths: string[]) { + let result = paths[0]; + for (let i = 1; i < paths.length; i++) { + result = combinePaths(result, paths[i]); + } + return result; + } + + const emptyModule = "export { };"; + + /** + * Produces the text of a source file which imports all of the + * specified module names + */ + function moduleImporting(...names: string[]) { + return names.map((n, i) => `import * as mod_${i} from ${n}`).join("\r\n"); + } + + function testProjectReferences(spec: TestSpecification, entryPointConfigFileName: string, checkResult: (prog: Program, host: fakes.CompilerHost) => void) { + const files = createMap(); + for (const key in spec) { + const sp = spec[key]; + const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json"); + const options = { + compilerOptions: { + composite: true, + outDir: "bin", + ...sp.options + }, + references: (sp.references || []).map(r => { + if (typeof r === "string") { + return { path: r }; + } + return r; + }), + ...sp.config + }; + const configContent = JSON.stringify(options); + const outDir = options.compilerOptions.outDir; + files.set(configFileName, configContent); + for (const sourceFile of Object.keys(sp.files)) { + files.set(sourceFile, sp.files[sourceFile]); + } + if (sp.outputFiles) { + for (const outFile of Object.keys(sp.outputFiles)) { + files.set(combineAllPaths("/", key, outDir, outFile), sp.outputFiles[outFile]); + } + } + } + + const vfsys = new vfs.FileSystem(false, { files: { "/lib.d.ts": TestFSWithWatch.libFile.content! } }); + files.forEach((v, k) => { + vfsys.mkdirpSync(getDirectoryPath(k)); + vfsys.writeFileSync(k, v); + }); + const host = new fakes.CompilerHost(new fakes.System(vfsys)); + + const { config, error } = readConfigFile(entryPointConfigFileName, name => host.readFile(name)); + + // We shouldn't have any errors about invalid tsconfig files in these tests + assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); + const file = parseJsonConfigFileContent(config, parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); + file.options.configFilePath = entryPointConfigFileName; + const prog = createProgram({ + rootNames: file.fileNames, + options: file.options, + host, + projectReferences: file.projectReferences + }); + checkResult(prog, host); + } + + describe("project-references meta check", () => { + it("default setup was created correctly", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [] + }, + "/reference": { + files: { "/secondary/b.ts": moduleImporting("../primary/a") }, + references: ["../primary"] + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", prog => { + assert.isTrue(!!prog, "Program should exist"); + assertNoErrors("Sanity check should not produce errors", prog.getOptionsDiagnostics()); + }); + }); + }); + + /** + * Validate that we enforce the basic settings constraints for referenced projects + */ + describe("project-references constraint checking for settings", () => { + it("errors when declaration = false", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [], + options: { + declaration: false + } + } + }; + + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Composite_projects_may_not_disable_declaration_emit); + }); + }); + + it("errors when the referenced project doesn't have composite:true", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [], + options: { + composite: false + } + }, + "/reference": { + files: { "/secondary/b.ts": moduleImporting("../primary/a") }, + references: ["../primary"] + } + }; + testProjectReferences(spec, "/reference/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about 'composite' not being set", errs, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true); + }); + }); + + it("errors when the file list is not exhaustive", () => { + const spec: TestSpecification = { + "/primary": { + files: { + "/primary/a.ts": "import * as b from './b'", + "/primary/b.ts": "export {}" + }, + config: { + files: ["a.ts"] + } + } + }; + + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern); + }); + }); + + it("errors when the referenced project doesn't exist", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: ["../foo"] + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about a missing file", errs, Diagnostics.File_0_does_not_exist); + }); + }); + + it("errors when a prepended project reference doesn't set outFile", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [{ path: "../someProj", prepend: true }] + }, + "/someProj": { + files: { "/someProj/b.ts": "const x = 100;" } + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about outFile not being set", errs, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set); + }); + }); + + it("errors when a prepended project reference output doesn't exist", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": "const y = x;" }, + references: [{ path: "../someProj", prepend: true }] + }, + "/someProj": { + files: { "/someProj/b.ts": "const x = 100;" }, + options: { outFile: "foo.js" } + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about outFile being missing", errs, Diagnostics.Output_file_0_from_project_1_does_not_exist); + }); + }); + }); + + /** + * Path mapping behavior + */ + describe("project-references path mapping", () => { + it("redirects to the output .d.ts file", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/a.ts": "export const m: number = 3;" }, + references: [], + outputFiles: { "a.d.ts": emptyModule } + }, + "/beta": { + files: { "/beta/b.ts": "import { m } from '../alpha/a'" }, + references: ["../alpha"] + } + }; + testProjectReferences(spec, "/beta/tsconfig.json", program => { + assertNoErrors("File setup should be correct", program.getOptionsDiagnostics()); + assertHasError("Found a type error", program.getSemanticDiagnostics(), Diagnostics.Module_0_has_no_exported_member_1); + }); + }); + }); + + describe("project-references nice-behavior", () => { + it("issues a nice error when the input file is missing", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/a.ts": "export const m: number = 3;" }, + references: [] + }, + "/beta": { + files: { "/beta/b.ts": "import { m } from '../alpha/a'" }, + references: ["../alpha"] + } + }; + testProjectReferences(spec, "/beta/tsconfig.json", program => { + assertHasError("Issues a useful error", program.getSemanticDiagnostics(), Diagnostics.Output_file_0_has_not_been_built_from_source_file_1); + }); + }); + }); + + /** + * 'composite' behavior + */ + describe("project-references behavior changes under composite: true", () => { + it("doesn't infer the rootDir from source paths", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/src/a.ts": "export const m: number = 3;" }, + options: { + declaration: true, + outDir: "bin" + }, + references: [] + } + }; + testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => { + program.emit(); + assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]); + }); + }); + }); + +} diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 0ba9e6f6351..74037d79418 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -884,7 +884,7 @@ namespace ts { }); }); - type FileOrFolder = TestFSWithWatch.FileOrFolder; + type File = TestFSWithWatch.File; import createTestSystem = TestFSWithWatch.createWatchedSystem; import libFile = TestFSWithWatch.libFile; @@ -920,22 +920,22 @@ namespace ts { verifyProgramIsUptoDate(program, fileNames, options); } - function verifyProgram(files: FileOrFolder[], rootFiles: string[], options: CompilerOptions, configFile: string) { + function verifyProgram(files: File[], rootFiles: string[], options: CompilerOptions, configFile: string) { const system = createTestSystem(files); verifyProgramWithoutConfigFile(system, rootFiles, options); verifyProgramWithConfigFile(system, configFile); } it("has empty options", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: "let x = 1" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/file2.ts", content: "let y = 1" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: "{}" }; @@ -944,19 +944,19 @@ namespace ts { it("has lib specified in the options", () => { const compilerOptions: CompilerOptions = { lib: ["es5", "es2015.promise"] }; - const app: FileOrFolder = { + const app: File = { path: "/src/app.ts", content: "var x: Promise;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/src/tsconfig.json", content: JSON.stringify({ compilerOptions }) }; - const es5Lib: FileOrFolder = { + const es5Lib: File = { path: "/compiler/lib.es5.d.ts", content: "declare const eval: any" }; - const es2015Promise: FileOrFolder = { + const es2015Promise: File = { path: "/compiler/lib.es2015.promise.d.ts", content: "declare class Promise {}" }; @@ -975,26 +975,26 @@ namespace ts { ] } }; - const app: FileOrFolder = { + const app: File = { path: "/src/packages/framework/app.ts", content: 'import classc from "module1/lib/file1";\ import classD from "module3/file3";\ let x = new classc();\ let y = new classD();' }; - const module1: FileOrFolder = { + const module1: File = { path: "/src/packages/mail/data/module1/lib/file1.ts", content: 'import classc from "module2/file2";export default classc;', }; - const module2: FileOrFolder = { + const module2: File = { path: "/src/packages/mail/data/module1/lib/module2/file2.ts", content: 'class classc { method2() { return "hello"; } }\nexport default classc', }; - const module3: FileOrFolder = { + const module3: File = { path: "/src/packages/styles/module3/file3.ts", content: "class classD { method() { return 10; } }\nexport default classD;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/src/tsconfig.json", content: JSON.stringify({ compilerOptions }) }; @@ -1013,26 +1013,26 @@ namespace ts { ] } }; - const app: FileOrFolder = { + const app: File = { path: "/src/packages/framework/app.ts", content: 'import classc from "module1/lib/file1";\ import classD from "module3/file3";\ let x = new classc();\ let y = new classD();' }; - const module1: FileOrFolder = { + const module1: File = { path: "/src/packages/mail/data/module1/lib/file1.ts", content: 'import classc from "module2/file2";export default classc;', }; - const module2: FileOrFolder = { + const module2: File = { path: "/src/packages/mail/data/module1/lib/module2/file2.ts", content: 'class classc { method2() { return "hello"; } }\nexport default classc', }; - const module3: FileOrFolder = { + const module3: File = { path: "/src/packages/styles/module3/file3.ts", content: "class classD { method() { return 10; } }\nexport default classD;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/src/tsconfig.json", content: JSON.stringify({ compilerOptions, include: ["packages/**/*.ts"] }) }; diff --git a/src/harness/unittests/telemetry.ts b/src/harness/unittests/telemetry.ts index 27512ba1e8c..e3649ae1c79 100644 --- a/src/harness/unittests/telemetry.ts +++ b/src/harness/unittests/telemetry.ts @@ -42,7 +42,7 @@ namespace ts.projectSystem { const et = new TestServerEventManager([...files, notIncludedFile, tsconfig]); et.service.openClientFile(files[0].path); et.assertProjectInfoTelemetryEvent({ - fileStats: { ts: 2, tsx: 1, js: 1, jsx: 1, dts: 1 }, + fileStats: fileStats({ ts: 2, tsx: 1, js: 1, jsx: 1, dts: 1 }), compilerOptions, include: true, }); @@ -234,9 +234,44 @@ namespace ts.projectSystem { languageServiceEnabled: false, }); }); + + describe("open files telemetry", () => { + it("sends event for inferred project", () => { + const ajs = makeFile("/a.js", "// @ts-check\nconst x = 0;"); + const bjs = makeFile("/b.js"); + const et = new TestServerEventManager([ajs, bjs]); + + et.service.openClientFile(ajs.path); + et.assertOpenFileTelemetryEvent({ checkJs: true }); + + et.service.openClientFile(bjs.path); + et.assertOpenFileTelemetryEvent({ checkJs: false }); + + // No repeated send for opening a file seen before. + et.service.openClientFile(bjs.path); + et.assertNoOpenFilesTelemetryEvent(); + }); + + it("not for '.ts' file", () => { + const ats = makeFile("/a.ts", ""); + const et = new TestServerEventManager([ats]); + + et.service.openClientFile(ats.path); + et.assertNoOpenFilesTelemetryEvent(); + }); + + it("even for project with 'ts-check' in config", () => { + const file = makeFile("/a.js"); + const compilerOptions: CompilerOptions = { checkJs: true }; + const jsconfig = makeFile("/jsconfig.json", { compilerOptions }); + const et = new TestServerEventManager([jsconfig, file]); + et.service.openClientFile(file.path); + et.assertOpenFileTelemetryEvent({ checkJs: false }); + }); + }); }); - function makeFile(path: string, content: {} = ""): FileOrFolder { - return { path, content: isString(content) ? "" : JSON.stringify(content) }; + function makeFile(path: string, content: {} = ""): File { + return { path, content: isString(content) ? content : JSON.stringify(content) }; } } diff --git a/src/harness/unittests/transform.ts b/src/harness/unittests/transform.ts index 3cca8f6210c..14b9ccdbfb8 100644 --- a/src/harness/unittests/transform.ts +++ b/src/harness/unittests/transform.ts @@ -250,6 +250,26 @@ namespace ts { } } }); + + testBaseline("transformDeclarationFile", () => { + return baselineDeclarationTransform(`var oldName = undefined;`, { + transformers: { + afterDeclarations: [replaceIdentifiersNamedOldNameWithNewName] + }, + compilerOptions: { + newLine: NewLineKind.CarriageReturnLineFeed, + declaration: true + } + }); + }); + + function baselineDeclarationTransform(text: string, opts: TranspileOptions) { + const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true, { documents: [new documents.TextDocument("/.src/index.ts", text)] }); + const host = new fakes.CompilerHost(fs, opts.compilerOptions); + const program = createProgram(["/.src/index.ts"], opts.compilerOptions, host); + program.emit(program.getSourceFiles()[1], (p, s, bom) => host.writeFile(p, s, bom), /*cancellationToken*/ undefined, /*onlyDts*/ true, opts.transformers); + return fs.readFileSync("/.src/index.d.ts").toString(); + } }); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index b64b02e301a..f3194d68dcd 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -4,7 +4,8 @@ namespace ts.tscWatch { import WatchedSystem = TestFSWithWatch.TestServerHost; - type FileOrFolder = TestFSWithWatch.FileOrFolder; + type File = TestFSWithWatch.File; + type SymLink = TestFSWithWatch.SymLink; import createWatchedSystem = TestFSWithWatch.createWatchedSystem; import checkArray = TestFSWithWatch.checkArray; import libFile = TestFSWithWatch.libFile; @@ -33,7 +34,7 @@ namespace ts.tscWatch { return () => watch.getCurrentProgram().getProgram(); } - function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { + function getEmittedLineForMultiFileOutput(file: File, host: WatchedSystem) { return `TSFILE: ${file.path.replace(".ts", ".js")}${host.newLine}`; } @@ -41,11 +42,11 @@ namespace ts.tscWatch { return `TSFILE: ${filename}${host.newLine}`; } - interface FileOrFolderEmit extends FileOrFolder { + interface FileOrFolderEmit extends File { output?: string; } - function getFileOrFolderEmit(file: FileOrFolder, getOutput?: (file: FileOrFolder) => string): FileOrFolderEmit { + function getFileOrFolderEmit(file: File, getOutput?: (file: File) => string): FileOrFolderEmit { const result = file as FileOrFolderEmit; if (getOutput) { result.output = getOutput(file); @@ -202,7 +203,7 @@ namespace ts.tscWatch { return getDiagnosticOfFileFrom(file, text, start, length, message); } - function getUnknownCompilerOption(program: Program, configFile: FileOrFolder, option: string) { + function getUnknownCompilerOption(program: Program, configFile: File, option: string) { const quotedOption = `"${option}"`; return getDiagnosticOfFile(program.getCompilerOptions().configFile, configFile.content.indexOf(quotedOption), quotedOption.length, Diagnostics.Unknown_compiler_option_0, option); } @@ -218,23 +219,23 @@ namespace ts.tscWatch { text, start, length, message); } - function getDiagnosticModuleNotFoundOfFile(program: Program, file: FileOrFolder, moduleName: string) { + function getDiagnosticModuleNotFoundOfFile(program: Program, file: File, moduleName: string) { const quotedModuleName = `"${moduleName}"`; return getDiagnosticOfFileFromProgram(program, file.path, file.content.indexOf(quotedModuleName), quotedModuleName.length, Diagnostics.Cannot_find_module_0, moduleName); } describe("tsc-watch program updates", () => { - const commonFile1: FileOrFolder = { + const commonFile1: File = { path: "/a/b/commonFile1.ts", content: "let x = 1" }; - const commonFile2: FileOrFolder = { + const commonFile2: File = { path: "/a/b/commonFile2.ts", content: "let y = 1" }; it("create watch without config file", () => { - const appFile: FileOrFolder = { + const appFile: File = { path: "/a/b/c/app.ts", content: ` import {f} from "./module" @@ -242,7 +243,7 @@ namespace ts.tscWatch { ` }; - const moduleFile: FileOrFolder = { + const moduleFile: File = { path: "/a/b/c/module.d.ts", content: `export let x: number` }; @@ -277,7 +278,7 @@ namespace ts.tscWatch { }); it("create configured project without file list", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: ` { @@ -287,15 +288,15 @@ namespace ts.tscWatch { ] }` }; - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/c/f1.ts", content: "let x = 1" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/d/f2.ts", content: "let y = 1" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/e/f3.ts", content: "let z = 1" }; @@ -315,7 +316,7 @@ namespace ts.tscWatch { // }); it("add new files to a configured program without file list", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{}` }; @@ -333,7 +334,7 @@ namespace ts.tscWatch { }); it("should ignore non-existing files specified in the config file", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, @@ -352,7 +353,7 @@ namespace ts.tscWatch { }); it("handle recreated files correctly", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{}` }; @@ -373,7 +374,7 @@ namespace ts.tscWatch { it("handles the missing files - that were added to program because they were added with /// { const commonFile2Name = "commonFile2.ts"; - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/commonFile1.ts", content: `/// let x = y` @@ -396,7 +397,7 @@ namespace ts.tscWatch { }); it("should reflect change in config file", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, @@ -419,7 +420,7 @@ namespace ts.tscWatch { }); it("works correctly when config file is changed but its content havent", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, @@ -441,14 +442,14 @@ namespace ts.tscWatch { }); it("files explicitly excluded in config file", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, "exclude": ["/a/c"] }` }; - const excludedFile1: FileOrFolder = { + const excludedFile1: File = { path: "/a/c/excluedFile1.ts", content: `let t = 1;` }; @@ -459,19 +460,19 @@ namespace ts.tscWatch { }); it("should properly handle module resolution changes in config file", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: `import { T } from "module1";` }; - const nodeModuleFile: FileOrFolder = { + const nodeModuleFile: File = { path: "/a/b/node_modules/module1.ts", content: `export interface T {}` }; - const classicModuleFile: FileOrFolder = { + const classicModuleFile: File = { path: "/a/module1.ts", content: `export interface T {}` }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -499,7 +500,7 @@ namespace ts.tscWatch { }); it("should tolerate config file errors and still try to build a project", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1117,15 +1118,15 @@ namespace ts.tscWatch { it("should not trigger recompilation because of program emit", () => { const proj = "/user/username/projects/myproject"; - const file1: FileOrFolder = { + const file1: File = { path: `${proj}/file1.ts`, content: "export const c = 30;" }; - const file2: FileOrFolder = { + const file2: File = { path: `${proj}/src/file2.ts`, content: `import {c} from "file1"; export const d = 30;` }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${proj}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { @@ -1153,7 +1154,7 @@ namespace ts.tscWatch { one(); } }`; - const file: FileOrFolder = { + const file: File = { path: "/a/b/file.ts", content: getFileContent(/*asModule*/ false) }; @@ -1174,11 +1175,11 @@ namespace ts.tscWatch { it("watched files when file is deleted and new file is added as part of change", () => { const projectLocation = "/home/username/project"; - const file: FileOrFolder = { + const file: File = { path: `${projectLocation}/src/file1.ts`, content: "var a = 10;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectLocation}/tsconfig.json`, content: "{}" }; @@ -1211,7 +1212,7 @@ namespace ts.tscWatch { }) }; - let getOutput: (file: FileOrFolder) => string; + let getOutput: (file: File) => string; if (out) { config.content = JSON.stringify({ compilerOptions: { listEmittedFiles: true, out } @@ -1266,23 +1267,23 @@ namespace ts.tscWatch { }); function verifyFilesEmittedOnce(useOutFile: boolean) { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/output/AnotherDependency/file1.d.ts", content: "declare namespace Common.SomeComponent.DynamicMenu { enum Z { Full = 0, Min = 1, Average = 2, } }" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/dependencies/file2.d.ts", content: "declare namespace Dependencies.SomeComponent { export class SomeClass { version: string; } }" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/project/src/main.ts", content: "namespace Main { export function fooBar() {} }" }; - const file4: FileOrFolder = { + const file4: File = { path: "/a/b/project/src/main2.ts", content: "namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/project/tsconfig.json", content: JSON.stringify({ compilerOptions: useOutFile ? @@ -1334,16 +1335,16 @@ namespace ts.tscWatch { /** list of the files that will be emitted for first compilation */ firstCompilationEmitFiles?: string[]; /** get the emit file for file - default is multi file emit line */ - getEmitLine?(file: FileOrFolder, host: WatchedSystem): string; + getEmitLine?(file: File, host: WatchedSystem): string; /** Additional files and folders to add */ - getAdditionalFileOrFolder?(): FileOrFolder[]; + getAdditionalFileOrFolder?(): File[]; /** initial list of files to emit if not the default list */ firstReloadFileList?: string[]; } function getInitialState({ configObj = {}, firstCompilationEmitFiles, getEmitLine, getAdditionalFileOrFolder, firstReloadFileList }: InitialStateParams = {}) { const host = createWatchedSystem([]); - const getOutputName = getEmitLine ? (file: FileOrFolder) => getEmitLine(file, host) : - (file: FileOrFolder) => getEmittedLineForMultiFileOutput(file, host); + const getOutputName = getEmitLine ? (file: File) => getEmitLine(file, host) : + (file: File) => getEmittedLineForMultiFileOutput(file, host); const moduleFile1 = getFileOrFolderEmit({ path: moduleFile1Path, @@ -1555,7 +1556,7 @@ namespace ts.tscWatch { }); it("should return cascaded affected file list", () => { - const file1Consumer1Consumer1: FileOrFolder = { + const file1Consumer1Consumer1: File = { path: "/a/b/file1Consumer1Consumer1.ts", content: `import {y} from "./file1Consumer1";` }; @@ -1582,13 +1583,13 @@ namespace ts.tscWatch { it("should work fine for files with circular references", () => { // TODO: do not exit on such errors? Just continue to watch the files for update in watch mode - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: ` /// export var t1 = 10;` }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/file2.ts", content: ` /// @@ -1611,7 +1612,7 @@ namespace ts.tscWatch { }); it("should detect removed code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// @@ -1632,7 +1633,7 @@ namespace ts.tscWatch { }); it("should detect non-existing code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// @@ -1659,7 +1660,7 @@ namespace ts.tscWatch { }); describe("tsc-watch emit file content", () => { - interface EmittedFile extends FileOrFolder { + interface EmittedFile extends File { shouldBeWritten: boolean; } function getEmittedFiles(files: FileOrFolderEmit[], contents: string[]): EmittedFile[] { @@ -1684,8 +1685,8 @@ namespace ts.tscWatch { } } - function verifyEmittedFileContents(newLine: string, inputFiles: FileOrFolder[], initialEmittedFileContents: string[], - modifyFiles: (files: FileOrFolderEmit[], emitedFiles: EmittedFile[]) => FileOrFolderEmit[], configFile?: FileOrFolder) { + function verifyEmittedFileContents(newLine: string, inputFiles: File[], initialEmittedFileContents: string[], + modifyFiles: (files: FileOrFolderEmit[], emitedFiles: EmittedFile[]) => FileOrFolderEmit[], configFile?: File) { const host = createWatchedSystem([], { newLine }); const files = concatenate( map(inputFiles, file => getFileOrFolderEmit(file, fileToConvert => getEmittedLineForMultiFileOutput(fileToConvert, host))), @@ -1778,15 +1779,15 @@ namespace ts.tscWatch { it("Elides const enums correctly in incremental compilation", () => { const currentDirectory = "/user/someone/projects/myproject"; - const file1: FileOrFolder = { + const file1: File = { path: `${currentDirectory}/file1.ts`, content: "export const enum E1 { V = 1 }" }; - const file2: FileOrFolder = { + const file2: File = { path: `${currentDirectory}/file2.ts`, content: `import { E1 } from "./file1"; export const enum E2 { V = E1.V }` }; - const file3: FileOrFolder = { + const file3: File = { path: `${currentDirectory}/file3.ts`, content: `import { E2 } from "./file2"; const v: E2 = E2.V;` }; @@ -1808,12 +1809,12 @@ namespace ts.tscWatch { it("file is deleted and created as part of change", () => { const projectLocation = "/home/username/project"; - const file: FileOrFolder = { + const file: File = { path: `${projectLocation}/app/file.ts`, content: "var a = 10;" }; const fileJs = `${projectLocation}/app/file.js`; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectLocation}/tsconfig.json`, content: JSON.stringify({ include: [ @@ -2101,19 +2102,19 @@ declare module "fs" { it("works when reusing program with files from external library", () => { interface ExpectedFile { path: string; isExpectedToEmit?: boolean; content?: string; } const configDir = "/a/b/projects/myProject/src/"; - const file1: FileOrFolder = { + const file1: File = { path: configDir + "file1.ts", content: 'import module1 = require("module1");\nmodule1("hello");' }; - const file2: FileOrFolder = { + const file2: File = { path: configDir + "file2.ts", content: 'import module11 = require("module1");\nmodule11("hello");' }; - const module1: FileOrFolder = { + const module1: File = { path: "/a/b/projects/myProject/node_modules/module1/index.js", content: "module.exports = options => { return options.toString(); }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: configDir + "tsconfig.json", content: JSON.stringify({ compilerOptions: { @@ -2169,7 +2170,7 @@ declare module "fs" { }; } - function createExpectedEmittedFile(file: FileOrFolder): ExpectedFile { + function createExpectedEmittedFile(file: File): ExpectedFile { return { path: removeFileExtension(file.path.replace(configDir, outDirFolder)) + Extension.Js, isExpectedToEmit: true, @@ -2180,11 +2181,11 @@ declare module "fs" { it("works when renaming node_modules folder that already contains @types folder", () => { const currentDirectory = "/user/username/projects/myproject"; - const file: FileOrFolder = { + const file: File = { path: `${currentDirectory}/a.ts`, content: `import * as q from "qqq";` }; - const module: FileOrFolder = { + const module: File = { path: `${currentDirectory}/node_modules2/@types/qqq/index.d.ts`, content: "export {}" }; @@ -2203,7 +2204,7 @@ declare module "fs" { describe("tsc-watch with when module emit is specified as node", () => { it("when instead of filechanged recursive directory watcher is invoked", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/rootFolder/project/tsconfig.json", content: JSON.stringify({ compilerOptions: { @@ -2217,11 +2218,11 @@ declare module "fs" { }) }; const outputFolder = "/a/rootFolder/project/Static/scripts/"; - const file1: FileOrFolder = { + const file1: File = { path: "/a/rootFolder/project/Scripts/TypeScript.ts", content: "var z = 10;" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/rootFolder/project/Scripts/Javascript.js", content: "var zz = 10;" }; @@ -2299,7 +2300,7 @@ declare module "fs" { describe("tsc-watch with different polling/non polling options", () => { it("watchFile using dynamic priority polling", () => { const projectFolder = "/a/username/project"; - const file1: FileOrFolder = { + const file1: File = { path: `${projectFolder}/typescript.ts`, content: "var z = 10;" }; @@ -2362,11 +2363,11 @@ declare module "fs" { function verifyRenamingFileInSubFolder(tscWatchDirectory: TestFSWithWatch.Tsc_WatchDirectory) { const projectFolder = "/a/username/project"; const projectSrcFolder = `${projectFolder}/src`; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectFolder}/tsconfig.json`, content: "{}" }; - const file: FileOrFolder = { + const file: File = { path: `${projectSrcFolder}/file1.ts`, content: "" }; @@ -2428,35 +2429,35 @@ declare module "fs" { it("when there are symlinks to folders in recursive folders", () => { const cwd = "/home/user/projects/myproject"; - const file1: FileOrFolder = { + const file1: File = { path: `${cwd}/src/file.ts`, content: `import * as a from "a"` }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${cwd}/tsconfig.json`, content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}` }; - const realA: FileOrFolder = { + const realA: File = { path: `${cwd}/node_modules/reala/index.d.ts`, content: `export {}` }; - const realB: FileOrFolder = { + const realB: File = { path: `${cwd}/node_modules/realb/index.d.ts`, content: `export {}` }; - const symLinkA: FileOrFolder = { + const symLinkA: SymLink = { path: `${cwd}/node_modules/a`, symLink: `${cwd}/node_modules/reala` }; - const symLinkB: FileOrFolder = { + const symLinkB: SymLink = { path: `${cwd}/node_modules/b`, symLink: `${cwd}/node_modules/realb` }; - const symLinkBInA: FileOrFolder = { + const symLinkBInA: SymLink = { path: `${cwd}/node_modules/reala/node_modules/b`, symLink: `${cwd}/node_modules/b` }; - const symLinkAInB: FileOrFolder = { + const symLinkAInB: SymLink = { path: `${cwd}/node_modules/realb/node_modules/a`, symLink: `${cwd}/node_modules/a` }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 5f04b643a35..f26170ef6ae 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -8,7 +8,9 @@ namespace ts.projectSystem { import CommandNames = server.CommandNames; export import TestServerHost = TestFSWithWatch.TestServerHost; - export type FileOrFolder = TestFSWithWatch.FileOrFolder; + export type File = TestFSWithWatch.File; + export type SymLink = TestFSWithWatch.SymLink; + export type Folder = TestFSWithWatch.Folder; export import createServerHost = TestFSWithWatch.createServerHost; export import checkArray = TestFSWithWatch.checkArray; export import libFile = TestFSWithWatch.libFile; @@ -170,7 +172,7 @@ namespace ts.projectSystem { readonly session: TestSession; readonly service: server.ProjectService; readonly host: TestServerHost; - constructor(files: FileOrFolder[], suppressDiagnosticEvents?: boolean) { + constructor(files: File[], suppressDiagnosticEvents?: boolean) { this.host = createServerHost(files); this.session = createSession(this.host, { canUseEvents: true, @@ -213,7 +215,7 @@ namespace ts.projectSystem { } assertProjectInfoTelemetryEvent(partial: Partial, configFile?: string): void { - assert.deepEqual(this.getEvent(server.ProjectInfoTelemetryEvent), { + assert.deepEqual(this.getEvent(server.ProjectInfoTelemetryEvent), { projectId: Harness.mockHash(configFile || "/tsconfig.json"), fileStats: fileStats({ ts: 1 }), compilerOptions: {}, @@ -234,6 +236,13 @@ namespace ts.projectSystem { ...partial, }); } + + assertOpenFileTelemetryEvent(info: server.OpenFileInfo): void { + assert.deepEqual(this.getEvent(server.OpenFileInfoTelemetryEvent), { info }); + } + assertNoOpenFilesTelemetryEvent(): void { + this.hasZeroEvent(server.OpenFileInfoTelemetryEvent); + } } class TestSession extends server.Session { @@ -394,7 +403,7 @@ namespace ts.projectSystem { return countWhere(recursiveWatchedDirs, dir => file.length > dir.length && startsWith(file, dir) && file[dir.length] === directorySeparator); } - function checkOpenFiles(projectService: server.ProjectService, expectedFiles: FileOrFolder[]) { + function checkOpenFiles(projectService: server.ProjectService, expectedFiles: File[]) { checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path).fileName), expectedFiles.map(file => file.path)); } @@ -451,7 +460,7 @@ namespace ts.projectSystem { return newRequest; } - export function openFilesForSession(files: FileOrFolder[], session: server.Session) { + export function openFilesForSession(files: File[], session: server.Session) { for (const file of files) { const request = makeSessionRequest(CommandNames.Open, { file: file.path }); session.executeCommand(request); @@ -512,17 +521,17 @@ namespace ts.projectSystem { } describe("tsserverProjectSystem general functionality", () => { - const commonFile1: FileOrFolder = { + const commonFile1: File = { path: "/a/b/commonFile1.ts", content: "let x = 1" }; - const commonFile2: FileOrFolder = { + const commonFile2: File = { path: "/a/b/commonFile2.ts", content: "let y = 1" }; it("create inferred project", () => { - const appFile: FileOrFolder = { + const appFile: File = { path: "/a/b/c/app.ts", content: ` import {f} from "./module" @@ -530,7 +539,7 @@ namespace ts.projectSystem { ` }; - const moduleFile: FileOrFolder = { + const moduleFile: File = { path: "/a/b/c/module.d.ts", content: `export let x: number` }; @@ -583,7 +592,7 @@ namespace ts.projectSystem { }); it("create configured project without file list", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: ` { @@ -593,15 +602,15 @@ namespace ts.projectSystem { ] }` }; - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/c/f1.ts", content: "let x = 1" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/d/f2.ts", content: "let y = 1" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/e/f3.ts", content: "let z = 1" }; @@ -625,7 +634,7 @@ namespace ts.projectSystem { }); it("create configured project with the file list", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: ` { @@ -633,15 +642,15 @@ namespace ts.projectSystem { "include": ["*.ts"] }` }; - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/f1.ts", content: "let x = 1" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/f2.ts", content: "let y = 1" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/c/f3.ts", content: "let z = 1" }; @@ -664,7 +673,7 @@ namespace ts.projectSystem { }); it("add and then remove a config file in a folder with loose files", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "files": ["commonFile1.ts"] @@ -713,7 +722,7 @@ namespace ts.projectSystem { }); it("add new files to a configured project without file list", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{}` }; @@ -735,7 +744,7 @@ namespace ts.projectSystem { }); it("should ignore non-existing files specified in the config file", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, @@ -769,7 +778,7 @@ namespace ts.projectSystem { path: "/c/app.ts", content: "let x = 1" }; - const makeProject = (f: FileOrFolder) => ({ projectFileName: f.path + ".csproj", rootFiles: [toExternalFile(f.path)], options: {} }); + const makeProject = (f: File) => ({ projectFileName: f.path + ".csproj", rootFiles: [toExternalFile(f.path)], options: {} }); const p1 = makeProject(f1); const p2 = makeProject(f2); const p3 = makeProject(f3); @@ -817,7 +826,7 @@ namespace ts.projectSystem { }); it("handle recreated files correctly", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{}` }; @@ -841,7 +850,7 @@ namespace ts.projectSystem { }); it("handles the missing files - that were added to program because they were added with /// { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/commonFile1.ts", content: `/// let x = y` @@ -878,7 +887,7 @@ namespace ts.projectSystem { }); it("should create new inferred projects for files excluded from a configured project", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, @@ -909,14 +918,14 @@ namespace ts.projectSystem { }); it("files explicitly excluded in config file", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": {}, "exclude": ["/a/c"] }` }; - const excludedFile1: FileOrFolder = { + const excludedFile1: File = { path: "/a/c/excluedFile1.ts", content: `let t = 1;` }; @@ -933,23 +942,23 @@ namespace ts.projectSystem { }); it("should properly handle module resolution changes in config file", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: `import { T } from "module1";` }; - const nodeModuleFile: FileOrFolder = { + const nodeModuleFile: File = { path: "/a/b/node_modules/module1.ts", content: `export interface T {}` }; - const classicModuleFile: FileOrFolder = { + const classicModuleFile: File = { path: "/a/module1.ts", content: `export interface T {}` }; - const randomFile: FileOrFolder = { + const randomFile: File = { path: "/a/file1.ts", content: `export interface T {}` }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -998,15 +1007,15 @@ namespace ts.projectSystem { }); it("should keep the configured project when the opened file is referenced by the project but not its root", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/main.ts", content: "import { objA } from './obj-a';" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/obj-a.ts", content: `export const objA = Object.assign({foo: "bar"}, {bar: "baz"});` }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1025,15 +1034,15 @@ namespace ts.projectSystem { }); it("should keep the configured project when the opened file is referenced by the project but not its root", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/main.ts", content: "import { objA } from './obj-a';" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/obj-a.ts", content: `export const objA = Object.assign({foo: "bar"}, {bar: "baz"});` }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1051,7 +1060,7 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 0); }); it("should tolerate config file errors and still try to build a project", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1108,7 +1117,7 @@ namespace ts.projectSystem { path: "/a/b/main.ts", content: "let x =1;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1154,7 +1163,7 @@ namespace ts.projectSystem { path: "/a/b/main2.ts", content: "let y =1;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -1188,7 +1197,7 @@ namespace ts.projectSystem { path: "/a/b/main.ts", content: "let x =1;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{ "compilerOptions": { @@ -2449,19 +2458,19 @@ namespace ts.projectSystem { }); it("Open ref of configured project when open file gets added to the project as part of configured file update", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/src/file1.ts", content: "let x = 1;" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/src/file2.ts", content: "let y = 1;" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/file3.ts", content: "let z = 1;" }; - const file4: FileOrFolder = { + const file4: File = { path: "/a/file4.ts", content: "let z = 1;" }; @@ -2527,7 +2536,7 @@ namespace ts.projectSystem { checkProjectActualFiles(inferredProject4, [file4.path]); assert.strictEqual(inferredProject5, inferredProject4); - const file5: FileOrFolder = { + const file5: File = { path: "/file5.ts", content: "let zz = 1;" }; @@ -2545,7 +2554,7 @@ namespace ts.projectSystem { infos.forEach(info => assert.strictEqual(projectService.getScriptInfoForPath(info.path), info)); } - function verifyScriptInfosAreUndefined(files: FileOrFolder[]) { + function verifyScriptInfosAreUndefined(files: File[]) { for (const file of files) { assert.isUndefined(projectService.getScriptInfoForPath(file.path as Path)); } @@ -2561,19 +2570,19 @@ namespace ts.projectSystem { }); it("Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/src/file1.ts", content: "let x = 1;" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/src/file2.ts", content: "let y = 1;" }; - const file3: FileOrFolder = { + const file3: File = { path: "/a/b/file3.ts", content: "let z = 1;" }; - const file4: FileOrFolder = { + const file4: File = { path: "/a/file4.ts", content: "let z = 1;" }; @@ -2753,7 +2762,7 @@ namespace ts.projectSystem { const session = createSession(host, { canUseEvents: true, eventHandler: e => { - if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent) { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent) { return; } assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); @@ -2805,7 +2814,7 @@ namespace ts.projectSystem { const session = createSession(host, { canUseEvents: true, eventHandler: e => { - if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent) { return; } assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); @@ -2943,19 +2952,19 @@ namespace ts.projectSystem { }); it("Changed module resolution reflected when specifying files list", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: 'import classc from "file2"' }; - const file2a: FileOrFolder = { + const file2a: File = { path: "/a/file2.ts", content: "export classc { method2a() { return 10; } }" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/file2.ts", content: "export classc { method2() { return 10; } }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: JSON.stringify({ files: [file1.path], compilerOptions: { module: "amd" } }) }; @@ -2997,24 +3006,24 @@ namespace ts.projectSystem { it("Failed lookup locations uses parent most node_modules directory", () => { const root = "/user/username/rootfolder"; - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/src/file1.ts", content: 'import { classc } from "module1"' }; - const module1: FileOrFolder = { + const module1: File = { path: "/a/b/node_modules/module1/index.d.ts", content: `import { class2 } from "module2"; export classc { method2a(): class2; }` }; - const module2: FileOrFolder = { + const module2: File = { path: "/a/b/node_modules/module2/index.d.ts", content: "export class2 { method2() { return 10; } }" }; - const module3: FileOrFolder = { + const module3: File = { path: "/a/b/node_modules/module/node_modules/module3/index.d.ts", content: "export class3 { method2() { return 10; } }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/src/tsconfig.json", content: JSON.stringify({ files: ["file1.ts"] }) }; @@ -3036,7 +3045,7 @@ namespace ts.projectSystem { }); it("Properly handle Windows-style outDir", () => { - const configFile: FileOrFolder = { + const configFile: File = { path: "C:\\a\\tsconfig.json", content: JSON.stringify({ compilerOptions: { @@ -3045,7 +3054,7 @@ namespace ts.projectSystem { include: ["*.ts"] }) }; - const file1: FileOrFolder = { + const file1: File = { path: "C:\\a\\f1.ts", content: "let x = 1;" }; @@ -3062,7 +3071,7 @@ namespace ts.projectSystem { }); it("dynamic file without external project", () => { - const file: FileOrFolder = { + const file: File = { path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js", content: "var x = 10;" }; @@ -3101,19 +3110,19 @@ namespace ts.projectSystem { }); it("files opened, closed affecting multiple projects", () => { - const file: FileOrFolder = { + const file: File = { path: "/a/b/projects/config/file.ts", content: `import {a} from "../files/file1"; export let b = a;` }; - const config: FileOrFolder = { + const config: File = { path: "/a/b/projects/config/tsconfig.json", content: "" }; - const filesFile1: FileOrFolder = { + const filesFile1: File = { path: "/a/b/projects/files/file1.ts", content: "export let a = 10;" }; - const filesFile2: FileOrFolder = { + const filesFile2: File = { path: "/a/b/projects/files/file2.ts", content: "export let aa = 10;" }; @@ -3179,15 +3188,15 @@ namespace ts.projectSystem { it("requests are done on file on pendingReload but has svc for previous version", () => { const projectLocation = "/user/username/projects/project"; - const file1: FileOrFolder = { + const file1: File = { path: `${projectLocation}/src/file1.ts`, content: `import { y } from "./file2"; let x = 10;` }; - const file2: FileOrFolder = { + const file2: File = { path: `${projectLocation}/src/file2.ts`, content: "export let y = 10;" }; - const config: FileOrFolder = { + const config: File = { path: `${projectLocation}/tsconfig.json`, content: "{}" }; @@ -3262,18 +3271,18 @@ namespace ts.projectSystem { it("Orphan source files are handled correctly on watch trigger", () => { const projectLocation = "/user/username/projects/project"; - const file1: FileOrFolder = { + const file1: File = { path: `${projectLocation}/src/file1.ts`, content: `export let x = 10;` }; - const file2: FileOrFolder = { + const file2: File = { path: `${projectLocation}/src/file2.ts`, content: "export let y = 10;" }; const configContent1 = JSON.stringify({ files: ["src/file1.ts", "src/file2.ts"] }); - const config: FileOrFolder = { + const config: File = { path: `${projectLocation}/tsconfig.json`, content: configContent1 }; @@ -3424,15 +3433,15 @@ namespace ts.projectSystem { it("folder rename updates project structure and reports no errors", () => { const projectDir = "/a/b/projects/myproject"; - const app: FileOrFolder = { + const app: File = { path: `${projectDir}/bar/app.ts`, content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }" }; - const foo: FileOrFolder = { + const foo: File = { path: `${projectDir}/foo/foo.ts`, content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectDir}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { module: "none", targer: "es5" }, exclude: ["node_modules"] }) }; @@ -3479,7 +3488,7 @@ namespace ts.projectSystem { }); it("Getting errors before opening file", () => { - const file: FileOrFolder = { + const file: File = { path: "/a/b/project/file.ts", content: "let x: number = false;" }; @@ -3505,15 +3514,15 @@ namespace ts.projectSystem { it("Reports errors correctly when file referenced by inferred project root, is opened right after closing the root file", () => { const projectRoot = "/user/username/projects/myproject"; - const app: FileOrFolder = { + const app: File = { path: `${projectRoot}/src/client/app.js`, content: "" }; - const serverUtilities: FileOrFolder = { + const serverUtilities: File = { path: `${projectRoot}/src/server/utilities.js`, content: `function getHostName() { return "hello"; } export { getHostName };` }; - const backendTest: FileOrFolder = { + const backendTest: File = { path: `${projectRoot}/test/backend/index.js`, content: `import { getHostName } from '../../src/server/utilities';export default getHostName;` }; @@ -3657,11 +3666,11 @@ namespace ts.projectSystem { } it("should not include type symbols", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.js", content: "function foo() {}" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/jsconfig.json", content: "{}" }; @@ -4050,19 +4059,19 @@ namespace ts.projectSystem { } function verifyOpenFileWorks(useCaseSensitiveFileNames: boolean) { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/src/app.ts", content: "let x = 10;" }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/B/lib/module2.ts", content: "let z = 10;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: "" }; - const configFile2: FileOrFolder = { + const configFile2: File = { path: "/a/tsconfig.json", content: "" }; @@ -4081,7 +4090,7 @@ namespace ts.projectSystem { verifyConfigFileName(file2, "/a/b", useCaseSensitiveFileNames ? configFile2 : configFile); verifyConfigFileName(file2, "/a/B", useCaseSensitiveFileNames ? undefined : configFile); - function verifyConfigFileName(file: FileOrFolder, projectRoot: string, expectedConfigFile: FileOrFolder | undefined) { + function verifyConfigFileName(file: File, projectRoot: string, expectedConfigFile: File | undefined) { const { configFileName } = service.openClientFile(file.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectRoot); assert.equal(configFileName, expectedConfigFile && expectedConfigFile.path); service.closeClientFile(file.path); @@ -4097,11 +4106,11 @@ namespace ts.projectSystem { it("uses existing project even if project refresh is pending", () => { const projectFolder = "/user/someuser/projects/myproject"; - const aFile: FileOrFolder = { + const aFile: File = { path: `${projectFolder}/src/a.ts`, content: "export const x = 0;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectFolder}/tsconfig.json`, content: "{}" }; @@ -4111,7 +4120,7 @@ namespace ts.projectSystem { service.openClientFile(aFile.path, /*fileContent*/ undefined, ScriptKind.TS, projectFolder); verifyProject(); - const bFile: FileOrFolder = { + const bFile: File = { path: `${projectFolder}/src/b.ts`, content: `export {}; declare module "./a" { export const y: number; }` }; @@ -4320,7 +4329,7 @@ namespace ts.projectSystem { it("npm install @types works", () => { const folderPath = "/a/b/projects/temp"; - const file1: FileOrFolder = { + const file1: File = { path: `${folderPath}/a.ts`, content: 'import f = require("pad"); f;' }; @@ -4367,7 +4376,7 @@ namespace ts.projectSystem { checkCompleteEvent(session, 2, expectedSequenceId); session.clearMessages(); - const padIndex: FileOrFolder = { + const padIndex: File = { path: `${folderPath}/node_modules/@types/pad/index.d.ts`, content: "export = pad;declare function pad(length: number, text: string, char ?: string): string;" }; @@ -4386,7 +4395,7 @@ namespace ts.projectSystem { }); it("suggestion diagnostics", () => { - const file: FileOrFolder = { + const file: File = { path: "/a.js", content: "function f(p) {}", }; @@ -4439,7 +4448,7 @@ namespace ts.projectSystem { }); it("disable suggestion diagnostics", () => { - const file: FileOrFolder = { + const file: File = { path: "/a.js", content: 'require("b")', }; @@ -4490,7 +4499,7 @@ namespace ts.projectSystem { }); it("suppressed diagnostic events", () => { - const file: FileOrFolder = { + const file: File = { path: "/a.ts", content: "1 = 2;", }; @@ -5261,13 +5270,13 @@ namespace ts.projectSystem { assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015); }); - function checkInferredProject(inferredProject: server.InferredProject, actualFiles: FileOrFolder[], target: ScriptTarget) { + function checkInferredProject(inferredProject: server.InferredProject, actualFiles: File[], target: ScriptTarget) { checkProjectActualFiles(inferredProject, actualFiles.map(f => f.path)); assert.equal(inferredProject.getCompilationSettings().target, target); } function verifyProjectRootWithCaseSensitivity(useCaseSensitiveFileNames: boolean) { - const files: [FileOrFolder, FileOrFolder, FileOrFolder, FileOrFolder] = [ + const files: [File, File, File, File] = [ { path: "/a/file1.ts", content: "let x = 1;" }, { path: "/A/file2.ts", content: "let y = 2;" }, { path: "/b/file2.ts", content: "let x = 3;" }, @@ -5351,7 +5360,7 @@ namespace ts.projectSystem { files.forEach(file => projectService.closeClientFile(file.path)); } - function verifyInferredProjectsState(expected: [FileOrFolder[], ScriptTarget][]) { + function verifyInferredProjectsState(expected: [File[], ScriptTarget][]) { checkNumberOfProjects(projectService, { inferredProjects: expected.length }); projectService.inferredProjects.forEach((p, index) => { const [actualFiles, target] = expected[index]; @@ -5610,11 +5619,11 @@ namespace ts.projectSystem { describe("when the opened file is not from project root", () => { const projectRoot = "/a/b/projects/project"; - const file: FileOrFolder = { + const file: File = { path: `${projectRoot}/src/index.ts`, content: "let y = 10" }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${projectRoot}/tsconfig.json`, content: "{}" }; @@ -5622,7 +5631,7 @@ namespace ts.projectSystem { const filesWithConfig = files.concat(tsconfig); const dirOfFile = getDirectoryPath(file.path); - function openClientFile(files: FileOrFolder[]) { + function openClientFile(files: File[]) { const host = createServerHost(files); const projectService = createProjectService(host); @@ -5963,7 +5972,7 @@ namespace ts.projectSystem { describe("tsserverProjectSystem occurence highlight on string", () => { it("should be marked if only on string values", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: `let t1 = "div";\nlet t2 = "div";\nlet t3 = { "div": 123 };\nlet t4 = t3["div"];` }; @@ -6009,11 +6018,11 @@ namespace ts.projectSystem { describe("tsserverProjectSystem maxNodeModuleJsDepth for inferred projects", () => { it("should be set to 2 if the project has js root files", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.js", content: `var t = require("test"); t.` }; - const moduleFile: FileOrFolder = { + const moduleFile: File = { path: "/a/b/node_modules/test/index.js", content: `var v = 10; module.exports = v;` }; @@ -6239,14 +6248,14 @@ namespace ts.projectSystem { function verifyNoCall(callback: CalledMaps) { const calledMap = calledMaps[callback]; - assert.equal(calledMap.size, 0, `${callback} shouldnt be called: ${arrayFrom(calledMap.keys())}`); + assert.equal(calledMap.size, 0, `${callback} shouldn't be called: ${arrayFrom(calledMap.keys())}`); } function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: Map) { TestFSWithWatch.checkMultiMapKeyCount(callback, calledMaps[callback], expectedKeys); } - function verifyCalledOnEachEntryNTimes(callback: CalledMaps, expectedKeys: string[], nTimes: number) { + function verifyCalledOnEachEntryNTimes(callback: CalledMaps, expectedKeys: ReadonlyArray, nTimes: number) { TestFSWithWatch.checkMultiMapEachKeyWithCount(callback, calledMaps[callback], expectedKeys, nTimes); } @@ -6254,7 +6263,7 @@ namespace ts.projectSystem { iterateOnCalledMaps(key => verifyNoCall(key)); } - function verifyNoHostCallsExceptFileExistsOnce(expectedKeys: string[]) { + function verifyNoHostCallsExceptFileExistsOnce(expectedKeys: ReadonlyArray) { verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, expectedKeys, 1); verifyNoCall(CalledMapsWithSingleArg.directoryExists); verifyNoCall(CalledMapsWithSingleArg.getDirectories); @@ -6278,12 +6287,12 @@ namespace ts.projectSystem { it("works using legacy resolution logic", () => { let rootContent = `import {x} from "f1"`; - const root: FileOrFolder = { + const root: File = { path: "/c/d/f0.ts", content: rootContent }; - const imported: FileOrFolder = { + const imported: File = { path: "/c/f1.ts", content: `foo()` }; @@ -6394,12 +6403,12 @@ namespace ts.projectSystem { }); it("loads missing files from disk", () => { - const root: FileOrFolder = { + const root: File = { path: "/c/foo.ts", content: `import {y} from "bar"` }; - const imported: FileOrFolder = { + const imported: File = { path: "/c/bar.d.ts", content: `export var y = 1` }; @@ -6431,25 +6440,25 @@ namespace ts.projectSystem { }); it("when calling goto definition of module", () => { - const clientFile: FileOrFolder = { + const clientFile: File = { path: "/a/b/controllers/vessels/client.ts", content: ` import { Vessel } from '~/models/vessel'; const v = new Vessel(); ` }; - const anotherModuleFile: FileOrFolder = { + const anotherModuleFile: File = { path: "/a/b/utils/db.ts", content: "export class Bookshelf { }" }; - const moduleFile: FileOrFolder = { + const moduleFile: File = { path: "/a/b/models/vessel.ts", content: ` import { Bookshelf } from '~/utils/db'; export class Vessel extends Bookshelf {} ` }; - const tsconfigFile: FileOrFolder = { + const tsconfigFile: File = { path: "/a/b/tsconfig.json", content: JSON.stringify({ compilerOptions: { @@ -6510,25 +6519,25 @@ namespace ts.projectSystem { const frontendDir = "/Users/someuser/work/applications/frontend"; const toCanonical: (s: string) => Path = useCaseSensitiveFileNames ? s => s as Path : s => s.toLowerCase() as Path; const canonicalFrontendDir = toCanonical(frontendDir); - const file1: FileOrFolder = { + const file1: File = { path: `${frontendDir}/src/app/utils/Analytic.ts`, content: "export class SomeClass { };" }; - const file2: FileOrFolder = { + const file2: File = { path: `${frontendDir}/src/app/redux/configureStore.ts`, content: "export class configureStore { }" }; - const file3: FileOrFolder = { + const file3: File = { path: `${frontendDir}/src/app/utils/Cookie.ts`, content: "export class Cookie { }" }; - const es2016LibFile: FileOrFolder = { + const es2016LibFile: File = { path: "/a/lib/lib.es2016.full.d.ts", content: libFile.content }; const typeRoots = ["types", "node_modules/@types"]; const types = ["node", "jest"]; - const tsconfigFile: FileOrFolder = { + const tsconfigFile: File = { path: `${frontendDir}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { @@ -6602,7 +6611,7 @@ namespace ts.projectSystem { verifyProjectAndWatchedDirectories(); callsTrackingHost.verifyNoHostCalls(); - function getFilePathIfNotOpen(f: FileOrFolder) { + function getFilePathIfNotOpen(f: File) { const path = toCanonical(f.path); const info = projectService.getScriptInfoForPath(toCanonical(f.path)); return info && info.isScriptOpen() ? undefined : path; @@ -6628,15 +6637,15 @@ namespace ts.projectSystem { describe("Subfolder invalidations correctly include parent folder failed lookup locations", () => { function runFailedLookupTest(resolution: "Node" | "Classic") { const projectLocation = "/proj"; - const file1: FileOrFolder = { + const file1: File = { path: `${projectLocation}/foo/boo/app.ts`, content: `import * as debug from "debug"` }; - const file2: FileOrFolder = { + const file2: File = { path: `${projectLocation}/foo/boo/moo/app.ts`, content: `import * as debug from "debug"` }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${projectLocation}/tsconfig.json`, content: JSON.stringify({ files: ["foo/boo/app.ts", "foo/boo/moo/app.ts"], @@ -6654,7 +6663,7 @@ namespace ts.projectSystem { assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]); assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]); - const debugTypesFile: FileOrFolder = { + const debugTypesFile: File = { path: `${projectLocation}/node_modules/debug/index.d.ts`, content: "export {}" }; @@ -6677,19 +6686,19 @@ namespace ts.projectSystem { describe("Verify npm install in directory with tsconfig file works when", () => { function verifyNpmInstall(timeoutDuringPartialInstallation: boolean) { const root = "/user/username/rootfolder/otherfolder"; - const getRootedFileOrFolder = (fileOrFolder: FileOrFolder) => { + const getRootedFileOrFolder = (fileOrFolder: File) => { fileOrFolder.path = root + fileOrFolder.path; return fileOrFolder; }; - const app: FileOrFolder = getRootedFileOrFolder({ + const app: File = getRootedFileOrFolder({ path: "/a/b/app.ts", content: "import _ from 'lodash';" }); - const tsconfigJson: FileOrFolder = getRootedFileOrFolder({ + const tsconfigJson: File = getRootedFileOrFolder({ path: "/a/b/tsconfig.json", content: '{ "compilerOptions": { } }' }); - const packageJson: FileOrFolder = getRootedFileOrFolder({ + const packageJson: File = getRootedFileOrFolder({ path: "/a/b/package.json", content: ` { @@ -6728,7 +6737,7 @@ namespace ts.projectSystem { let timeoutAfterReloadFs = timeoutDuringPartialInstallation; // Simulate npm install - const filesAndFoldersToAdd: FileOrFolder[] = [ + const filesAndFoldersToAdd: File[] = [ { path: "/a/b/node_modules" }, { path: "/a/b/node_modules/.staging/@types" }, { path: "/a/b/node_modules/.staging/lodash-b0733faa" }, @@ -6843,11 +6852,11 @@ namespace ts.projectSystem { it("when node_modules dont receive event for the @types file addition", () => { const projectLocation = "/user/username/folder/myproject"; - const app: FileOrFolder = { + const app: File = { path: `${projectLocation}/app.ts`, content: `import * as debug from "debug"` }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${projectLocation}/tsconfig.json`, content: "" }; @@ -6861,7 +6870,7 @@ namespace ts.projectSystem { checkProjectActualFiles(project, files.map(f => f.path)); assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]); - const debugTypesFile: FileOrFolder = { + const debugTypesFile: File = { path: `${projectLocation}/node_modules/@types/debug/index.d.ts`, content: "export {}" }; @@ -6892,7 +6901,7 @@ namespace ts.projectSystem { } function createVerifyInitialOpen(session: TestSession, verifyProjectsUpdatedInBackgroundEventHandler: (events: server.ProjectsUpdatedInBackgroundEvent[]) => void) { - return (file: FileOrFolder) => { + return (file: File) => { session.executeCommandSeq({ command: server.CommandNames.Open, arguments: { @@ -6906,24 +6915,24 @@ namespace ts.projectSystem { interface ProjectsUpdatedInBackgroundEventVerifier { session: TestSession; verifyProjectsUpdatedInBackgroundEventHandler(events: server.ProjectsUpdatedInBackgroundEvent[]): void; - verifyInitialOpen(file: FileOrFolder): void; + verifyInitialOpen(file: File): void; } function verifyProjectsUpdatedInBackgroundEvent(createSession: (host: TestServerHost) => ProjectsUpdatedInBackgroundEventVerifier) { it("when adding new file", () => { - const commonFile1: FileOrFolder = { + const commonFile1: File = { path: "/a/b/file1.ts", content: "export var x = 10;" }; - const commonFile2: FileOrFolder = { + const commonFile2: File = { path: "/a/b/file2.ts", content: "export var y = 10;" }; - const commonFile3: FileOrFolder = { + const commonFile3: File = { path: "/a/b/file3.ts", content: "export var z = 10;" }; - const configFile: FileOrFolder = { + const configFile: File = { path: "/a/b/tsconfig.json", content: `{}` }; @@ -6953,18 +6962,18 @@ namespace ts.projectSystem { describe("with --out or --outFile setting", () => { function verifyEventWithOutSettings(compilerOptions: CompilerOptions = {}) { - const config: FileOrFolder = { + const config: File = { path: "/a/tsconfig.json", content: JSON.stringify({ compilerOptions }) }; - const f1: FileOrFolder = { + const f1: File = { path: "/a/a.ts", content: "export let x = 1" }; - const f2: FileOrFolder = { + const f2: File = { path: "/a/b.ts", content: "export let y = 1" }; @@ -7020,32 +7029,32 @@ namespace ts.projectSystem { /** custom config file options */ configObj?: any; /** Additional files and folders to add */ - getAdditionalFileOrFolder?(): FileOrFolder[]; + getAdditionalFileOrFolder?(): File[]; /** initial list of files to reload in fs and first file in this list being the file to open */ firstReloadFileList?: string[]; } function getInitialState({ configObj = {}, getAdditionalFileOrFolder, firstReloadFileList }: InitialStateParams = {}) { - const moduleFile1: FileOrFolder = { + const moduleFile1: File = { path: moduleFile1Path, content: "export function Foo() { };", }; - const file1Consumer1: FileOrFolder = { + const file1Consumer1: File = { path: file1Consumer1Path, content: `import {Foo} from "./moduleFile1"; export var y = 10;`, }; - const file1Consumer2: FileOrFolder = { + const file1Consumer2: File = { path: "/a/b/file1Consumer2.ts", content: `import {Foo} from "./moduleFile1"; let z = 10;`, }; - const moduleFile2: FileOrFolder = { + const moduleFile2: File = { path: "/a/b/moduleFile2.ts", content: `export var Foo4 = 10;`, }; - const globalFile3: FileOrFolder = { + const globalFile3: File = { path: "/a/b/globalFile3.ts", content: `interface GlobalFoo { age: number }` }; @@ -7085,13 +7094,13 @@ namespace ts.projectSystem { return find(files, file => file.path === fileName); } - function verifyNoProjectsUpdatedInBackgroundEvent(filesToReload?: FileOrFolder[]) { + function verifyNoProjectsUpdatedInBackgroundEvent(filesToReload?: File[]) { host.reloadFS(filesToReload || files); host.runQueuedTimeoutCallbacks(); verifyProjectsUpdatedInBackgroundEventHandler([]); } - function verifyProjectsUpdatedInBackgroundEvent(filesToReload?: FileOrFolder[]) { + function verifyProjectsUpdatedInBackgroundEvent(filesToReload?: File[]) { host.reloadFS(filesToReload || files); host.runQueuedTimeoutCallbacks(); verifyProjectsUpdatedInBackgroundEventHandler([{ @@ -7102,7 +7111,7 @@ namespace ts.projectSystem { }]); } - function updateContentOfOpenFile(file: FileOrFolder, newContent: string) { + function updateContentOfOpenFile(file: File, newContent: string) { session.executeCommandSeq({ command: server.CommandNames.Change, arguments: { @@ -7172,7 +7181,7 @@ namespace ts.projectSystem { it("should be up-to-date with newly created files", () => { const { moduleFile1, files, verifyProjectsUpdatedInBackgroundEvent, } = getInitialState(); - const file1Consumer3: FileOrFolder = { + const file1Consumer3: File = { path: "/a/b/file1Consumer3.ts", content: `import {Foo} from "./moduleFile1"; let y = Foo();` }; @@ -7220,7 +7229,7 @@ namespace ts.projectSystem { }); it("should return cascaded affected file list", () => { - const file1Consumer1Consumer1: FileOrFolder = { + const file1Consumer1Consumer1: File = { path: "/a/b/file1Consumer1Consumer1.ts", content: `import {y} from "./file1Consumer1";` }; @@ -7242,13 +7251,13 @@ namespace ts.projectSystem { }); it("should work fine for files with circular references", () => { - const file1: FileOrFolder = { + const file1: File = { path: "/a/b/file1.ts", content: ` /// export var t1 = 10;` }; - const file2: FileOrFolder = { + const file2: File = { path: "/a/b/file2.ts", content: ` /// @@ -7264,7 +7273,7 @@ namespace ts.projectSystem { }); it("should detect removed code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// @@ -7279,7 +7288,7 @@ namespace ts.projectSystem { }); it("should detect non-existing code file", () => { - const referenceFile1: FileOrFolder = { + const referenceFile1: File = { path: "/a/b/referenceFile1.ts", content: ` /// @@ -7301,19 +7310,19 @@ namespace ts.projectSystem { describe("resolution when resolution cache size", () => { function verifyWithMaxCacheLimit(limitHit: boolean, useSlashRootAsSomeNotRootFolderInUserDirectory: boolean) { const rootFolder = useSlashRootAsSomeNotRootFolderInUserDirectory ? "/user/username/rootfolder/otherfolder/" : "/"; - const file1: FileOrFolder = { + const file1: File = { path: rootFolder + "a/b/project/file1.ts", content: 'import a from "file2"' }; - const file2: FileOrFolder = { + const file2: File = { path: rootFolder + "a/b/node_modules/file2.d.ts", content: "export class a { }" }; - const file3: FileOrFolder = { + const file3: File = { path: rootFolder + "a/b/project/file3.ts", content: "export class c { }" }; - const configFile: FileOrFolder = { + const configFile: File = { path: rootFolder + "a/b/project/tsconfig.json", content: JSON.stringify({ compilerOptions: { typeRoots: [] } }) }; @@ -7479,15 +7488,15 @@ namespace ts.projectSystem { describe("tsserverProjectSystem Watched recursive directories with windows style file system", () => { function verifyWatchedDirectories(useProjectAtRoot: boolean) { const root = useProjectAtRoot ? "c:/" : "c:/myfolder/allproject/"; - const configFile: FileOrFolder = { + const configFile: File = { path: root + "project/tsconfig.json", content: "{}" }; - const file1: FileOrFolder = { + const file1: File = { path: root + "project/file1.ts", content: "let x = 10;" }; - const file2: FileOrFolder = { + const file2: File = { path: root + "project/file2.ts", content: "let y = 10;" }; @@ -7520,13 +7529,13 @@ namespace ts.projectSystem { it("when projectRootPath is provided", () => { const projects = "/users/username/projects"; const projectRootPath = `${projects}/san2`; - const file: FileOrFolder = { + const file: File = { path: `${projectRootPath}/x.js`, content: "const aaaaaaav = 1;" }; const currentDirectory = `${projects}/anotherProject`; - const packageJsonInCurrentDirectory: FileOrFolder = { + const packageJsonInCurrentDirectory: File = { path: `${currentDirectory}/package.json`, content: JSON.stringify({ devDependencies: { @@ -7534,7 +7543,7 @@ namespace ts.projectSystem { }, }) }; - const packageJsonOfPkgcurrentdirectory: FileOrFolder = { + const packageJsonOfPkgcurrentdirectory: File = { path: `${currentDirectory}/node_modules/pkgcurrentdirectory/package.json`, content: JSON.stringify({ name: "pkgcurrentdirectory", @@ -7542,20 +7551,20 @@ namespace ts.projectSystem { typings: "index.d.ts" }) }; - const indexOfPkgcurrentdirectory: FileOrFolder = { + const indexOfPkgcurrentdirectory: File = { path: `${currentDirectory}/node_modules/pkgcurrentdirectory/index.d.ts`, content: "export function foo() { }" }; const typingsCache = `/users/username/Library/Caches/typescript/2.7`; - const typingsCachePackageJson: FileOrFolder = { + const typingsCachePackageJson: File = { path: `${typingsCache}/package.json`, content: JSON.stringify({ devDependencies: { }, }) }; - const typingsCachePackageLockJson: FileOrFolder = { + const typingsCachePackageLockJson: File = { path: `${typingsCache}/package-lock.json`, content: JSON.stringify({ dependencies: { @@ -7598,37 +7607,37 @@ namespace ts.projectSystem { it("rename in common file renames all project", () => { const projects = "/users/username/projects"; const folderA = `${projects}/a`; - const aFile: FileOrFolder = { + const aFile: File = { path: `${folderA}/a.ts`, content: `import {C} from "./c/fc"; console.log(C)` }; - const aTsconfig: FileOrFolder = { + const aTsconfig: File = { path: `${folderA}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) }; - const aC: FileOrFolder = { + const aC: SymLink = { path: `${folderA}/c`, symLink: "../c" }; const aFc = `${folderA}/c/fc.ts`; const folderB = `${projects}/b`; - const bFile: FileOrFolder = { + const bFile: File = { path: `${folderB}/b.ts`, content: `import {C} from "./c/fc"; console.log(C)` }; - const bTsconfig: FileOrFolder = { + const bTsconfig: File = { path: `${folderB}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) }; - const bC: FileOrFolder = { + const bC: SymLink = { path: `${folderB}/c`, symLink: "../c" }; const bFc = `${folderB}/c/fc.ts`; const folderC = `${projects}/c`; - const cFile: FileOrFolder = { + const cFile: File = { path: `${folderC}/fc.ts`, content: `export const C = 8` }; @@ -7704,24 +7713,216 @@ namespace ts.projectSystem { } } }); + + describe("module resolution when symlinked folder contents change and resolve modules", () => { + const projectRootPath = "/users/username/projects/myproject"; + const packages = `${projectRootPath}/javascript/packages`; + const recognizersDateTime = `${packages}/recognizers-date-time`; + const recognizersText = `${packages}/recognizers-text`; + const recognizersTextDist = `${recognizersText}/dist`; + const moduleName = "@microsoft/recognizers-text"; + const moduleNameInFile = `"${moduleName}"`; + const recognizersDateTimeSrcFile: File = { + path: `${recognizersDateTime}/src/datetime/baseDate.ts`, + content: `import {C} from ${moduleNameInFile}; +new C();` + }; + const recognizerDateTimeTsconfigPath = `${recognizersDateTime}/tsconfig.json`; + const recognizerDateTimeTsconfigWithoutPathMapping: File = { + path: recognizerDateTimeTsconfigPath, + content: JSON.stringify({ + include: ["src"] + }) + }; + const recognizerDateTimeTsconfigWithPathMapping: File = { + path: recognizerDateTimeTsconfigPath, + content: JSON.stringify({ + compilerOptions: { + rootDir: "src", + baseUrl: "./", + paths: { + "@microsoft/*": ["../*"] + } + }, + include: ["src"] + }) + }; + const nodeModulesRecorgnizersText: SymLink = { + path: `${recognizersDateTime}/node_modules/@microsoft/recognizers-text`, + symLink: recognizersText + }; + const recognizerTextSrcFile: File = { + path: `${recognizersText}/src/recognizers-text.ts`, + content: `export class C { method () { return 10; } }` + }; + const recongnizerTextDistTypingFile: File = { + path: `${recognizersTextDist}/types/recognizers-text.d.ts`, + content: `export class C { method(): number; }` + }; + const recongnizerTextPackageJson: File = { + path: `${recognizersText}/package.json`, + content: JSON.stringify({ + typings: "dist/types/recognizers-text.d.ts" + }) + }; + const filesInProjectWithUnresolvedModule = [recognizerDateTimeTsconfigPath, libFile.path, recognizersDateTimeSrcFile.path]; + const filesInProjectWithResolvedModule = [...filesInProjectWithUnresolvedModule, recongnizerTextDistTypingFile.path]; + + function verifyErrors(session: TestSession, semanticErrors: protocol.Diagnostic[]) { + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [recognizersDateTimeSrcFile.path], + } + }); + + const host = session.host; + host.checkTimeoutQueueLengthAndRun(1); + + checkErrorMessage(session, "syntaxDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "semanticDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: semanticErrors }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "suggestionDiag", { + file: recognizersDateTimeSrcFile.path, + diagnostics: [], + }); + checkCompleteEvent(session, 2, expectedSequenceId); + } + + function createSingleWatchMap(paths: string[]) { + return arrayToMap(paths, p => p, () => 1); + } + + function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], directories: string[]) { + checkWatchedFilesDetailed(host, createSingleWatchMap(files.filter(f => f !== recognizersDateTimeSrcFile.path))); + checkWatchedDirectories(host, emptyArray, /*recursive*/ false); + checkWatchedDirectoriesDetailed(host, createSingleWatchMap(directories), /*recursive*/ true); + } + + function createSessionAndOpenFile(host: TestServerHost) { + const session = createSession(host, { canUseEvents: true }); + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, + arguments: { + file: recognizersDateTimeSrcFile.path, + projectRootPath + } + }); + return session; + } + + function verifyModuleResolution(withPathMapping: boolean) { + describe(withPathMapping ? "when tsconfig file contains path mapping" : "when tsconfig does not contain path mapping", () => { + const filesWithSources = [libFile, recognizersDateTimeSrcFile, withPathMapping ? recognizerDateTimeTsconfigWithPathMapping : recognizerDateTimeTsconfigWithoutPathMapping, recognizerTextSrcFile, recongnizerTextPackageJson]; + const filesWithNodeModulesSetup = [...filesWithSources, nodeModulesRecorgnizersText]; + const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile]; + + const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, withPathMapping ? packages : recognizersDateTime, ...getTypeRootsFromLocation(recognizersDateTime)]; + const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)]; + + function verifyProjectWithResolvedModule(session: TestSession) { + const projectService = session.getProjectService(); + const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath); + checkProjectActualFiles(project, filesInProjectWithResolvedModule); + verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule); + verifyErrors(session, []); + } + + function verifyProjectWithUnresolvedModule(session: TestSession) { + const projectService = session.getProjectService(); + const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath); + checkProjectActualFiles(project, filesInProjectWithUnresolvedModule); + verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule); + const startOffset = recognizersDateTimeSrcFile.content.indexOf('"') + 1; + verifyErrors(session, [ + createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + moduleNameInFile.length }, Diagnostics.Cannot_find_module_0, [moduleName]) + ]); + } + + it("when project compiles from sources", () => { + const host = createServerHost(filesWithSources); + const session = createSessionAndOpenFile(host); + verifyProjectWithUnresolvedModule(session); + + host.reloadFS(filesAfterCompilation); + host.runQueuedTimeoutCallbacks(); + + verifyProjectWithResolvedModule(session); + }); + + it("when project has node_modules setup but doesnt have modules in typings folder and then recompiles", () => { + const host = createServerHost(filesWithNodeModulesSetup); + const session = createSessionAndOpenFile(host); + verifyProjectWithUnresolvedModule(session); + + host.reloadFS(filesAfterCompilation); + host.runQueuedTimeoutCallbacks(); + + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } + }); + + it("when project recompiles after deleting generated folders", () => { + const host = createServerHost(filesAfterCompilation); + const session = createSessionAndOpenFile(host); + + verifyProjectWithResolvedModule(session); + + host.removeFolder(recognizersTextDist, /*recursive*/ true); + host.runQueuedTimeoutCallbacks(); + + verifyProjectWithUnresolvedModule(session); + + host.ensureFileOrFolder(recongnizerTextDistTypingFile); + host.runQueuedTimeoutCallbacks(); + + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } + }); + }); + } + + verifyModuleResolution(/*withPathMapping*/ false); + verifyModuleResolution(/*withPathMapping*/ true); + }); }); describe("tsserverProjectSystem forceConsistentCasingInFileNames", () => { it("works when extends is specified with a case insensitive file system", () => { const rootPath = "/Users/username/dev/project"; - const file1: FileOrFolder = { + const file1: File = { path: `${rootPath}/index.ts`, content: 'import {x} from "file2";', }; - const file2: FileOrFolder = { + const file2: File = { path: `${rootPath}/file2.js`, content: "", }; - const file2Dts: FileOrFolder = { + const file2Dts: File = { path: `${rootPath}/types/file2/index.d.ts`, content: "export declare const x: string;", }; - const tsconfigAll: FileOrFolder = { + const tsconfigAll: File = { path: `${rootPath}/tsconfig.all.json`, content: JSON.stringify({ compilerOptions: { @@ -7732,7 +7933,7 @@ namespace ts.projectSystem { }, }), }; - const tsconfig: FileOrFolder = { + const tsconfig: File = { path: `${rootPath}/tsconfig.json`, content: JSON.stringify({ extends: "./tsconfig.all.json" }), }; @@ -7752,17 +7953,17 @@ namespace ts.projectSystem { describe("tsserverProjectSystem module resolution caching", () => { const projectLocation = "/user/username/projects/myproject"; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectLocation}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { traceResolution: true } }) }; function getModules(module1Path: string, module2Path: string) { - const module1: FileOrFolder = { + const module1: File = { path: module1Path, content: `export function module1() {}` }; - const module2: FileOrFolder = { + const module2: File = { path: module2Path, content: `export function module2() {}` }; @@ -7774,7 +7975,7 @@ namespace ts.projectSystem { resolutionTrace.length = 0; } - function getExpectedFileDoesNotExistResolutionTrace(host: TestServerHost, expectedTrace: string[], foundModule: boolean, module: FileOrFolder, directory: string, file: string, ignoreIfParentMissing?: boolean) { + function getExpectedFileDoesNotExistResolutionTrace(host: TestServerHost, expectedTrace: string[], foundModule: boolean, module: File, directory: string, file: string, ignoreIfParentMissing?: boolean) { if (!foundModule) { const path = combinePaths(directory, file); if (!ignoreIfParentMissing || host.directoryExists(getDirectoryPath(path))) { @@ -7789,7 +7990,7 @@ namespace ts.projectSystem { return foundModule; } - function getExpectedMissedLocationResolutionTrace(host: TestServerHost, expectedTrace: string[], dirPath: string, module: FileOrFolder, moduleName: string, useNodeModules: boolean, cacheLocation?: string) { + function getExpectedMissedLocationResolutionTrace(host: TestServerHost, expectedTrace: string[], dirPath: string, module: File, moduleName: string, useNodeModules: boolean, cacheLocation?: string) { let foundModule = false; forEachAncestorDirectory(dirPath, dirPath => { if (dirPath === cacheLocation) { @@ -7813,14 +8014,14 @@ namespace ts.projectSystem { }); } - function getExpectedResolutionTraceHeader(expectedTrace: string[], file: FileOrFolder, moduleName: string) { + function getExpectedResolutionTraceHeader(expectedTrace: string[], file: File, moduleName: string) { expectedTrace.push( `======== Resolving module '${moduleName}' from '${file.path}'. ========`, `Module resolution kind is not specified, using 'NodeJs'.` ); } - function getExpectedResolutionTraceFooter(expectedTrace: string[], module: FileOrFolder, moduleName: string, addRealPathTrace: boolean, ignoreModuleFileFound?: boolean) { + function getExpectedResolutionTraceFooter(expectedTrace: string[], module: File, moduleName: string, addRealPathTrace: boolean, ignoreModuleFileFound?: boolean) { if (!ignoreModuleFileFound) { expectedTrace.push(`File '${module.path}' exist - use it as a name resolution result.`); } @@ -7830,7 +8031,7 @@ namespace ts.projectSystem { expectedTrace.push(`======== Module name '${moduleName}' was successfully resolved to '${module.path}'. ========`); } - function getExpectedRelativeModuleResolutionTrace(host: TestServerHost, file: FileOrFolder, module: FileOrFolder, moduleName: string, expectedTrace: string[] = []) { + function getExpectedRelativeModuleResolutionTrace(host: TestServerHost, file: File, module: File, moduleName: string, expectedTrace: string[] = []) { getExpectedResolutionTraceHeader(expectedTrace, file, moduleName); expectedTrace.push(`Loading module as file / folder, candidate module location '${removeFileExtension(module.path)}', target file type 'TypeScript'.`); getExpectedMissedLocationResolutionTrace(host, expectedTrace, getDirectoryPath(normalizePath(combinePaths(getDirectoryPath(file.path), moduleName))), module, moduleName.substring(moduleName.lastIndexOf("/") + 1), /*useNodeModules*/ false); @@ -7838,7 +8039,7 @@ namespace ts.projectSystem { return expectedTrace; } - function getExpectedNonRelativeModuleResolutionTrace(host: TestServerHost, file: FileOrFolder, module: FileOrFolder, moduleName: string, expectedTrace: string[] = []) { + function getExpectedNonRelativeModuleResolutionTrace(host: TestServerHost, file: File, module: File, moduleName: string, expectedTrace: string[] = []) { getExpectedResolutionTraceHeader(expectedTrace, file, moduleName); expectedTrace.push(`Loading module '${moduleName}' from 'node_modules' folder, target file type 'TypeScript'.`); getExpectedMissedLocationResolutionTrace(host, expectedTrace, getDirectoryPath(file.path), module, moduleName, /*useNodeModules*/ true); @@ -7846,7 +8047,7 @@ namespace ts.projectSystem { return expectedTrace; } - function getExpectedNonRelativeModuleResolutionFromCacheTrace(host: TestServerHost, file: FileOrFolder, module: FileOrFolder, moduleName: string, cacheLocation: string, expectedTrace: string[] = []) { + function getExpectedNonRelativeModuleResolutionFromCacheTrace(host: TestServerHost, file: File, module: File, moduleName: string, cacheLocation: string, expectedTrace: string[] = []) { getExpectedResolutionTraceHeader(expectedTrace, file, moduleName); expectedTrace.push(`Loading module '${moduleName}' from 'node_modules' folder, target file type 'TypeScript'.`); getExpectedMissedLocationResolutionTrace(host, expectedTrace, getDirectoryPath(file.path), module, moduleName, /*useNodeModules*/ true, cacheLocation); @@ -7855,11 +8056,11 @@ namespace ts.projectSystem { return expectedTrace; } - function getExpectedReusingResolutionFromOldProgram(file: FileOrFolder, moduleName: string) { + function getExpectedReusingResolutionFromOldProgram(file: File, moduleName: string) { return `Reusing resolution of module '${moduleName}' to file '${file.path}' from old program.`; } - function verifyWatchesWithConfigFile(host: TestServerHost, files: FileOrFolder[], openFile: FileOrFolder) { + function verifyWatchesWithConfigFile(host: TestServerHost, files: File[], openFile: File) { checkWatchedFiles(host, mapDefined(files, f => f === openFile ? undefined : f.path)); checkWatchedDirectories(host, [], /*recursive*/ false); const configDirectory = getDirectoryPath(configFile.path); @@ -7868,11 +8069,11 @@ namespace ts.projectSystem { describe("from files in same folder", () => { function getFiles(fileContent: string) { - const file1: FileOrFolder = { + const file1: File = { path: `${projectLocation}/src/file1.ts`, content: fileContent }; - const file2: FileOrFolder = { + const file2: File = { path: `${projectLocation}/src/file2.ts`, content: fileContent }; @@ -7936,19 +8137,19 @@ namespace ts.projectSystem { describe("from files in different folders", () => { function getFiles(fileContent1: string, fileContent2 = fileContent1, fileContent3 = fileContent1, fileContent4 = fileContent1) { - const file1: FileOrFolder = { + const file1: File = { path: `${projectLocation}/product/src/file1.ts`, content: fileContent1 }; - const file2: FileOrFolder = { + const file2: File = { path: `${projectLocation}/product/src/feature/file2.ts`, content: fileContent2 }; - const file3: FileOrFolder = { + const file3: File = { path: `${projectLocation}/product/test/src/file3.ts`, content: fileContent3 }; - const file4: FileOrFolder = { + const file4: File = { path: `${projectLocation}/product/test/file4.ts`, content: fileContent4 }; @@ -8103,15 +8304,15 @@ namespace ts.projectSystem { function verifyCompletionListWithNewFileInSubFolder(tscWatchDirectory: TestFSWithWatch.Tsc_WatchDirectory) { const projectFolder = "/a/username/project"; const projectSrcFolder = `${projectFolder}/src`; - const configFile: FileOrFolder = { + const configFile: File = { path: `${projectFolder}/tsconfig.json`, content: "{}" }; - const index: FileOrFolder = { + const index: File = { path: `${projectSrcFolder}/index.ts`, content: `import {} from "./"` }; - const file1: FileOrFolder = { + const file1: File = { path: `${projectSrcFolder}/file1.ts`, content: "" }; @@ -8145,7 +8346,7 @@ namespace ts.projectSystem { verifyProjectAndCompletions(); // Add file2 - const file2: FileOrFolder = { + const file2: File = { path: `${projectSrcFolder}/file2.ts`, content: "" }; diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index a8c7d4895d1..2f83fe90c8a 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -30,7 +30,7 @@ namespace ts.projectSystem { } } - function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void { + function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[] | string, typingFiles: File[], cb: TI.RequestCompletedAction): void { self.addPostExecAction(installedTypings, success => { for (const file of typingFiles) { host.ensureFileOrFolder(file); @@ -417,7 +417,7 @@ namespace ts.projectSystem { } installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings: string[] = []; - const typingFiles: FileOrFolder[] = []; + const typingFiles: File[] = []; executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -460,7 +460,7 @@ namespace ts.projectSystem { } installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings: string[] = []; - const typingFiles: FileOrFolder[] = []; + const typingFiles: File[] = []; executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -697,7 +697,7 @@ namespace ts.projectSystem { super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") }); } installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { - let typingFiles: (FileOrFolder & { typings: string })[] = []; + let typingFiles: (File & { typings: string })[] = []; if (args.indexOf(typingsName("commander")) >= 0) { typingFiles = [commander, jquery, lodash, cordova]; } @@ -1193,7 +1193,7 @@ namespace ts.projectSystem { } installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) { const installedTypings: string[] = []; - const typingFiles: FileOrFolder[] = []; + const typingFiles: File[] = []; executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -1656,12 +1656,12 @@ namespace ts.projectSystem { return foooResolution; } - function verifyUnresolvedImportResolutions(appContents: string, typingNames: string[], typingFiles: FileOrFolder[]) { - const app: FileOrFolder = { + function verifyUnresolvedImportResolutions(appContents: string, typingNames: string[], typingFiles: File[]) { + const app: File = { path: appPath, content: `${appContents}import * as x from "fooo";` }; - const fooo: FileOrFolder = { + const fooo: File = { path: foooPath, content: `export var x: string;` }; @@ -1697,15 +1697,15 @@ namespace ts.projectSystem { }); it("correctly invalidate the resolutions with typing names that are trimmed", () => { - const fooAA: FileOrFolder = { + const fooAA: File = { path: `${globalTypingsCacheLocation}/node_modules/foo/a/a.d.ts`, content: "export function a (): void;" }; - const fooAB: FileOrFolder = { + const fooAB: File = { path: `${globalTypingsCacheLocation}/node_modules/foo/a/b.d.ts`, content: "export function b (): void;" }; - const fooAC: FileOrFolder = { + const fooAC: File = { path: `${globalTypingsCacheLocation}/node_modules/foo/a/c.d.ts`, content: "export function c (): void;" }; diff --git a/src/harness/utils.ts b/src/harness/utils.ts index 03e9e56169b..83e51a065a0 100644 --- a/src/harness/utils.ts +++ b/src/harness/utils.ts @@ -2,31 +2,6 @@ * Common utilities */ namespace utils { - const leadingCommentRegExp = /^(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+/; - const trailingCommentRegExp = /(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+$/; - const leadingAndTrailingCommentRegExp = /^(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+|(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+$/g; - const allCommentRegExp = /(['"])(?:(?!\1).|\\[^])*\1|(\/\*[^]*?\*\/|\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)/g; - - export const enum CommentRemoval { - leading, - trailing, - leadingAndTrailing, - all - } - - export function removeComments(text: string, removal: CommentRemoval) { - switch (removal) { - case CommentRemoval.leading: - return text.replace(leadingCommentRegExp, ""); - case CommentRemoval.trailing: - return text.replace(trailingCommentRegExp, ""); - case CommentRemoval.leadingAndTrailing: - return text.replace(leadingAndTrailingCommentRegExp, ""); - case CommentRemoval.all: - return text.replace(allCommentRegExp, (match, quote) => quote ? match : ""); - } - } - const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g; export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean) { return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined; diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index 698c8abd477..4972dd08925 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -332,35 +332,20 @@ namespace vfs { } } - private _depth: string[] = []; - /** * Make a directory and all of its parent paths (if they don't exist). */ public mkdirpSync(path: string) { - try { - this._depth.push(path); - path = this._resolve(path); - this.mkdirSync(path); - } - catch (e) { - if (e.code === "ENOENT") { - if (this._depth.length > 10) { - console.log(`path: ${path}`); - console.log(`dirname: ${vpath.dirname(path)}`); - console.log(this._depth); - throw e; - } - this.mkdirpSync(vpath.dirname(path)); - this.mkdirSync(path); + path = this._resolve(path); + const result = this._walk(path, /*noFollow*/ true, (error, result) => { + if (error.code === "ENOENT") { + this._mkdir(result); + return "retry"; } - else if (e.code !== "EEXIST") { - throw e; - } - } - finally { - this._depth.pop(); - } + return "throw"; + }); + + if (!result.node) this._mkdir(result); } /** @@ -464,9 +449,11 @@ namespace vfs { public mkdirSync(path: string) { if (this.isReadonly) throw createIOError("EROFS"); - const { parent, links, node: existingNode, basename } = this._walk(this._resolve(path), /*noFollow*/ true); - if (existingNode) throw createIOError("EEXIST"); + this._mkdir(this._walk(this._resolve(path), /*noFollow*/ true)); + } + private _mkdir({ parent, links, node: existingNode, basename }: WalkResult) { + if (existingNode) throw createIOError("EEXIST"); const time = this.time(); const node = this._mknod(parent ? parent.dev : ++devCount, S_IFDIR, /*mode*/ 0o777, time); this._addLink(parent, links, basename, node, time); @@ -810,17 +797,18 @@ namespace vfs { * @param path The path to follow. * @param noFollow A value indicating whether to *not* dereference a symbolic link at the * end of a path. - * @param allowPartial A value indicating whether to return a partial result if the node - * at the end of the path cannot be found. * * @link http://man7.org/linux/man-pages/man7/path_resolution.7.html */ - private _walk(path: string, noFollow?: boolean): WalkResult { + private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "retry" | "throw"): WalkResult; + private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "stop" | "retry" | "throw"): WalkResult | undefined; + private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "stop" | "retry" | "throw"): WalkResult | undefined { let links = this._getRootLinks(); let parent: DirectoryInode | undefined; let components = vpath.parse(path); let step = 0; let depth = 0; + let retry = false; while (true) { if (depth >= 40) throw createIOError("ELOOP"); const lastStep = step === components.length - 1; @@ -830,7 +818,8 @@ namespace vfs { return { realpath: vpath.format(components), basename, parent, links, node }; } if (node === undefined) { - throw createIOError("ENOENT"); + if (trapError(createIOError("ENOENT"), node)) continue; + return undefined; } if (isSymlink(node)) { const dirname = vpath.format(components.slice(0, step)); @@ -840,15 +829,30 @@ namespace vfs { components = vpath.parse(symlink).concat(components.slice(step + 1)); step = 0; depth++; + retry = false; continue; } if (isDirectory(node)) { links = this._getLinks(node); parent = node; step++; + retry = false; continue; } - throw createIOError("ENOTDIR"); + if (trapError(createIOError("ENOTDIR"), node)) continue; + return undefined; + } + + function trapError(error: NodeJS.ErrnoException, node?: Inode) { + const realpath = vpath.format(components.slice(0, step + 1)); + const basename = components[step]; + const result = !retry && onError ? onError(error, { realpath, basename, parent, links, node }) : "throw"; + if (result === "stop") return false; + if (result === "retry") { + retry = true; + return true; + } + throw error; } } @@ -1118,6 +1122,7 @@ namespace vfs { export function createIOError(code: keyof typeof IOErrorMessages) { const err: NodeJS.ErrnoException = new Error(`${code}: ${IOErrorMessages[code]}`); err.code = code; + if (Error.captureStackTrace) Error.captureStackTrace(err, createIOError); return err; } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 323a51c25ae..ef3d4955e42 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -1,7 +1,7 @@ /// namespace ts.TestFSWithWatch { - export const libFile: FileOrFolder = { + export const libFile: File = { path: "/a/lib/lib.d.ts", content: `/// interface Boolean {} @@ -39,7 +39,7 @@ interface Array {}` environmentVariables?: Map; } - export function createWatchedSystem(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { + export function createWatchedSystem(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { if (!params) { params = {}; } @@ -54,7 +54,7 @@ interface Array {}` return host; } - export function createServerHost(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { + export function createServerHost(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { if (!params) { params = {}; } @@ -69,42 +69,61 @@ interface Array {}` return host; } - export interface FileOrFolder { + export interface File { path: string; - content?: string; + content: string; fileSize?: number; - symLink?: string; } - interface FSEntry { + export interface Folder { + path: string; + } + + export interface SymLink { + path: string; + symLink: string; + } + + export type FileOrFolderOrSymLink = File | Folder | SymLink; + function isFile(fileOrFolderOrSymLink: FileOrFolderOrSymLink): fileOrFolderOrSymLink is File { + return isString((fileOrFolderOrSymLink).content); + } + + function isSymLink(fileOrFolderOrSymLink: FileOrFolderOrSymLink): fileOrFolderOrSymLink is SymLink { + return isString((fileOrFolderOrSymLink).symLink); + } + + interface FSEntryBase { path: Path; fullPath: string; modifiedTime: Date; } - interface File extends FSEntry { + interface FsFile extends FSEntryBase { content: string; fileSize?: number; } - interface Folder extends FSEntry { + interface FsFolder extends FSEntryBase { entries: SortedArray; } - interface SymLink extends FSEntry { + interface FsSymLink extends FSEntryBase { symLink: string; } - function isFolder(s: FSEntry): s is Folder { - return s && isArray((s).entries); + type FSEntry = FsFile | FsFolder | FsSymLink; + + function isFsFolder(s: FSEntry): s is FsFolder { + return s && isArray((s).entries); } - function isFile(s: FSEntry): s is File { - return s && isString((s).content); + function isFsFile(s: FSEntry): s is FsFile { + return s && isString((s).content); } - function isSymLink(s: FSEntry): s is SymLink { - return s && isString((s).symLink); + function isFsSymLink(s: FSEntry): s is FsSymLink { + return s && isString((s).symLink); } function invokeWatcherCallbacks(callbacks: T[], invokeCallback: (cb: T) => void): void { @@ -306,12 +325,12 @@ interface Array {}` private readonly dynamicPriorityWatchFile: HostWatchFile; private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined; - constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderList: ReadonlyArray, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map) { + constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderorSymLinkList: ReadonlyArray, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map) { this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); this.executingFilePath = this.getHostSpecificPath(executingFilePath); this.currentDirectory = this.getHostSpecificPath(currentDirectory); - this.reloadFS(fileOrFolderList); + this.reloadFS(fileOrFolderorSymLinkList); this.dynamicPriorityWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHFILE") === "DynamicPriorityPolling" ? createDynamicPriorityPollingWatchFile(this) : undefined; @@ -373,12 +392,12 @@ interface Array {}` return new Date(this.time); } - reloadFS(fileOrFolderList: ReadonlyArray, options?: Partial) { + reloadFS(fileOrFolderOrSymLinkList: ReadonlyArray, options?: Partial) { const mapNewLeaves = createMap(); const isNewFs = this.fs.size === 0; - fileOrFolderList = fileOrFolderList.concat(this.withSafeList ? safeList : []); - const filesOrFoldersToLoad: ReadonlyArray = !this.useWindowsStylePath ? fileOrFolderList : - fileOrFolderList.map(f => { + fileOrFolderOrSymLinkList = fileOrFolderOrSymLinkList.concat(this.withSafeList ? safeList : []); + const filesOrFoldersToLoad: ReadonlyArray = !this.useWindowsStylePath ? fileOrFolderOrSymLinkList : + fileOrFolderOrSymLinkList.map(f => { const result = clone(f); result.path = this.getHostSpecificPath(f.path); return result; @@ -389,8 +408,8 @@ interface Array {}` // If its a change const currentEntry = this.fs.get(path); if (currentEntry) { - if (isFile(currentEntry)) { - if (isString(fileOrDirectory.content)) { + if (isFsFile(currentEntry)) { + if (isFile(fileOrDirectory)) { // Update file if (currentEntry.content !== fileOrDirectory.content) { this.modifyFile(fileOrDirectory.path, fileOrDirectory.content, options); @@ -400,12 +419,12 @@ interface Array {}` // TODO: Changing from file => folder/Symlink } } - else if (isSymLink(currentEntry)) { + else if (isFsSymLink(currentEntry)) { // TODO: update symlinks } else { // Folder - if (isString(fileOrDirectory.content)) { + if (isFile(fileOrDirectory)) { // TODO: Changing from folder => file } else { @@ -424,7 +443,7 @@ interface Array {}` // If this entry is not from the new file or folder if (!mapNewLeaves.get(path)) { // Leaf entries that arent in new list => remove these - if (isFile(fileOrDirectory) || isSymLink(fileOrDirectory) || isFolder(fileOrDirectory) && fileOrDirectory.entries.length === 0) { + if (isFsFile(fileOrDirectory) || isFsSymLink(fileOrDirectory) || isFsFolder(fileOrDirectory) && fileOrDirectory.entries.length === 0) { this.removeFileOrFolder(fileOrDirectory, folder => !mapNewLeaves.get(folder.path)); } } @@ -435,7 +454,7 @@ interface Array {}` modifyFile(filePath: string, content: string, options?: Partial) { const path = this.toFullPath(filePath); const currentEntry = this.fs.get(path); - if (!currentEntry || !isFile(currentEntry)) { + if (!currentEntry || !isFsFile(currentEntry)) { throw new Error(`file not present: ${filePath}`); } @@ -459,7 +478,7 @@ interface Array {}` renameFolder(folderName: string, newFolderName: string) { const fullPath = getNormalizedAbsolutePath(folderName, this.currentDirectory); const path = this.toPath(fullPath); - const folder = this.fs.get(path) as Folder; + const folder = this.fs.get(path) as FsFolder; Debug.assert(!!folder); // Only remove the folder @@ -467,19 +486,19 @@ interface Array {}` // Add updated folder with new folder name const newFullPath = getNormalizedAbsolutePath(newFolderName, this.currentDirectory); - const newFolder = this.toFolder(newFullPath); + const newFolder = this.toFsFolder(newFullPath); const newPath = newFolder.path; const basePath = getDirectoryPath(path); Debug.assert(basePath !== path); Debug.assert(basePath === getDirectoryPath(newPath)); - const baseFolder = this.fs.get(basePath) as Folder; + const baseFolder = this.fs.get(basePath) as FsFolder; this.addFileOrFolderInFolder(baseFolder, newFolder); // Invoke watches for files in the folder as deleted (from old path) this.renameFolderEntries(folder, newFolder); } - private renameFolderEntries(oldFolder: Folder, newFolder: Folder) { + private renameFolderEntries(oldFolder: FsFolder, newFolder: FsFolder) { for (const entry of oldFolder.entries) { this.fs.delete(entry.path); this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Deleted); @@ -491,38 +510,38 @@ interface Array {}` } this.fs.set(entry.path, entry); this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Created); - if (isFolder(entry)) { + if (isFsFolder(entry)) { this.renameFolderEntries(entry, entry); } } } - ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) { - if (isString(fileOrDirectory.content)) { - const file = this.toFile(fileOrDirectory); + ensureFileOrFolder(fileOrDirectoryOrSymLink: FileOrFolderOrSymLink, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) { + if (isFile(fileOrDirectoryOrSymLink)) { + const file = this.toFsFile(fileOrDirectoryOrSymLink); // file may already exist when updating existing type declaration file if (!this.fs.get(file.path)) { const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath)); this.addFileOrFolderInFolder(baseFolder, file, ignoreWatchInvokedWithTriggerAsFileCreate); } } - else if (isString(fileOrDirectory.symLink)) { - const symLink = this.toSymLink(fileOrDirectory); + else if (isSymLink(fileOrDirectoryOrSymLink)) { + const symLink = this.toFsSymLink(fileOrDirectoryOrSymLink); Debug.assert(!this.fs.get(symLink.path)); const baseFolder = this.ensureFolder(getDirectoryPath(symLink.fullPath)); this.addFileOrFolderInFolder(baseFolder, symLink, ignoreWatchInvokedWithTriggerAsFileCreate); } else { - const fullPath = getNormalizedAbsolutePath(fileOrDirectory.path, this.currentDirectory); + const fullPath = getNormalizedAbsolutePath(fileOrDirectoryOrSymLink.path, this.currentDirectory); this.ensureFolder(fullPath); } } - private ensureFolder(fullPath: string): Folder { + private ensureFolder(fullPath: string): FsFolder { const path = this.toPath(fullPath); - let folder = this.fs.get(path) as Folder; + let folder = this.fs.get(path) as FsFolder; if (!folder) { - folder = this.toFolder(fullPath); + folder = this.toFsFolder(fullPath); const baseFullPath = getDirectoryPath(fullPath); if (fullPath !== baseFullPath) { // Add folder in the base folder @@ -535,11 +554,11 @@ interface Array {}` this.fs.set(path, folder); } } - Debug.assert(isFolder(folder)); + Debug.assert(isFsFolder(folder)); return folder; } - private addFileOrFolderInFolder(folder: Folder, fileOrDirectory: File | Folder | SymLink, ignoreWatch?: boolean) { + private addFileOrFolderInFolder(folder: FsFolder, fileOrDirectory: FsFile | FsFolder | FsSymLink, ignoreWatch?: boolean) { insertSorted(folder.entries, fileOrDirectory, (a, b) => compareStringsCaseSensitive(getBaseFileName(a.path), getBaseFileName(b.path))); folder.modifiedTime = this.now(); this.fs.set(fileOrDirectory.path, fileOrDirectory); @@ -551,9 +570,9 @@ interface Array {}` this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath); } - private removeFileOrFolder(fileOrDirectory: File | Folder | SymLink, isRemovableLeafFolder: (folder: Folder) => boolean, isRenaming?: boolean) { + private removeFileOrFolder(fileOrDirectory: FsFile | FsFolder | FsSymLink, isRemovableLeafFolder: (folder: FsFolder) => boolean, isRenaming?: boolean) { const basePath = getDirectoryPath(fileOrDirectory.path); - const baseFolder = this.fs.get(basePath) as Folder; + const baseFolder = this.fs.get(basePath) as FsFolder; if (basePath !== fileOrDirectory.path) { Debug.assert(!!baseFolder); baseFolder.modifiedTime = this.now(); @@ -562,7 +581,7 @@ interface Array {}` this.fs.delete(fileOrDirectory.path); this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted); - if (isFolder(fileOrDirectory)) { + if (isFsFolder(fileOrDirectory)) { Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming); const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath); // Invoke directory and recursive directory watcher for the folder @@ -582,6 +601,24 @@ interface Array {}` } } + removeFolder(folderPath: string, recursive?: boolean) { + const path = this.toFullPath(folderPath); + const currentEntry = this.fs.get(path) as FsFolder; + Debug.assert(isFsFolder(currentEntry)); + if (recursive && currentEntry.entries.length) { + const subEntries = currentEntry.entries.slice(); + subEntries.forEach(fsEntry => { + if (isFsFolder(fsEntry)) { + this.removeFolder(fsEntry.fullPath, recursive); + } + else { + this.removeFileOrFolder(fsEntry, returnFalse); + } + }); + } + this.removeFileOrFolder(currentEntry, returnFalse); + } + // For overriding the methods invokeWatchedDirectoriesCallback(folderFullPath: string, relativePath: string) { invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath)); @@ -626,7 +663,7 @@ interface Array {}` } } - private toFsEntry(path: string): FSEntry { + private toFsEntry(path: string): FSEntryBase { const fullPath = getNormalizedAbsolutePath(path, this.currentDirectory); return { path: this.toPath(fullPath), @@ -635,23 +672,23 @@ interface Array {}` }; } - private toFile(fileOrDirectory: FileOrFolder): File { - const file = this.toFsEntry(fileOrDirectory.path) as File; - file.content = fileOrDirectory.content; - file.fileSize = fileOrDirectory.fileSize; - return file; + private toFsFile(file: File): FsFile { + const fsFile = this.toFsEntry(file.path) as FsFile; + fsFile.content = file.content; + fsFile.fileSize = file.fileSize; + return fsFile; } - private toSymLink(fileOrDirectory: FileOrFolder): SymLink { - const symLink = this.toFsEntry(fileOrDirectory.path) as SymLink; - symLink.symLink = getNormalizedAbsolutePath(fileOrDirectory.symLink, getDirectoryPath(symLink.fullPath)); - return symLink; + private toFsSymLink(symLink: SymLink): FsSymLink { + const fsSymLink = this.toFsEntry(symLink.path) as FsSymLink; + fsSymLink.symLink = getNormalizedAbsolutePath(symLink.symLink, getDirectoryPath(fsSymLink.fullPath)); + return fsSymLink; } - private toFolder(path: string): Folder { - const folder = this.toFsEntry(path) as Folder; - folder.entries = [] as SortedArray; - return folder; + private toFsFolder(path: string): FsFolder { + const fsFolder = this.toFsEntry(path) as FsFolder; + fsFolder.entries = [] as SortedArray; + return fsFolder; } private getRealFsEntry(isFsEntry: (fsEntry: FSEntry) => fsEntry is T, path: Path, fsEntry = this.fs.get(path)): T | undefined { @@ -659,7 +696,7 @@ interface Array {}` return fsEntry; } - if (isSymLink(fsEntry)) { + if (isFsSymLink(fsEntry)) { return this.getRealFsEntry(isFsEntry, this.toPath(fsEntry.symLink)); } @@ -676,20 +713,20 @@ interface Array {}` return undefined; } - private isFile(fsEntry: FSEntry) { + private isFsFile(fsEntry: FSEntry) { return !!this.getRealFile(fsEntry.path, fsEntry); } - private getRealFile(path: Path, fsEntry?: FSEntry): File | undefined { - return this.getRealFsEntry(isFile, path, fsEntry); + private getRealFile(path: Path, fsEntry?: FSEntry): FsFile | undefined { + return this.getRealFsEntry(isFsFile, path, fsEntry); } - private isFolder(fsEntry: FSEntry) { + private isFsFolder(fsEntry: FSEntry) { return !!this.getRealFolder(fsEntry.path, fsEntry); } - private getRealFolder(path: Path, fsEntry = this.fs.get(path)): Folder | undefined { - return this.getRealFsEntry(isFolder, path, fsEntry); + private getRealFolder(path: Path, fsEntry = this.fs.get(path)): FsFolder | undefined { + return this.getRealFsEntry(isFsFolder, path, fsEntry); } fileExists(s: string) { @@ -711,7 +748,7 @@ interface Array {}` getFileSize(s: string) { const path = this.toFullPath(s); const entry = this.fs.get(path); - if (isFile(entry)) { + if (isFsFile(entry)) { return entry.fileSize ? entry.fileSize : entry.content.length; } return undefined; @@ -726,7 +763,7 @@ interface Array {}` const path = this.toFullPath(s); const folder = this.getRealFolder(path); if (folder) { - return mapDefined(folder.entries, entry => this.isFolder(entry) ? getBaseFileName(entry.fullPath) : undefined); + return mapDefined(folder.entries, entry => this.isFsFolder(entry) ? getBaseFileName(entry.fullPath) : undefined); } Debug.fail(folder ? "getDirectories called on file" : "getDirectories called on missing folder"); return []; @@ -739,10 +776,10 @@ interface Array {}` const folder = this.getRealFolder(this.toPath(dir)); if (folder) { folder.entries.forEach((entry) => { - if (this.isFolder(entry)) { + if (this.isFsFolder(entry)) { directories.push(getBaseFileName(entry.fullPath)); } - else if (this.isFile(entry)) { + else if (this.isFsFile(entry)) { files.push(getBaseFileName(entry.fullPath)); } else { @@ -840,24 +877,24 @@ interface Array {}` } createDirectory(directoryName: string): void { - const folder = this.toFolder(directoryName); + const folder = this.toFsFolder(directoryName); // base folder has to be present const base = getDirectoryPath(folder.path); - const baseFolder = this.fs.get(base) as Folder; - Debug.assert(isFolder(baseFolder)); + const baseFolder = this.fs.get(base) as FsFolder; + Debug.assert(isFsFolder(baseFolder)); Debug.assert(!this.fs.get(folder.path)); this.addFileOrFolderInFolder(baseFolder, folder); } writeFile(path: string, content: string): void { - const file = this.toFile({ path, content }); + const file = this.toFsFile({ path, content }); // base folder has to be present const base = getDirectoryPath(file.path); - const folder = this.fs.get(base) as Folder; - Debug.assert(isFolder(folder)); + const folder = this.fs.get(base) as FsFolder; + Debug.assert(isFsFolder(folder)); this.addFileOrFolderInFolder(folder, file); } @@ -885,7 +922,7 @@ interface Array {}` const dirFullPath = this.realpath(getDirectoryPath(fullPath)); const realFullPath = combinePaths(dirFullPath, getBaseFileName(fullPath)); const fsEntry = this.fs.get(this.toPath(realFullPath)); - if (isSymLink(fsEntry)) { + if (isFsSymLink(fsEntry)) { return this.realpath(fsEntry.symLink); } diff --git a/src/harness/vpath.ts b/src/harness/vpath.ts index 9b42ba2a28d..31430b50e2b 100644 --- a/src/harness/vpath.ts +++ b/src/harness/vpath.ts @@ -124,4 +124,8 @@ namespace vpath { return isDeclaration(path) && basename(path).startsWith("lib."); } + + export function isTsConfigFile(path: string): boolean { + return path.indexOf("tsconfig") !== -1 && path.indexOf("json") !== -1; + } } \ No newline at end of file diff --git a/src/lib/scripthost.d.ts b/src/lib/scripthost.d.ts index 1fbd185949f..c3ac4a7e656 100644 --- a/src/lib/scripthost.d.ts +++ b/src/lib/scripthost.d.ts @@ -201,6 +201,11 @@ declare var WScript: { Sleep(intTime: number): void; }; +/** + * WSH is an alias for WScript under Windows Script Host + */ +declare var WSH: typeof WScript; + /** * Represents an Automation SAFEARRAY */ diff --git a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1cfc21834fe..0364ba3402a 100644 --- a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2520,6 +2520,15 @@ + + + + + + + + + @@ -2547,6 +2556,15 @@ + + + + + + + + + @@ -7428,6 +7446,15 @@ + + + + + + + + + @@ -9366,6 +9393,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index c57179896e8..0772c6551d1 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2523,6 +2523,9 @@ + + + @@ -2556,6 +2559,9 @@ + + + @@ -4296,6 +4302,12 @@ + + + + + + @@ -5592,6 +5604,12 @@ + + + + + + @@ -7443,6 +7461,9 @@ + + + @@ -9387,6 +9408,9 @@ + + + diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index 2690ba4fe89..1a0f4433a62 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2529,6 +2529,15 @@ + + + + + + + + + @@ -2556,6 +2565,15 @@ + + + + + + + + + @@ -4293,6 +4311,12 @@ + + + + + + @@ -5589,6 +5613,12 @@ + + + + + + @@ -7437,6 +7467,15 @@ + + + + + + + + + @@ -9375,6 +9414,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1675ab764eb..9d3620e29e4 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2520,6 +2520,9 @@ + + + @@ -2553,6 +2556,9 @@ + + + @@ -4293,6 +4299,12 @@ + + + + + + @@ -5589,6 +5601,12 @@ + + + + + + @@ -7437,6 +7455,9 @@ + + + @@ -9381,6 +9402,9 @@ + + + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index fef4c0bf1b9..b6d30cd95f8 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2529,6 +2529,15 @@ + + + + + + + + + @@ -2556,6 +2565,15 @@ + + + + + + + + + @@ -4293,6 +4311,12 @@ + + + + + + @@ -5589,6 +5613,12 @@ + + + + + + @@ -7440,6 +7470,9 @@ + + + @@ -9381,6 +9414,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index 42785ba47cb..83c9542ac40 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2529,6 +2529,15 @@ + + + + + + + + + @@ -2556,6 +2565,15 @@ + + + + + + + + + @@ -4293,6 +4311,12 @@ + + + + + + @@ -5589,6 +5613,12 @@ + + + + + + @@ -7440,6 +7470,9 @@ + + + @@ -9381,6 +9414,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index fa924a9d856..1a9638ca02d 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2520,6 +2520,15 @@ + + + + + + + + + @@ -2547,6 +2556,15 @@ + + + + + + + + + @@ -4284,6 +4302,12 @@ + + + + + + @@ -5580,6 +5604,12 @@ + + + + + + @@ -7428,6 +7458,15 @@ + + + + + + + + + @@ -9366,6 +9405,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl index e2a8c44ad2a..4b662f5b9c3 100644 --- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2523,6 +2523,9 @@ + + + @@ -2556,6 +2559,9 @@ + + + @@ -4296,6 +4302,12 @@ + + + + + + @@ -5592,6 +5604,12 @@ + + + + + + @@ -7443,6 +7461,9 @@ + + + @@ -9387,6 +9408,9 @@ + + + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index 9d97508babb..e159b01229e 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -1107,6 +1107,12 @@ + + + + + + @@ -2043,6 +2049,12 @@ + + + + + + @@ -2421,6 +2433,12 @@ + + + + + + @@ -2523,6 +2541,9 @@ + + + @@ -2556,6 +2577,9 @@ + + + @@ -3138,6 +3162,12 @@ + + + + + + @@ -3693,6 +3723,12 @@ + + + + + + @@ -4296,6 +4332,12 @@ + + + + + + @@ -5592,6 +5634,12 @@ + + + + + + @@ -5610,6 +5658,18 @@ + + + + + + + + + + + + @@ -6006,6 +6066,18 @@ + + + + + + + + + + + + @@ -6432,6 +6504,12 @@ + + + + + + @@ -6444,6 +6522,12 @@ + + + + + + @@ -7443,6 +7527,9 @@ + + + @@ -9387,6 +9474,9 @@ + + + diff --git a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl index f7c9f90e59e..6f596916ae7 100644 --- a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2510,6 +2510,15 @@ + + + + + + + + + @@ -2537,6 +2546,15 @@ + + + + + + + + + @@ -4274,6 +4292,12 @@ + + + + + + @@ -5570,6 +5594,12 @@ + + + + + + @@ -7415,6 +7445,15 @@ + + + + + + + + + @@ -9353,6 +9392,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl index 72d1e44d2d4..5d69f25ad5d 100644 --- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2510,6 +2510,15 @@ + + + + + + + + + @@ -2537,6 +2546,15 @@ + + + + + + + + + @@ -4274,6 +4292,12 @@ + + + + + + @@ -5570,6 +5594,12 @@ + + + + + + @@ -7418,6 +7448,9 @@ + + + @@ -9359,6 +9392,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index 098f08d8cd2..055e897855c 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2519,6 +2519,15 @@ + + + + + + + + + @@ -2546,6 +2555,15 @@ + + + + + + + + + @@ -4283,6 +4301,12 @@ + + + + + + @@ -5579,6 +5603,12 @@ + + + + + + @@ -7427,6 +7457,15 @@ + + + + + + + + + @@ -9365,6 +9404,15 @@ + + + + + + + + + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 4da1437d75c..19679bd8c21 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2516,6 +2516,9 @@ + + + @@ -2549,6 +2552,9 @@ + + + @@ -4289,6 +4295,12 @@ + + + + + + @@ -5585,6 +5597,12 @@ + + + + + + @@ -7436,6 +7454,9 @@ + + + @@ -9380,6 +9401,9 @@ + + + diff --git a/src/server/client.ts b/src/server/client.ts index 39231ae878d..a07e793dcba 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -139,6 +139,11 @@ namespace ts.server { this.processRequest(CommandNames.Change, args); } + toLineColumnOffset(fileName: string, position: number) { + const { line, offset } = this.positionToOneBasedLineOffset(fileName, position); + return { line, character: offset }; + } + getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { const args = this.createFileLocationRequestArgs(fileName, position); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4a91b287986..9de36f0687f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -6,6 +6,7 @@ namespace ts.server { export const ConfigFileDiagEvent = "configFileDiag"; export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; export const ProjectInfoTelemetryEvent = "projectInfo"; + export const OpenFileInfoTelemetryEvent = "openFileInfo"; // tslint:enable variable-name export interface ProjectsUpdatedInBackgroundEvent { @@ -55,6 +56,20 @@ namespace ts.server { readonly version: string; } + /** + * Info that we may send about a file that was just opened. + * Info about a file will only be sent once per session, even if the file changes in ways that might affect the info. + * Currently this is only sent for '.js' files. + */ + export interface OpenFileInfoTelemetryEvent { + readonly eventName: typeof OpenFileInfoTelemetryEvent; + readonly data: OpenFileInfoTelemetryEventData; + } + + export interface OpenFileInfoTelemetryEventData { + readonly info: OpenFileInfo; + } + export interface ProjectInfoTypeAcquisitionData { readonly enable: boolean; // Actual values of include/exclude entries are scrubbed. @@ -70,7 +85,11 @@ namespace ts.server { readonly dts: number; } - export type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; + export interface OpenFileInfo { + readonly checkJs: boolean; + } + + export type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent; export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void; @@ -325,6 +344,9 @@ namespace ts.server { * Container of all known scripts */ private readonly filenameToScriptInfo = createMap(); + // Set of all '.js' files ever opened. + private readonly allJsFilesForOpenFileTelemetry = createMap(); + /** * Map to the real path of the infos */ @@ -577,7 +599,8 @@ namespace ts.server { return this.pendingProjectUpdates.has(project.getProjectName()); } - private sendProjectsUpdatedInBackgroundEvent() { + /* @internal */ + sendProjectsUpdatedInBackgroundEvent() { if (!this.eventHandler) { return; } @@ -1330,7 +1353,8 @@ namespace ts.server { configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined, wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories), typeAcquisition: parsedCommandLine.typeAcquisition, - compileOnSave: parsedCommandLine.compileOnSave + compileOnSave: parsedCommandLine.compileOnSave, + projectReferences: parsedCommandLine.projectReferences }; return { projectOptions, configFileErrors: errors, configFileSpecs: parsedCommandLine.configFileSpecs }; @@ -1463,7 +1487,8 @@ namespace ts.server { projectOptions.compilerOptions, lastFileExceededProgramSize, projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave, - cachedDirectoryStructureHost); + cachedDirectoryStructureHost, + projectOptions.projectReferences); project.configFileSpecs = configFileSpecs; // TODO: We probably should also watch the configFiles that are extended @@ -1588,6 +1613,7 @@ namespace ts.server { // Update the project project.configFileSpecs = configFileSpecs; project.setProjectErrors(configFileErrors); + project.updateReferences(projectOptions.projectReferences); const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); if (lastFileExceededProgramSize) { project.disableLanguageService(lastFileExceededProgramSize); @@ -2091,9 +2117,19 @@ namespace ts.server { this.printProjects(); + this.telemetryOnOpenFile(info); return { configFileName, configFileErrors }; } + private telemetryOnOpenFile(scriptInfo: ScriptInfo): void { + if (!this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) { + return; + } + + const info: OpenFileInfo = { checkJs: !!scriptInfo.getDefaultProject().getSourceFile(scriptInfo.path).checkJsDirective }; + this.eventHandler({ eventName: OpenFileInfoTelemetryEvent, data: { info } }); + } + /** * Close file whose contents is managed by the client * @param filename is absolute pathname diff --git a/src/server/project.ts b/src/server/project.ts index 9d8b3a36dd4..a19739d2ba1 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -6,9 +6,12 @@ namespace ts.server { External } + /* @internal */ + export type Mutable = { -readonly [K in keyof T]: T[K]; }; + /* @internal */ export function countEachFileTypes(infos: ScriptInfo[]): FileStats { - const result = { js: 0, jsx: 0, ts: 0, tsx: 0, dts: 0 }; + const result: Mutable = { js: 0, jsx: 0, ts: 0, tsx: 0, dts: 0 }; for (const info of infos) { switch (info.scriptKind) { case ScriptKind.JS: @@ -164,7 +167,7 @@ namespace ts.server { return hasOneOrMoreJsAndNoTsFiles(this); } - public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} { + public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} | undefined { const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules"))); log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`); const result = host.require(resolvedPath, moduleName); @@ -265,6 +268,10 @@ namespace ts.server { return this.projectStateVersion.toString(); } + getProjectReferences(): ReadonlyArray | undefined { + return undefined; + } + getScriptFileNames() { if (!this.rootFiles) { return ts.emptyArray; @@ -1102,6 +1109,11 @@ namespace ts.server { } } + /** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */ + refreshDiagnostics() { + this.projectService.sendProjectsUpdatedInBackgroundEvent(); + } + private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) { try { if (typeof pluginModuleFactory !== "function") { @@ -1283,7 +1295,8 @@ namespace ts.server { compilerOptions: CompilerOptions, lastFileExceededProgramSize: string | undefined, public compileOnSaveEnabled: boolean, - cachedDirectoryStructureHost: CachedDirectoryStructureHost) { + cachedDirectoryStructureHost: CachedDirectoryStructureHost, + private projectReferences: ReadonlyArray | undefined) { super(configFileName, ProjectKind.Configured, projectService, @@ -1325,6 +1338,14 @@ namespace ts.server { return asNormalizedPath(this.getProjectName()); } + getProjectReferences(): ReadonlyArray | undefined { + return this.projectReferences; + } + + updateReferences(refs: ReadonlyArray | undefined) { + this.projectReferences = refs; + } + enablePlugins() { const host = this.projectService.host; const options = this.getCompilationSettings(); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d78e3f6bb0f..289478e7453 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1765,6 +1765,8 @@ namespace ts.server.protocol { arguments: FormatOnKeyRequestArgs; } + export type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; + /** * Arguments for completions messages. */ @@ -1773,7 +1775,7 @@ namespace ts.server.protocol { * Optional prefix to apply to possible completions. */ prefix?: string; - triggerCharacter?: string; + triggerCharacter?: CompletionsTriggerCharacter; /** * @deprecated Use UserPreferences.includeCompletionsForModuleExports */ @@ -2734,6 +2736,7 @@ namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; diff --git a/src/server/session.ts b/src/server/session.ts index bb635988755..8d6defa1562 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -699,12 +699,14 @@ namespace ts.server { } private toFileSpan(fileName: string, textSpan: TextSpan, project: Project): protocol.FileSpan { - const scriptInfo = project.getScriptInfo(fileName); + const ls = project.getLanguageService(); + const start = ls.toLineColumnOffset(fileName, textSpan.start); + const end = ls.toLineColumnOffset(fileName, textSpanEnd(textSpan)); return { file: fileName, - start: scriptInfo.positionToLineOffset(textSpan.start), - end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) + start: { line: start.line + 1, offset: start.character + 1 }, + end: { line: end.line + 1, offset: end.character + 1 } }; } diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index c255757481f..7440735ecb2 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -11,7 +11,7 @@ namespace ts.server { enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray): void; attach(projectService: ProjectService): void; onProjectClosed(p: Project): void; - readonly globalTypingsCacheLocation: string; + readonly globalTypingsCacheLocation: string | undefined; } export const nullTypingsInstaller: ITypingsInstaller = { diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index 35f926f1c14..875f5d881f0 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -2,7 +2,8 @@ namespace ts.server.typingsInstaller { const fs: { - appendFileSync(file: string, content: string): void + appendFileSync(file: string, content: string): void; + existsSync(path: string): boolean; } = require("fs"); const path: { @@ -32,11 +33,12 @@ namespace ts.server.typingsInstaller { /** Used if `--npmLocation` is not passed. */ function getDefaultNPMLocation(processName: string) { if (path.basename(processName).indexOf("node") === 0) { - return `"${path.join(path.dirname(process.argv[0]), "npm")}"`; - } - else { - return "npm"; + const npmPath = `"${path.join(path.dirname(process.argv[0]), "npm")}"`; + if (fs.existsSync(npmPath)) { + return npmPath; + } } + return "npm"; } interface TypesRegistryFile { diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 363393d401e..5705960f3d2 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -128,6 +128,8 @@ namespace ts.server { configHasFilesProperty: boolean; configHasIncludeProperty: boolean; configHasExcludeProperty: boolean; + + projectReferences: ReadonlyArray | undefined; /** * these fields can be present in the project file */ diff --git a/src/services/codefixes/convertToEs6Module.ts b/src/services/codefixes/convertToEs6Module.ts index a8df21dc96d..b496697d541 100644 --- a/src/services/codefixes/convertToEs6Module.ts +++ b/src/services/codefixes/convertToEs6Module.ts @@ -192,13 +192,17 @@ namespace ts.codefix { changes.deleteNode(sourceFile, assignment.parent); } else { - let newNodes = isObjectLiteralExpression(right) ? tryChangeModuleExportsObject(right) : undefined; - let changedToDefaultExport = false; - if (!newNodes) { - ([newNodes, changedToDefaultExport] = convertModuleExportsToExportDefault(right, checker)); + const replacement = isObjectLiteralExpression(right) ? tryChangeModuleExportsObject(right) + : isRequireCall(right, /*checkArgumentIsStringLiteralLike*/ true) ? convertReExportAll(right.arguments[0], checker) + : undefined; + if (replacement) { + changes.replaceNodeWithNodes(sourceFile, assignment.parent, replacement[0]); + return replacement[1]; + } + else { + changes.replaceRangeWithText(sourceFile, createTextRange(left.getStart(sourceFile), right.pos), "export default"); + return true; } - changes.replaceNodeWithNodes(sourceFile, assignment.parent, newNodes); - return changedToDefaultExport; } } else if (isExportsOrModuleExportsOrAlias(sourceFile, left.expression)) { @@ -212,8 +216,8 @@ namespace ts.codefix { * Convert `module.exports = { ... }` to individual exports.. * We can't always do this if the module has interesting members -- then it will be a default export instead. */ - function tryChangeModuleExportsObject(object: ObjectLiteralExpression): ReadonlyArray | undefined { - return mapAllOrFail(object.properties, prop => { + function tryChangeModuleExportsObject(object: ObjectLiteralExpression): [ReadonlyArray, ModuleExportsChanged] | undefined { + const statements = mapAllOrFail(object.properties, prop => { switch (prop.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: @@ -229,6 +233,7 @@ namespace ts.codefix { Debug.assertNever(prop); } }); + return statements && [statements, true]; } function convertNamedExport( @@ -256,31 +261,6 @@ namespace ts.codefix { } } - function convertModuleExportsToExportDefault(exported: Expression, checker: TypeChecker): [ReadonlyArray, ModuleExportsChanged] { - const modifiers = [createToken(SyntaxKind.ExportKeyword), createToken(SyntaxKind.DefaultKeyword)]; - switch (exported.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: { - // `module.exports = function f() {}` --> `export default function f() {}` - const fn = exported as FunctionExpression | ArrowFunction; - return [[functionExpressionToDeclaration(fn.name && fn.name.text, modifiers, fn)], true]; - } - case SyntaxKind.ClassExpression: { - // `module.exports = class C {}` --> `export default class C {}` - const cls = exported as ClassExpression; - return [[classExpressionToDeclaration(cls.name && cls.name.text, modifiers, cls)], true]; - } - case SyntaxKind.CallExpression: - if (isRequireCall(exported, /*checkArgumentIsStringLiteralLike*/ true)) { - return convertReExportAll(exported.arguments[0], checker); - } - // falls through - default: - // `module.exports = 0;` --> `export default 0;` - return [[createExportAssignment(/*decorators*/ undefined, /*modifiers*/ undefined, /*isExportEquals*/ false, exported)], true]; - } - } - function convertReExportAll(reExported: StringLiteralLike, checker: TypeChecker): [ReadonlyArray, ModuleExportsChanged] { // `module.exports = require("x");` ==> `export * from "x"; export { default } from "x";` const moduleSpecifier = reExported.text; diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index fd360c73ea1..5b411bedde4 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -8,6 +8,7 @@ namespace ts.codefix { Diagnostics._0_is_declared_but_never_used.code, Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code, Diagnostics.All_imports_in_import_declaration_are_unused.code, + Diagnostics.All_destructured_elements_are_unused.code, ]; registerCodeFix({ errorCodes, @@ -18,6 +19,10 @@ namespace ts.codefix { const changes = textChanges.ChangeTracker.with(context, t => t.deleteNode(sourceFile, importDecl)); return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; } + const delDestructure = textChanges.ChangeTracker.with(context, t => tryDeleteFullDestructure(t, sourceFile, context.span.start)); + if (delDestructure.length) { + return [createCodeFixAction(fixName, delDestructure, Diagnostics.Remove_destructuring, fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; + } const token = getToken(sourceFile, textSpanEnd(context.span)); const result: CodeFixAction[] = []; @@ -50,7 +55,9 @@ namespace ts.codefix { changes.deleteNode(sourceFile, importDecl); } else { - tryDeleteDeclaration(changes, sourceFile, token); + if (!tryDeleteFullDestructure(changes, sourceFile, diag.start!)) { + tryDeleteDeclaration(changes, sourceFile, token); + } } break; default: @@ -65,6 +72,26 @@ namespace ts.codefix { return startToken.kind === SyntaxKind.ImportKeyword ? tryCast(startToken.parent, isImportDeclaration) : undefined; } + function tryDeleteFullDestructure(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number): boolean { + const startToken = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + if (startToken.kind !== SyntaxKind.OpenBraceToken || !isObjectBindingPattern(startToken.parent)) return false; + const decl = startToken.parent.parent; + switch (decl.kind) { + case SyntaxKind.VariableDeclaration: + tryDeleteVariableDeclaration(changes, sourceFile, decl); + break; + case SyntaxKind.Parameter: + changes.deleteNodeInList(sourceFile, decl); + break; + case SyntaxKind.BindingElement: + changes.deleteNode(sourceFile, decl); + break; + default: + return Debug.assertNever(decl); + } + return true; + } + function getToken(sourceFile: SourceFile, pos: number): Node { const token = findPrecedingToken(pos, sourceFile); // this handles var ["computed"] = 12; @@ -171,6 +198,21 @@ namespace ts.codefix { } break; + case SyntaxKind.BindingElement: { + const pattern = (parent as BindingElement).parent; + switch (pattern.kind) { + case SyntaxKind.ArrayBindingPattern: + changes.deleteNode(sourceFile, parent); // Don't delete ',' + break; + case SyntaxKind.ObjectBindingPattern: + changes.deleteNodeInList(sourceFile, parent); + break; + default: + return Debug.assertNever(pattern); + } + break; + } + // handle case where 'import a = A;' case SyntaxKind.ImportEqualsDeclaration: const importEquals = getAncestor(identifier, SyntaxKind.ImportEqualsDeclaration); diff --git a/src/services/completions.ts b/src/services/completions.ts index f5aba7f99e8..4950d1370e1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -25,7 +25,7 @@ namespace ts.Completions { const enum GlobalsSearch { Continue, Success, Fail } - export function getCompletionsAtPosition(host: LanguageServiceHost, program: Program, log: Log, sourceFile: SourceFile, position: number, preferences: UserPreferences, triggerCharacter: string | undefined): CompletionInfo | undefined { + export function getCompletionsAtPosition(host: LanguageServiceHost, program: Program, log: Log, sourceFile: SourceFile, position: number, preferences: UserPreferences, triggerCharacter: CompletionsTriggerCharacter | undefined): CompletionInfo | undefined { const typeChecker = program.getTypeChecker(); const compilerOptions = program.getCompilerOptions(); if (isInReferenceComment(sourceFile, position)) { @@ -81,7 +81,7 @@ namespace ts.Completions { return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries }; } case StringLiteralCompletionKind.Types: { - const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.typeElement, sortText: "0" })); + const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.string, sortText: "0" })); return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, entries }; } default: @@ -2208,8 +2208,12 @@ namespace ts.Completions { return !!type.getStringIndexType() || !!type.getNumberIndexType(); } - function isValidTrigger(sourceFile: SourceFile, triggerCharacter: string, contextToken: Node, position: number): boolean { + function isValidTrigger(sourceFile: SourceFile, triggerCharacter: CompletionsTriggerCharacter, contextToken: Node, position: number): boolean { switch (triggerCharacter) { + case ".": + case "/": + case "@": + return true; case '"': case "'": case "`": @@ -2219,7 +2223,7 @@ namespace ts.Completions { // Opening JSX tag return contextToken.kind === SyntaxKind.LessThanToken && contextToken.parent.kind !== SyntaxKind.BinaryExpression; default: - return Debug.fail(triggerCharacter); + return Debug.assertNever(triggerCharacter); } } diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 69c75548fe5..65653483d07 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -74,6 +74,10 @@ namespace ts.DocumentHighlights { case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: return getFromAllDeclarations(isAccessor, [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]); + case SyntaxKind.AwaitKeyword: + return useParent(node.parent, isAwaitExpression, getAsyncAndAwaitOccurrences); + case SyntaxKind.AsyncKeyword: + return highlightSpans(getAsyncAndAwaitOccurrences(node)); default: return isModifierKind(node.kind) && (isDeclaration(node.parent) || isVariableStatement(node.parent)) ? highlightSpans(getModifierOccurrences(node.kind, node.parent)) @@ -368,6 +372,35 @@ namespace ts.DocumentHighlights { return keywords; } + function getAsyncAndAwaitOccurrences(node: Node): Node[] | undefined { + const func = getContainingFunction(node); + if (!func) { + return undefined; + } + + const keywords: Node[] = []; + + if (func.modifiers) { + func.modifiers.forEach(modifier => { + pushKeywordIf(keywords, modifier, SyntaxKind.AsyncKeyword); + }); + } + + forEachChild(func, aggregate); + + return keywords; + + function aggregate(node: Node): void { + if (isAwaitExpression(node)) { + pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.AwaitKeyword); + } + // Do not cross function boundaries. + if (!isFunctionLike(node) && !isClassLike(node) && !isInterfaceDeclaration(node) && !isModuleDeclaration(node) && !isTypeAliasDeclaration(node) && !isTypeNode(node)) { + forEachChild(node, aggregate); + } + } + } + function getIfElseOccurrences(ifStatement: IfStatement, sourceFile: SourceFile): HighlightSpan[] { const keywords = getIfElseKeywords(ifStatement, sourceFile); const result: HighlightSpan[] = []; diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 81742018fb7..d464f7a408a 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -172,9 +172,10 @@ namespace ts { const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true); let entry = bucket.get(path); + const scriptTarget = scriptKind === ScriptKind.JSON ? ScriptTarget.JSON : compilationSettings.target; if (!entry) { // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile, diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 8054ea666f3..bedb657a66d 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -119,7 +119,7 @@ namespace ts.FindAllReferences { const { node } = def; const symbol = checker.getSymbolAtLocation(node); const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind( - checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts; + checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts || [textPart("this")]; return { node, name: "this", kind: ScriptElementKind.variableElement, displayParts }; } case "string": { @@ -272,6 +272,8 @@ namespace ts.FindAllReferences.Core { case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: return true; + case SyntaxKind.LiteralType: + return isImportTypeNode(node.parent.parent); case SyntaxKind.CallExpression: return isRequireCall(node.parent as CallExpression, /*checkArgumentIsStringLiteralLike*/ false) || isImportCall(node.parent as CallExpression); default: @@ -407,7 +409,6 @@ namespace ts.FindAllReferences.Core { return firstDefined(symbol.declarations, decl => { if (!decl.parent) { // Assertions for GH#21814. We should be handling SourceFile symbols in `getReferencedSymbolsForModule` instead of getting here. - Debug.assert(decl.kind === SyntaxKind.SourceFile); Debug.fail(`Unexpected symbol at ${Debug.showSyntaxKind(node)}: ${Debug.showSymbol(symbol)}`); } return isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent) diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index a82f11f239b..0e89be42532 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -327,9 +327,10 @@ namespace ts.formatting { export function getContainingList(node: Node, sourceFile: SourceFile): NodeArray { if (node.parent) { + const { end } = node; switch (node.parent.kind) { case SyntaxKind.TypeReference: - return getListIfStartEndIsInListRange((node.parent).typeArguments, node.getStart(sourceFile), node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).typeArguments, node.getStart(sourceFile), end); case SyntaxKind.ObjectLiteralExpression: return (node.parent).properties; case SyntaxKind.ArrayLiteralExpression: @@ -344,22 +345,25 @@ namespace ts.formatting { case SyntaxKind.ConstructorType: case SyntaxKind.ConstructSignature: { const start = node.getStart(sourceFile); - return getListIfStartEndIsInListRange((node.parent).typeParameters, start, node.getEnd()) || - getListIfStartEndIsInListRange((node.parent).parameters, start, node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).typeParameters, start, end) || + getListIfStartEndIsInListRange((node.parent).parameters, start, end); } case SyntaxKind.ClassDeclaration: - return getListIfStartEndIsInListRange((node.parent).typeParameters, node.getStart(sourceFile), node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).typeParameters, node.getStart(sourceFile), end); case SyntaxKind.NewExpression: case SyntaxKind.CallExpression: { const start = node.getStart(sourceFile); - return getListIfStartEndIsInListRange((node.parent).typeArguments, start, node.getEnd()) || - getListIfStartEndIsInListRange((node.parent).arguments, start, node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).typeArguments, start, end) || + getListIfStartEndIsInListRange((node.parent).arguments, start, end); } case SyntaxKind.VariableDeclarationList: - return getListIfStartEndIsInListRange((node.parent).declarations, node.getStart(sourceFile), node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).declarations, node.getStart(sourceFile), end); case SyntaxKind.NamedImports: case SyntaxKind.NamedExports: - return getListIfStartEndIsInListRange((node.parent).elements, node.getStart(sourceFile), node.getEnd()); + return getListIfStartEndIsInListRange((node.parent).elements, node.getStart(sourceFile), end); + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + return getListIfStartEndIsInListRange((node.parent).elements, node.getStart(sourceFile), end); } } return undefined; diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index a5f1c5ed4f4..d5fc94c6d62 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -45,7 +45,7 @@ namespace ts { if (checker.getSymbolAtLocation(importStringLiteral)) continue; const resolved = program.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName); - if (contains(resolved.failedLookupLocations, oldFilePath)) { + if (resolved && contains(resolved.failedLookupLocations, oldFilePath)) { result.push({ sourceFile, toUpdate: importStringLiteral }); } } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 10684c268be..91754e2ac76 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -9,7 +9,27 @@ namespace ts.OutliningElementsCollector { function addNodeOutliningSpans(sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push): void { let depthRemaining = 40; - sourceFile.forEachChild(function walk(n) { + let current = 0; + const statements = sourceFile.statements; + const n = statements.length; + while (current < n) { + while (current < n && !isAnyImportSyntax(statements[current])) { + visitNonImportNode(statements[current]); + current++; + } + if (current === n) break; + const firstImport = current; + while (current < n && isAnyImportSyntax(statements[current])) { + addOutliningForLeadingCommentsForNode(statements[current], sourceFile, cancellationToken, out); + current++; + } + const lastImport = current - 1; + if (lastImport !== firstImport) { + out.push(createOutliningSpanFromBounds(findChildOfKind(statements[firstImport], SyntaxKind.ImportKeyword, sourceFile)!.getStart(sourceFile), statements[lastImport].getEnd(), OutliningSpanKind.Imports)); + } + } + + function visitNonImportNode(n: Node) { if (depthRemaining === 0) return; cancellationToken.throwIfCancellationRequested(); @@ -23,17 +43,17 @@ namespace ts.OutliningElementsCollector { depthRemaining--; if (isIfStatement(n) && n.elseStatement && isIfStatement(n.elseStatement)) { // Consider an 'else if' to be on the same depth as the 'if'. - walk(n.expression); - walk(n.thenStatement); + visitNonImportNode(n.expression); + visitNonImportNode(n.thenStatement); depthRemaining++; - walk(n.elseStatement); + visitNonImportNode(n.elseStatement); depthRemaining--; } else { - n.forEachChild(walk); + n.forEachChild(visitNonImportNode); } depthRemaining++; - }); + } } function addRegionOutliningSpans(sourceFile: SourceFile, out: Push): void { diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index 7fd3b7cfd0b..f42d70a0c52 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -27,7 +27,7 @@ namespace ts.Completions.PathCompletions { const scriptDirectory = getDirectoryPath(scriptPath); if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) { - const extensions = getSupportedExtensions(compilerOptions); + const extensions = getSupportedExtensionsForModuleResolution(compilerOptions); if (compilerOptions.rootDirs) { return getCompletionEntriesForDirectoryFragmentWithRootDirs( compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, compilerOptions, host, scriptPath); @@ -42,6 +42,13 @@ namespace ts.Completions.PathCompletions { } } + function getSupportedExtensionsForModuleResolution(compilerOptions: CompilerOptions) { + const extensions = getSupportedExtensions(compilerOptions); + return compilerOptions.resolveJsonModule && getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeJs ? + extensions.concat(Extension.Json) : + extensions; + } + /** * Takes a script path and returns paths for all potential folders that could be merged with its * containing folder via the "rootDirs" compiler option @@ -122,7 +129,7 @@ namespace ts.Completions.PathCompletions { continue; } - const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); + const foundFileName = includeExtensions || fileExtensionIs(filePath, Extension.Json) ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); if (!foundFiles.has(foundFileName)) { foundFiles.set(foundFileName, true); @@ -162,7 +169,7 @@ namespace ts.Completions.PathCompletions { const result: NameAndKind[] = []; - const fileExtensions = getSupportedExtensions(compilerOptions); + const fileExtensions = getSupportedExtensionsForModuleResolution(compilerOptions); if (baseUrl) { const projectDir = compilerOptions.project || host.getCurrentDirectory(); const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl); diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index 174b3fa5ffc..9df31ddce51 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -1,7 +1,7 @@ namespace ts { export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo { const pragmaContext: PragmaContext = { - languageVersion: ScriptTarget.ES5, // controls weather the token scanner considers unicode identifiers or not - shouldn't matter, since we're only using it for trivia + languageVersion: ScriptTarget.ES5, // controls whether the token scanner considers unicode identifiers or not - shouldn't matter, since we're only using it for trivia pragmas: undefined, checkJsDirective: undefined, referencedFiles: [], diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 33ecbbaab66..523ad97e5fa 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -49,7 +49,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { : undefined; const fieldModifiers = isInClassLike ? getModifiers(isJS, isStatic, SyntaxKind.PrivateKeyword) : undefined; - updateFieldDeclaration(changeTracker, file, declaration, fieldName, fieldModifiers, container); + updateFieldDeclaration(changeTracker, file, declaration, fieldName, fieldModifiers); const getAccessor = generateGetAccessor(fieldName, accessorName, type, accessorModifiers, isStatic, container); const setAccessor = generateSetAccessor(fieldName, accessorName, type, accessorModifiers, isStatic, container); @@ -60,7 +60,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const edits = changeTracker.getChanges(); const renameFilename = file.fileName; const renameLocationOffset = isIdentifier(fieldName) ? 0 : -1; - const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, fieldName.text, /*isDeclaredBeforeUse*/ false); + const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, fieldName.text, /*preferLastLocation*/ isParameter(declaration)); return { renameFilename, renameLocation, edits }; } @@ -163,26 +163,12 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { changeTracker.replaceNode(file, declaration, property); } - function updateParameterPropertyDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: ParameterDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined, classLikeContainer: ClassLikeDeclaration) { - const property = createProperty( - declaration.decorators, - modifiers, - fieldName, - declaration.questionToken, - declaration.type, - declaration.initializer - ); - - changeTracker.insertNodeAtClassStart(file, classLikeContainer, property); - changeTracker.deleteNodeInList(file, declaration); - } - - function updatePropertyAssignmentDeclaration (changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyAssignment, fieldName: AcceptedNameType) { + function updatePropertyAssignmentDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyAssignment, fieldName: AcceptedNameType) { const assignment = updatePropertyAssignment(declaration, fieldName, declaration.initializer); changeTracker.replacePropertyAssignment(file, declaration, assignment); } - function updateFieldDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: AcceptedDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined, container: ContainerDeclaration) { + function updateFieldDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: AcceptedDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined) { if (isPropertyDeclaration(declaration)) { updatePropertyDeclaration(changeTracker, file, declaration, fieldName, modifiers); } @@ -190,7 +176,8 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { updatePropertyAssignmentDeclaration(changeTracker, file, declaration, fieldName); } else { - updateParameterPropertyDeclaration(changeTracker, file, declaration, fieldName, modifiers, container); + changeTracker.replaceNode(file, declaration, + updateParameter(declaration, declaration.decorators, modifiers, declaration.dotDotDotToken, cast(fieldName, isIdentifier), declaration.questionToken, declaration.type, declaration.initializer)); } } diff --git a/src/services/services.ts b/src/services/services.ts index 96f995690e8..da8cb57439f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -879,6 +879,10 @@ namespace ts { return this._compilationSettings; } + public getProjectReferences(): ReadonlyArray | undefined { + return this.host.getProjectReferences && this.host.getProjectReferences(); + } + private createEntry(fileName: string, path: Path) { let entry: CachedHostFileInformation; const scriptSnapshot = this.host.getScriptSnapshot(fileName); @@ -913,9 +917,18 @@ namespace ts { } public getRootFileNames(): string[] { - return arrayFrom(this.fileNameToEntry.values(), entry => { - return isString(entry) ? entry : entry.hostFileName; + const names: string[] = []; + this.fileNameToEntry.forEach(entry => { + if (isString(entry)) { + names.push(entry); + } + else { + if (entry.scriptKind !== ScriptKind.JSON) { + names.push(entry.hostFileName); + } + } }); + return names; } public getVersion(path: Path): string { @@ -1242,7 +1255,14 @@ namespace ts { } const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings); - program = createProgram(rootFileNames, newSettings, compilerHost, program); + const options: CreateProgramOptions = { + rootNames: rootFileNames, + options: newSettings, + host: compilerHost, + oldProgram: program, + projectReferences: hostCache.getProjectReferences() + }; + program = createProgram(options); // hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point. // It needs to be cleared to allow all collected snapshots to be released @@ -1502,6 +1522,12 @@ namespace ts { return checker.getSymbolAtLocation(node); } + function toLineColumnOffset(fileName: string, position: number) { + const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const file = program.getSourceFile(path) || sourcemappedFileCache.get(path); + return file.getLineAndCharacterOfPosition(position); + } + // Sometimes tools can sometimes see the following line as a source mapping url comment, so we mangle it a bit (the [M]) const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)$/gm; const base64UrlRegExp = /^data:(?:application\/json(?:;charset=[uU][tT][fF]-8);base64,([A-Za-z0-9+\/=]+)$)?/; @@ -2266,6 +2292,7 @@ namespace ts { getProgram, getApplicableRefactors, getEditsForRefactor, + toLineColumnOffset }; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index d5ef90ff3ef..eca1c639196 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -442,19 +442,25 @@ namespace ts.textChanges { public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration, newElement: ClassElement): void { const clsStart = cls.getStart(sourceFile); - let prefix = ""; - let suffix = this.newLineCharacter; - if (addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(cls), cls)) { - prefix = this.newLineCharacter; - // For `class C {\n}`, don't add the trailing "\n" - if (cls.members.length === 0 && !(positionsAreOnSameLine as any)(...getClassBraceEnds(cls, sourceFile), sourceFile)) { // TODO: GH#4130 remove 'as any' - suffix = ""; - } - } - const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(clsStart, sourceFile), clsStart, sourceFile, this.formatContext.options) + this.formatContext.options.indentSize; - this.insertNodeAt(sourceFile, cls.members.pos, newElement, { indentation, prefix, suffix }); + this.insertNodeAt(sourceFile, cls.members.pos, newElement, { indentation, ...this.getInsertNodeAtClassStartPrefixSuffix(sourceFile, cls) }); + } + + private getInsertNodeAtClassStartPrefixSuffix(sourceFile: SourceFile, cls: ClassLikeDeclaration): { prefix: string, suffix: string } { + if (cls.members.length === 0) { + if (addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(cls), cls)) { + // For `class C {\n}`, don't add the trailing "\n" + const shouldSuffix = (positionsAreOnSameLine as any)(...getClassBraceEnds(cls, sourceFile), sourceFile); // TODO: GH#4130 remove 'as any' + return { prefix: this.newLineCharacter, suffix: shouldSuffix ? this.newLineCharacter : "" }; + } + else { + return { prefix: "", suffix: this.newLineCharacter }; + } + } + else { + return { prefix: this.newLineCharacter, suffix: "" }; + } } public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node): this { diff --git a/src/services/types.ts b/src/services/types.ts index 1f5c3ba08ed..96a364eb957 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -181,6 +181,7 @@ namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; @@ -322,6 +323,8 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -357,9 +360,11 @@ namespace ts { export type OrganizeImportsScope = CombinedCodeFixScope; + export type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; + export interface GetCompletionsAtPositionOptions extends UserPreferences { /** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */ - triggerCharacter?: string; + triggerCharacter?: CompletionsTriggerCharacter; /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ @@ -826,9 +831,17 @@ namespace ts { } export const enum OutliningSpanKind { + /** Single or multi-line comments */ Comment = "comment", + + /** Sections marked by '// #region' and '// #endregion' comments */ Region = "region", - Code = "code" + + /** Declarations and expressions */ + Code = "code", + + /** Contiguous blocks of import declarations */ + Imports = "imports" } export const enum OutputFileType { @@ -989,6 +1002,9 @@ namespace ts { * */ jsxAttribute = "JSX attribute", + + /** String literal */ + string = "string", } export const enum ScriptElementKindModifier { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 453abdea9ee..3d50ded1886 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -940,22 +940,22 @@ namespace ts { break; case SyntaxKind.CloseBraceToken: - // This can be object type, skip untill we find the matching open brace token - // Skip untill the matching open brace token + // This can be object type, skip until we find the matching open brace token + // Skip until the matching open brace token token = findPrecedingMatchingToken(token, SyntaxKind.OpenBraceToken, sourceFile); if (!token) return false; break; case SyntaxKind.CloseParenToken: - // This can be object type, skip untill we find the matching open brace token - // Skip untill the matching open brace token + // This can be object type, skip until we find the matching open brace token + // Skip until the matching open brace token token = findPrecedingMatchingToken(token, SyntaxKind.OpenParenToken, sourceFile); if (!token) return false; break; case SyntaxKind.CloseBracketToken: - // This can be object type, skip untill we find the matching open brace token - // Skip untill the matching open brace token + // This can be object type, skip until we find the matching open brace token + // Skip until the matching open brace token token = findPrecedingMatchingToken(token, SyntaxKind.OpenBracketToken, sourceFile); if (!token) return false; break; @@ -1538,7 +1538,7 @@ namespace ts { * user was before extracting it. */ /* @internal */ - export function getRenameLocation(edits: ReadonlyArray, renameFilename: string, name: string, isDeclaredBeforeUse: boolean): number { + export function getRenameLocation(edits: ReadonlyArray, renameFilename: string, name: string, preferLastLocation: boolean): number { let delta = 0; let lastPos = -1; for (const { fileName, textChanges } of edits) { @@ -1550,7 +1550,7 @@ namespace ts { lastPos = span.start + delta + index; // If the reference comes first, return immediately. - if (!isDeclaredBeforeUse) { + if (!preferLastLocation) { return lastPos; } } @@ -1559,7 +1559,7 @@ namespace ts { } // If the declaration comes first, return the position of the last occurrence. - Debug.assert(isDeclaredBeforeUse); + Debug.assert(preferLastLocation); Debug.assert(lastPos >= 0); return lastPos; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3225122cc9a..6865d40fd81 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -337,32 +337,34 @@ declare namespace ts { EnumMember = 272, SourceFile = 273, Bundle = 274, - JSDocTypeExpression = 275, - JSDocAllType = 276, - JSDocUnknownType = 277, - JSDocNullableType = 278, - JSDocNonNullableType = 279, - JSDocOptionalType = 280, - JSDocFunctionType = 281, - JSDocVariadicType = 282, - JSDocComment = 283, - JSDocTypeLiteral = 284, - JSDocTag = 285, - JSDocAugmentsTag = 286, - JSDocClassTag = 287, - JSDocParameterTag = 288, - JSDocReturnTag = 289, - JSDocTypeTag = 290, - JSDocTemplateTag = 291, - JSDocTypedefTag = 292, - JSDocPropertyTag = 293, - SyntaxList = 294, - NotEmittedStatement = 295, - PartiallyEmittedExpression = 296, - CommaListExpression = 297, - MergeDeclarationMarker = 298, - EndOfDeclarationMarker = 299, - Count = 300, + UnparsedSource = 275, + InputFiles = 276, + JSDocTypeExpression = 277, + JSDocAllType = 278, + JSDocUnknownType = 279, + JSDocNullableType = 280, + JSDocNonNullableType = 281, + JSDocOptionalType = 282, + JSDocFunctionType = 283, + JSDocVariadicType = 284, + JSDocComment = 285, + JSDocTypeLiteral = 286, + JSDocTag = 287, + JSDocAugmentsTag = 288, + JSDocClassTag = 289, + JSDocParameterTag = 290, + JSDocReturnTag = 291, + JSDocTypeTag = 292, + JSDocTemplateTag = 293, + JSDocTypedefTag = 294, + JSDocPropertyTag = 295, + SyntaxList = 296, + NotEmittedStatement = 297, + PartiallyEmittedExpression = 298, + CommaListExpression = 299, + MergeDeclarationMarker = 300, + EndOfDeclarationMarker = 301, + Count = 302, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -388,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1150,7 +1152,7 @@ declare namespace ts { kind: SyntaxKind.NotEmittedStatement; } /** - * A list of comma-seperated expressions. This node is only created by transformations. + * A list of comma-separated expressions. This node is only created by transformations. */ interface CommaListExpression extends Expression { kind: SyntaxKind.CommaListExpression; @@ -1646,8 +1648,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + declarationText: string; + } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { statements: NodeArray; } @@ -1717,12 +1729,19 @@ declare namespace ts { */ getTypeChecker(): TypeChecker; isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } interface CustomTransformers { - /** Custom transformers to evaluate before built-in transformations. */ + /** Custom transformers to evaluate before built-in .js transformations. */ before?: TransformerFactory[]; - /** Custom transformers to evaluate after built-in transformations. */ + /** Custom transformers to evaluate after built-in .js transformations. */ after?: TransformerFactory[]; + /** Custom transformers to evaluate after built-in .d.ts transformations. */ + afterDeclarations?: TransformerFactory[]; } interface SourceMapSpan { /** Line number in the .js file. */ @@ -2336,7 +2355,17 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + interface ProjectReference { + /** A normalized path on disk */ + path: string; + /** The path as the user originally wrote it */ + originalPath?: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ + prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; + } + type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2392,6 +2421,7 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2467,6 +2497,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { @@ -2478,6 +2509,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2489,8 +2521,17 @@ declare namespace ts { } interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; } + interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } interface ModuleResolutionHost { fileExists(fileName: string): boolean; readFile(fileName: string): string | undefined; @@ -2581,6 +2622,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files @@ -3747,8 +3789,10 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray): Bundle; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createUnparsedSourceFile(text: string): UnparsedSource; + function createInputFiles(javascript: string, declaration: string): InputFiles; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; @@ -3955,6 +3999,7 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ + function createProgram(createProgramOptions: CreateProgramOptions): Program; function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; } declare namespace ts { @@ -4094,7 +4139,6 @@ declare namespace ts { function createAbstractBuilder(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray): BuilderProgram; } declare namespace ts { - type DiagnosticReporter = (diagnostic: Diagnostic) => void; type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void; /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ type CreateProgram = (rootNames: ReadonlyArray | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray) => T; @@ -4157,15 +4201,6 @@ declare namespace ts { /** Compiler options */ options: CompilerOptions; } - /** - * Reports config file diagnostics - */ - interface ConfigFileDiagnosticsReporter { - /** - * Reports unrecoverable error when parsing config file - */ - onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; - } /** * Host to create watch with config file */ @@ -4212,6 +4247,26 @@ declare namespace ts { } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; + type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined; /** * Read tsconfig.json file * @param fileName The path to the config file @@ -4385,6 +4440,7 @@ declare namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; @@ -4462,6 +4518,7 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -4486,9 +4543,10 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; + type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; interface GetCompletionsAtPositionOptions extends UserPreferences { /** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */ - triggerCharacter?: string; + triggerCharacter?: CompletionsTriggerCharacter; /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ @@ -4892,9 +4950,14 @@ declare namespace ts { kind: OutliningSpanKind; } enum OutliningSpanKind { + /** Single or multi-line comments */ Comment = "comment", + /** Sections marked by '// #region' and '// #endregion' comments */ Region = "region", - Code = "code" + /** Declarations and expressions */ + Code = "code", + /** Contiguous blocks of import declarations */ + Imports = "imports" } enum OutputFileType { JavaScript = 0, @@ -5018,7 +5081,9 @@ declare namespace ts { /** * */ - jsxAttribute = "JSX attribute" + jsxAttribute = "JSX attribute", + /** String literal */ + string = "string" } enum ScriptElementKindModifier { none = "", @@ -5378,6 +5443,7 @@ declare namespace ts.server { configHasFilesProperty: boolean; configHasIncludeProperty: boolean; configHasExcludeProperty: boolean; + projectReferences: ReadonlyArray | undefined; /** * these fields can be present in the project file */ @@ -6716,6 +6782,7 @@ declare namespace ts.server.protocol { command: CommandTypes.Formatonkey; arguments: FormatOnKeyRequestArgs; } + type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; /** * Arguments for completions messages. */ @@ -6724,7 +6791,7 @@ declare namespace ts.server.protocol { * Optional prefix to apply to possible completions. */ prefix?: string; - triggerCharacter?: string; + triggerCharacter?: CompletionsTriggerCharacter; /** * @deprecated Use UserPreferences.includeCompletionsForModuleExports */ @@ -7557,6 +7624,7 @@ declare namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; @@ -7668,7 +7736,7 @@ declare namespace ts.server { enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray): void; attach(projectService: ProjectService): void; onProjectClosed(p: Project): void; - readonly globalTypingsCacheLocation: string; + readonly globalTypingsCacheLocation: string | undefined; } const nullTypingsInstaller: ITypingsInstaller; } @@ -7745,7 +7813,7 @@ declare namespace ts.server { private readonly cancellationToken; isNonTsProject(): boolean; isJsOnlyProject(): boolean; - static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {}; + static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} | undefined; isKnownTypesPackageName(name: string): boolean; installPackage(options: InstallPackageOptions): Promise; private readonly typingsCache; @@ -7753,6 +7821,7 @@ declare namespace ts.server { getCompilerOptions(): CompilerOptions; getNewLine(): string; getProjectVersion(): string; + getProjectReferences(): ReadonlyArray | undefined; getScriptFileNames(): string[]; private getOrCreateScriptInfoAndAttachToProject; getScriptKind(fileName: string): ScriptKind; @@ -7826,6 +7895,8 @@ declare namespace ts.server { protected removeRoot(info: ScriptInfo): void; protected enableGlobalPlugins(): void; protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void; + /** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */ + refreshDiagnostics(): void; private enableProxy; } /** @@ -7852,6 +7923,7 @@ declare namespace ts.server { */ class ConfiguredProject extends Project { compileOnSaveEnabled: boolean; + private projectReferences; private typeAcquisition; private directoriesWatchedForWildcards; readonly canonicalConfigFilePath: NormalizedPath; @@ -7864,6 +7936,8 @@ declare namespace ts.server { */ updateGraph(): boolean; getConfigFilePath(): NormalizedPath; + getProjectReferences(): ReadonlyArray | undefined; + updateReferences(refs: ReadonlyArray | undefined): void; enablePlugins(): void; /** * Get the errors that dont have any file name associated @@ -7899,6 +7973,7 @@ declare namespace ts.server { const ConfigFileDiagEvent = "configFileDiag"; const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; const ProjectInfoTelemetryEvent = "projectInfo"; + const OpenFileInfoTelemetryEvent = "openFileInfo"; interface ProjectsUpdatedInBackgroundEvent { eventName: typeof ProjectsUpdatedInBackgroundEvent; data: { @@ -7947,6 +8022,18 @@ declare namespace ts.server { /** TypeScript version used by the server. */ readonly version: string; } + /** + * Info that we may send about a file that was just opened. + * Info about a file will only be sent once per session, even if the file changes in ways that might affect the info. + * Currently this is only sent for '.js' files. + */ + interface OpenFileInfoTelemetryEvent { + readonly eventName: typeof OpenFileInfoTelemetryEvent; + readonly data: OpenFileInfoTelemetryEventData; + } + interface OpenFileInfoTelemetryEventData { + readonly info: OpenFileInfo; + } interface ProjectInfoTypeAcquisitionData { readonly enable: boolean; readonly include: boolean; @@ -7959,7 +8046,10 @@ declare namespace ts.server { readonly tsx: number; readonly dts: number; } - type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; + interface OpenFileInfo { + readonly checkJs: boolean; + } + type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent; type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void; interface SafeList { [name: string]: { @@ -8010,6 +8100,7 @@ declare namespace ts.server { * Container of all known scripts */ private readonly filenameToScriptInfo; + private readonly allJsFilesForOpenFileTelemetry; /** * maps external project file name to list of config files that were the part of this project */ @@ -8078,7 +8169,6 @@ declare namespace ts.server { updateTypingsForProject(response: SetTypings | InvalidateCachedTypings | PackageInstalledResponse): void; private delayEnsureProjectForOpenFiles; private delayUpdateProjectGraph; - private sendProjectsUpdatedInBackgroundEvent; private delayUpdateProjectGraphs; setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void; findProject(projectName: string): Project | undefined; @@ -8215,6 +8305,7 @@ declare namespace ts.server { openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult; private findExternalProjectContainingOpenScriptInfo; openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult; + private telemetryOnOpenFile; /** * Close file whose contents is managed by the client * @param filename is absolute pathname diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 80d8a5b8148..3e428b01dad 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -337,32 +337,34 @@ declare namespace ts { EnumMember = 272, SourceFile = 273, Bundle = 274, - JSDocTypeExpression = 275, - JSDocAllType = 276, - JSDocUnknownType = 277, - JSDocNullableType = 278, - JSDocNonNullableType = 279, - JSDocOptionalType = 280, - JSDocFunctionType = 281, - JSDocVariadicType = 282, - JSDocComment = 283, - JSDocTypeLiteral = 284, - JSDocTag = 285, - JSDocAugmentsTag = 286, - JSDocClassTag = 287, - JSDocParameterTag = 288, - JSDocReturnTag = 289, - JSDocTypeTag = 290, - JSDocTemplateTag = 291, - JSDocTypedefTag = 292, - JSDocPropertyTag = 293, - SyntaxList = 294, - NotEmittedStatement = 295, - PartiallyEmittedExpression = 296, - CommaListExpression = 297, - MergeDeclarationMarker = 298, - EndOfDeclarationMarker = 299, - Count = 300, + UnparsedSource = 275, + InputFiles = 276, + JSDocTypeExpression = 277, + JSDocAllType = 278, + JSDocUnknownType = 279, + JSDocNullableType = 280, + JSDocNonNullableType = 281, + JSDocOptionalType = 282, + JSDocFunctionType = 283, + JSDocVariadicType = 284, + JSDocComment = 285, + JSDocTypeLiteral = 286, + JSDocTag = 287, + JSDocAugmentsTag = 288, + JSDocClassTag = 289, + JSDocParameterTag = 290, + JSDocReturnTag = 291, + JSDocTypeTag = 292, + JSDocTemplateTag = 293, + JSDocTypedefTag = 294, + JSDocPropertyTag = 295, + SyntaxList = 296, + NotEmittedStatement = 297, + PartiallyEmittedExpression = 298, + CommaListExpression = 299, + MergeDeclarationMarker = 300, + EndOfDeclarationMarker = 301, + Count = 302, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -388,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1150,7 +1152,7 @@ declare namespace ts { kind: SyntaxKind.NotEmittedStatement; } /** - * A list of comma-seperated expressions. This node is only created by transformations. + * A list of comma-separated expressions. This node is only created by transformations. */ interface CommaListExpression extends Expression { kind: SyntaxKind.CommaListExpression; @@ -1646,8 +1648,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + declarationText: string; + } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { statements: NodeArray; } @@ -1717,12 +1729,19 @@ declare namespace ts { */ getTypeChecker(): TypeChecker; isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } interface CustomTransformers { - /** Custom transformers to evaluate before built-in transformations. */ + /** Custom transformers to evaluate before built-in .js transformations. */ before?: TransformerFactory[]; - /** Custom transformers to evaluate after built-in transformations. */ + /** Custom transformers to evaluate after built-in .js transformations. */ after?: TransformerFactory[]; + /** Custom transformers to evaluate after built-in .d.ts transformations. */ + afterDeclarations?: TransformerFactory[]; } interface SourceMapSpan { /** Line number in the .js file. */ @@ -2336,7 +2355,17 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + interface ProjectReference { + /** A normalized path on disk */ + path: string; + /** The path as the user originally wrote it */ + originalPath?: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ + prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; + } + type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2392,6 +2421,7 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2467,6 +2497,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { @@ -2478,6 +2509,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2489,8 +2521,17 @@ declare namespace ts { } interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; } + interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } interface ModuleResolutionHost { fileExists(fileName: string): boolean; readFile(fileName: string): string | undefined; @@ -2581,6 +2622,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files @@ -3747,8 +3789,10 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray): Bundle; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createUnparsedSourceFile(text: string): UnparsedSource; + function createInputFiles(javascript: string, declaration: string): InputFiles; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; @@ -3955,6 +3999,7 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ + function createProgram(createProgramOptions: CreateProgramOptions): Program; function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; } declare namespace ts { @@ -4094,7 +4139,6 @@ declare namespace ts { function createAbstractBuilder(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray): BuilderProgram; } declare namespace ts { - type DiagnosticReporter = (diagnostic: Diagnostic) => void; type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void; /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ type CreateProgram = (rootNames: ReadonlyArray | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray) => T; @@ -4157,15 +4201,6 @@ declare namespace ts { /** Compiler options */ options: CompilerOptions; } - /** - * Reports config file diagnostics - */ - interface ConfigFileDiagnosticsReporter { - /** - * Reports unrecoverable error when parsing config file - */ - onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; - } /** * Host to create watch with config file */ @@ -4212,6 +4247,26 @@ declare namespace ts { } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; + type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined; /** * Read tsconfig.json file * @param fileName The path to the config file @@ -4385,6 +4440,7 @@ declare namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; @@ -4462,6 +4518,7 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -4486,9 +4543,10 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; + type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; interface GetCompletionsAtPositionOptions extends UserPreferences { /** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */ - triggerCharacter?: string; + triggerCharacter?: CompletionsTriggerCharacter; /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ @@ -4892,9 +4950,14 @@ declare namespace ts { kind: OutliningSpanKind; } enum OutliningSpanKind { + /** Single or multi-line comments */ Comment = "comment", + /** Sections marked by '// #region' and '// #endregion' comments */ Region = "region", - Code = "code" + /** Declarations and expressions */ + Code = "code", + /** Contiguous blocks of import declarations */ + Imports = "imports" } enum OutputFileType { JavaScript = 0, @@ -5018,7 +5081,9 @@ declare namespace ts { /** * */ - jsxAttribute = "JSX attribute" + jsxAttribute = "JSX attribute", + /** String literal */ + string = "string" } enum ScriptElementKindModifier { none = "", diff --git a/tests/baselines/reference/asyncImportNestedYield.js b/tests/baselines/reference/asyncImportNestedYield.js index 0dd76a13ace..776caed5167 100644 --- a/tests/baselines/reference/asyncImportNestedYield.js +++ b/tests/baselines/reference/asyncImportNestedYield.js @@ -47,9 +47,10 @@ function foo() { return __asyncGenerator(this, arguments, function foo_1() { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, "foo"]; - case 1: return [4 /*yield*/, __await.apply(void 0, [Promise.resolve().then(function () { return require(_a.sent()); })])]; - case 2: + case 0: return [4 /*yield*/, __await("foo")]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: return [4 /*yield*/, __await.apply(void 0, [Promise.resolve().then(function () { return require(_a.sent()); })])]; + case 3: Promise.resolve().then(function () { return require((_a.sent())["default"]); }); return [2 /*return*/]; } diff --git a/tests/baselines/reference/declarationMapsOutFile.js.map b/tests/baselines/reference/declarationMapsOutFile.js.map index 051318f8064..0018f3f9f31 100644 --- a/tests/baselines/reference/declarationMapsOutFile.js.map +++ b/tests/baselines/reference/declarationMapsOutFile.js.map @@ -1,2 +1,2 @@ //// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":";IAAA,MAAM;QACF,OAAO,CAAC,CAAC,EAAE;YAAC,CAAC,EAAE,MAAM,CAAA;SAAC;;;QAGtB,MAAM,CAAC,IAAI;KAGd;;;ICPD,OAAO,EAAC,GAAG,EAAC,UAAY;IAExB,MAAM,CAAC,KAAY,CAAC;IAGpB,MAAM,CAAC,IAAI,CAAC;;KAAqB,CAAC;IAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":";IAAA,MAAM;QACF,OAAO,CAAC,GAAG;YAAC,CAAC,EAAE,MAAM,CAAA;SAAC;;;QAGtB,MAAM,CAAC,IAAI;KAGd;;;ICPD,OAAO,EAAC,GAAG,EAAC,UAAY;IAExB,MAAM,CAAC,KAAY,CAAC;IAGpB,MAAM,CAAC,IAAI,CAAC;;KAAqB,CAAC;IAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt b/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt index 39d83719b20..f1090c4f765 100644 --- a/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt @@ -22,20 +22,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1-> class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(3, 9) Source(2, 5) + SourceIndex(0) 2 >Emitted(3, 16) Source(2, 12) + SourceIndex(0) 3 >Emitted(3, 17) Source(2, 13) + SourceIndex(0) -4 >Emitted(3, 18) Source(2, 14) + SourceIndex(0) -5 >Emitted(3, 20) Source(2, 16) + SourceIndex(0) +4 >Emitted(3, 20) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^^^^^ diff --git a/tests/baselines/reference/declarationMapsOutFile2.js.map b/tests/baselines/reference/declarationMapsOutFile2.js.map index 22845f90fb2..579077e0679 100644 --- a/tests/baselines/reference/declarationMapsOutFile2.js.map +++ b/tests/baselines/reference/declarationMapsOutFile2.js.map @@ -1,2 +1,2 @@ //// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt b/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt index d7a829917e6..469e5ff2bd9 100644 --- a/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt @@ -18,20 +18,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1->class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(2, 5) Source(2, 5) + SourceIndex(0) 2 >Emitted(2, 12) Source(2, 12) + SourceIndex(0) 3 >Emitted(2, 13) Source(2, 13) + SourceIndex(0) -4 >Emitted(2, 14) Source(2, 14) + SourceIndex(0) -5 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) +4 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^ diff --git a/tests/baselines/reference/declarationMapsWithSourceMap.js.map b/tests/baselines/reference/declarationMapsWithSourceMap.js.map index 17ef6a6004c..9e27f440818 100644 --- a/tests/baselines/reference/declarationMapsWithSourceMap.js.map +++ b/tests/baselines/reference/declarationMapsWithSourceMap.js.map @@ -1,3 +1,3 @@ //// [bundle.js.map] {"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"}//// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt b/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt index 0f9df308ef6..86d306d00fb 100644 --- a/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt @@ -312,20 +312,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1->class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(2, 5) Source(2, 5) + SourceIndex(0) 2 >Emitted(2, 12) Source(2, 12) + SourceIndex(0) 3 >Emitted(2, 13) Source(2, 13) + SourceIndex(0) -4 >Emitted(2, 14) Source(2, 14) + SourceIndex(0) -5 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) +4 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^ diff --git a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js index 2de9f939ba1..58dbe061d18 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js @@ -95,7 +95,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar class C2 { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield; + const x = yield yield __await(void 0); }); } } @@ -115,7 +115,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar class C3 { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield 1; + const x = yield yield __await(1); }); } } @@ -175,7 +175,7 @@ var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { class C5 { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield 1; }); })()))); + const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield yield __await(1); }); })()))); }); } } diff --git a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js index a91120c4079..0387225261c 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js @@ -160,8 +160,9 @@ var C2 = /** @class */ (function () { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/]; - case 1: + case 0: return [4 /*yield*/, __await(void 0)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -218,8 +219,9 @@ var C3 = /** @class */ (function () { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -377,8 +379,9 @@ var C5 = /** @class */ (function () { switch (_a.label) { case 0: return [5 /*yield**/, __values(__asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function () { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: _a.sent(); return [2 /*return*/]; } diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js index d0bf1c902c0..1d242f4ce6b 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js @@ -61,7 +61,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar }; function f2() { return __asyncGenerator(this, arguments, function* f2_1() { - const x = yield; + const x = yield yield __await(void 0); }); } //// [F3.js] @@ -79,7 +79,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar }; function f3() { return __asyncGenerator(this, arguments, function* f3_1() { - const x = yield 1; + const x = yield yield __await(1); }); } //// [F4.js] @@ -135,7 +135,7 @@ var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { }; function f5() { return __asyncGenerator(this, arguments, function* f5_1() { - const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield 1; }); })()))); + const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield yield __await(1); }); })()))); }); } //// [F6.js] diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js index 70cfee32542..88a39d18cd0 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js @@ -121,8 +121,9 @@ function f2() { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/]; - case 1: + case 0: return [4 /*yield*/, __await(void 0)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -174,8 +175,9 @@ function f3() { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -323,8 +325,9 @@ function f5() { switch (_a.label) { case 0: return [5 /*yield**/, __values(__asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function () { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: _a.sent(); return [2 /*return*/]; } diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js index 909196952f0..9c964275ff3 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js @@ -61,7 +61,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar }; const f2 = function () { return __asyncGenerator(this, arguments, function* () { - const x = yield; + const x = yield yield __await(void 0); }); }; //// [F3.js] @@ -79,7 +79,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar }; const f3 = function () { return __asyncGenerator(this, arguments, function* () { - const x = yield 1; + const x = yield yield __await(1); }); }; //// [F4.js] @@ -135,7 +135,7 @@ var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { }; const f5 = function () { return __asyncGenerator(this, arguments, function* () { - const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield 1; }); })()))); + const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield yield __await(1); }); })()))); }); }; //// [F6.js] diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js index c9dff58c7ce..969036e96af 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js @@ -121,8 +121,9 @@ var f2 = function () { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/]; - case 1: + case 0: return [4 /*yield*/, __await(void 0)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -174,8 +175,9 @@ var f3 = function () { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -323,8 +325,9 @@ var f5 = function () { switch (_a.label) { case 0: return [5 /*yield**/, __values(__asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function () { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: _a.sent(); return [2 /*return*/]; } diff --git a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js index 99caa9d3ad3..0f64e788b29 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js @@ -78,7 +78,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar const o2 = { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield; + const x = yield yield __await(void 0); }); } }; @@ -98,7 +98,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar const o3 = { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield 1; + const x = yield yield __await(1); }); } }; @@ -158,7 +158,7 @@ var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { const o5 = { f() { return __asyncGenerator(this, arguments, function* f_1() { - const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield 1; }); })()))); + const x = yield __await(yield* __asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function* () { yield yield __await(1); }); })()))); }); } }; diff --git a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js index 76d74351923..ae217449715 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js @@ -138,8 +138,9 @@ var o2 = { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/]; - case 1: + case 0: return [4 /*yield*/, __await(void 0)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -193,8 +194,9 @@ var o3 = { var x; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: x = _a.sent(); return [2 /*return*/]; } @@ -346,8 +348,9 @@ var o5 = { switch (_a.label) { case 0: return [5 /*yield**/, __values(__asyncDelegator(__asyncValues((function () { return __asyncGenerator(this, arguments, function () { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, 1]; - case 1: + case 0: return [4 /*yield*/, __await(1)]; + case 1: return [4 /*yield*/, _a.sent()]; + case 2: _a.sent(); return [2 /*return*/]; } diff --git a/tests/baselines/reference/emitter.forAwait.es2015.js b/tests/baselines/reference/emitter.forAwait.es2015.js index 6b6bdd739fd..980dc3a8e3b 100644 --- a/tests/baselines/reference/emitter.forAwait.es2015.js +++ b/tests/baselines/reference/emitter.forAwait.es2015.js @@ -128,7 +128,7 @@ function f3() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield __await(y_1_1.value); + const x = yield yield __await(__await(y_1_1.value)); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -164,7 +164,7 @@ function f4() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - x = yield __await(y_1_1.value); + x = yield yield __await(__await(y_1_1.value)); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -235,7 +235,7 @@ function f6() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield __await(y_1_1.value); + const x = yield yield __await(__await(y_1_1.value)); continue outer; } } diff --git a/tests/baselines/reference/emitter.forAwait.es2017.js b/tests/baselines/reference/emitter.forAwait.es2017.js index 8eb21442558..9fcf6190990 100644 --- a/tests/baselines/reference/emitter.forAwait.es2017.js +++ b/tests/baselines/reference/emitter.forAwait.es2017.js @@ -108,7 +108,7 @@ function f3() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield __await(y_1_1.value); + const x = yield yield __await(__await(y_1_1.value)); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -144,7 +144,7 @@ function f4() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - x = yield __await(y_1_1.value); + x = yield yield __await(__await(y_1_1.value)); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -205,7 +205,7 @@ function f6() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield __await(y_1_1.value); + const x = yield yield __await(__await(y_1_1.value)); continue outer; } } diff --git a/tests/baselines/reference/emitter.forAwait.es5.js b/tests/baselines/reference/emitter.forAwait.es5.js index b4ea4ddcf54..277bc2b4e6b 100644 --- a/tests/baselines/reference/emitter.forAwait.es5.js +++ b/tests/baselines/reference/emitter.forAwait.es5.js @@ -252,35 +252,36 @@ function f3() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 7, 8, 13]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, __await(y_1_1.value)]; - case 3: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; + return [4 /*yield*/, __await(__await(y_1_1.value))]; + case 3: return [4 /*yield*/, _b.sent()]; + case 4: x = _b.sent(); - _b.label = 4; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + _b.label = 5; + case 5: return [3 /*break*/, 1]; + case 6: return [3 /*break*/, 13]; + case 7: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; - return [4 /*yield*/, __await(_a.call(y_1))]; + return [3 /*break*/, 13]; case 8: + _b.trys.push([8, , 11, 12]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [4 /*yield*/, __await(_a.call(y_1))]; + case 9: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 10; + case 10: return [3 /*break*/, 12]; + case 11: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 12: return [7 /*endfinally*/]; + case 13: return [2 /*return*/]; } }); }); @@ -336,35 +337,36 @@ function f4() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 7, 8, 13]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, __await(y_1_1.value)]; - case 3: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; + return [4 /*yield*/, __await(__await(y_1_1.value))]; + case 3: return [4 /*yield*/, _b.sent()]; + case 4: x = _b.sent(); - _b.label = 4; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + _b.label = 5; + case 5: return [3 /*break*/, 1]; + case 6: return [3 /*break*/, 13]; + case 7: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; - return [4 /*yield*/, __await(_a.call(y_1))]; + return [3 /*break*/, 13]; case 8: + _b.trys.push([8, , 11, 12]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [4 /*yield*/, __await(_a.call(y_1))]; + case 9: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 10; + case 10: return [3 /*break*/, 12]; + case 11: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 12: return [7 /*endfinally*/]; + case 13: return [2 /*return*/]; } }); }); @@ -502,35 +504,36 @@ function f6() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 7, 8, 13]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, __await(y_1_1.value)]; - case 3: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; + return [4 /*yield*/, __await(__await(y_1_1.value))]; + case 3: return [4 /*yield*/, _b.sent()]; + case 4: x = _b.sent(); - return [3 /*break*/, 4]; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + return [3 /*break*/, 5]; + case 5: return [3 /*break*/, 1]; + case 6: return [3 /*break*/, 13]; + case 7: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; - return [4 /*yield*/, __await(_a.call(y_1))]; + return [3 /*break*/, 13]; case 8: + _b.trys.push([8, , 11, 12]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [4 /*yield*/, __await(_a.call(y_1))]; + case 9: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 10; + case 10: return [3 /*break*/, 12]; + case 11: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 12: return [7 /*endfinally*/]; + case 13: return [2 /*return*/]; } }); }); diff --git a/tests/baselines/reference/importCallExpressionErrorInES2015.errors.txt b/tests/baselines/reference/importCallExpressionErrorInES2015.errors.txt index d47980d1312..b2000f9319e 100644 --- a/tests/baselines/reference/importCallExpressionErrorInES2015.errors.txt +++ b/tests/baselines/reference/importCallExpressionErrorInES2015.errors.txt @@ -1,6 +1,6 @@ -tests/cases/conformance/dynamicImport/1.ts(1,1): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. -tests/cases/conformance/dynamicImport/1.ts(2,10): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. -tests/cases/conformance/dynamicImport/1.ts(8,16): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +tests/cases/conformance/dynamicImport/1.ts(1,1): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. +tests/cases/conformance/dynamicImport/1.ts(2,10): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. +tests/cases/conformance/dynamicImport/1.ts(8,16): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. ==== tests/cases/conformance/dynamicImport/0.ts (0 errors) ==== @@ -9,10 +9,10 @@ tests/cases/conformance/dynamicImport/1.ts(8,16): error TS1323: Dynamic import c ==== tests/cases/conformance/dynamicImport/1.ts (3 errors) ==== import("./0"); ~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. var p1 = import("./0"); ~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. p1.then(zero => { return zero.foo(); }) @@ -20,5 +20,5 @@ tests/cases/conformance/dynamicImport/1.ts(8,16): error TS1323: Dynamic import c function foo() { const p2 = import("./0"); ~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. } \ No newline at end of file diff --git a/tests/baselines/reference/importCallExpressionNestedES2015.errors.txt b/tests/baselines/reference/importCallExpressionNestedES2015.errors.txt index 1208dd0c709..ef1a96ee913 100644 --- a/tests/baselines/reference/importCallExpressionNestedES2015.errors.txt +++ b/tests/baselines/reference/importCallExpressionNestedES2015.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/dynamicImport/index.ts(2,18): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. -tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +tests/cases/conformance/dynamicImport/index.ts(2,18): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. +tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. ==== tests/cases/conformance/dynamicImport/foo.ts (0 errors) ==== @@ -9,7 +9,7 @@ tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic impo async function foo() { return await import((await import("./foo")).default); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. ~~~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. } \ No newline at end of file diff --git a/tests/baselines/reference/importCallExpressionNestedES20152.errors.txt b/tests/baselines/reference/importCallExpressionNestedES20152.errors.txt index 1208dd0c709..ef1a96ee913 100644 --- a/tests/baselines/reference/importCallExpressionNestedES20152.errors.txt +++ b/tests/baselines/reference/importCallExpressionNestedES20152.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/dynamicImport/index.ts(2,18): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. -tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +tests/cases/conformance/dynamicImport/index.ts(2,18): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. +tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. ==== tests/cases/conformance/dynamicImport/foo.ts (0 errors) ==== @@ -9,7 +9,7 @@ tests/cases/conformance/dynamicImport/index.ts(2,32): error TS1323: Dynamic impo async function foo() { return await import((await import("./foo")).default); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. ~~~~~~~~~~~~~~~ -!!! error TS1323: Dynamic import cannot be used when targeting ECMAScript 2015 modules. +!!! error TS1323: Dynamic import is only supported when '--module' flag is 'commonjs' or 'esNext'. } \ No newline at end of file diff --git a/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js index 699c4faf921..14057a6cdab 100644 --- a/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js +++ b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js @@ -22,12 +22,13 @@ function f() { case 0: return [4 /*yield*/, tslib_1.__await(1)]; case 1: _a.sent(); - return [4 /*yield*/, 2]; - case 2: + return [4 /*yield*/, tslib_1.__await(2)]; + case 2: return [4 /*yield*/, _a.sent()]; + case 3: _a.sent(); return [5 /*yield**/, tslib_1.__values(tslib_1.__asyncDelegator(tslib_1.__asyncValues([3])))]; - case 3: return [4 /*yield*/, tslib_1.__await.apply(void 0, [_a.sent()])]; - case 4: + case 4: return [4 /*yield*/, tslib_1.__await.apply(void 0, [_a.sent()])]; + case 5: _a.sent(); return [2 /*return*/]; } diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 3d222583ffd..c23f10f3e68 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -627,6 +627,14 @@ class Unbounded { let y: {} | undefined | null = x; } } + +// Repro from #23940 + +interface I7 { + x: any; +} +type Foo7 = T; +declare function f7(type: K): Foo7; //// [keyofAndIndexedAccess.js] @@ -1368,3 +1376,8 @@ declare function fn(o: T, k: K): void; declare class Unbounded { foo(x: T[keyof T]): void; } +interface I7 { + x: any; +} +declare type Foo7 = T; +declare function f7(type: K): Foo7; diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index e88983a2d0d..5a3d94102d4 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -2235,3 +2235,26 @@ class Unbounded { } } +// Repro from #23940 + +interface I7 { +>I7 : Symbol(I7, Decl(keyofAndIndexedAccess.ts, 627, 1)) + + x: any; +>x : Symbol(I7.x, Decl(keyofAndIndexedAccess.ts, 631, 14)) +} +type Foo7 = T; +>Foo7 : Symbol(Foo7, Decl(keyofAndIndexedAccess.ts, 633, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 634, 10)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 634, 10)) + +declare function f7(type: K): Foo7; +>f7 : Symbol(f7, Decl(keyofAndIndexedAccess.ts, 634, 32)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 635, 20)) +>I7 : Symbol(I7, Decl(keyofAndIndexedAccess.ts, 627, 1)) +>type : Symbol(type, Decl(keyofAndIndexedAccess.ts, 635, 40)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 635, 20)) +>Foo7 : Symbol(Foo7, Decl(keyofAndIndexedAccess.ts, 633, 1)) +>I7 : Symbol(I7, Decl(keyofAndIndexedAccess.ts, 627, 1)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 635, 20)) + diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 0d6a350bd3c..a85b34d707b 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -2591,3 +2591,26 @@ class Unbounded { } } +// Repro from #23940 + +interface I7 { +>I7 : I7 + + x: any; +>x : any +} +type Foo7 = T; +>Foo7 : T +>T : T +>T : T + +declare function f7(type: K): Foo7; +>f7 : (type: K) => I7[K] +>K : K +>I7 : I7 +>type : K +>K : K +>Foo7 : T +>I7 : I7 +>K : K + diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts new file mode 100644 index 00000000000..289bf129119 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts @@ -0,0 +1,4 @@ +/// +declare class B { + c: C; +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js new file mode 100644 index 00000000000..e3580f23910 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js @@ -0,0 +1,6 @@ +/// +var B = /** @class */ (function () { + function B() { + } + return B; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts new file mode 100644 index 00000000000..8147620b211 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts @@ -0,0 +1,2 @@ +declare class C { +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js new file mode 100644 index 00000000000..35b4a697f30 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js @@ -0,0 +1,5 @@ +var C = /** @class */ (function () { + function C() { + } + return C; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts new file mode 100644 index 00000000000..289bf129119 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts @@ -0,0 +1,4 @@ +/// +declare class B { + c: C; +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js new file mode 100644 index 00000000000..e3580f23910 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js @@ -0,0 +1,6 @@ +/// +var B = /** @class */ (function () { + function B() { + } + return B; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts new file mode 100644 index 00000000000..8147620b211 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts @@ -0,0 +1,2 @@ +declare class C { +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js new file mode 100644 index 00000000000..35b4a697f30 --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js @@ -0,0 +1,5 @@ +var C = /** @class */ (function () { + function C() { + } + return C; +}()); diff --git a/tests/baselines/reference/recursiveResolveDeclaredMembers.symbols b/tests/baselines/reference/recursiveResolveDeclaredMembers.symbols new file mode 100644 index 00000000000..b531073f705 --- /dev/null +++ b/tests/baselines/reference/recursiveResolveDeclaredMembers.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/types.ts === +// #23025 +export interface F { +>F : Symbol(F, Decl(types.ts, 0, 0)) + + (): E; +>E : Symbol(E, Decl(other.js, 0, 4)) +} +export interface D {} +>D : Symbol(D, Decl(types.ts, 3, 1)) +>T : Symbol(T, Decl(types.ts, 4, 19)) +>F : Symbol(F, Decl(types.ts, 0, 0)) +>F : Symbol(F, Decl(types.ts, 0, 0)) + +=== tests/cases/compiler/other.js === +/** @typedef {import("./types").D} E */ +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/recursiveResolveDeclaredMembers.types b/tests/baselines/reference/recursiveResolveDeclaredMembers.types new file mode 100644 index 00000000000..9a1aac95224 --- /dev/null +++ b/tests/baselines/reference/recursiveResolveDeclaredMembers.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/types.ts === +// #23025 +export interface F { +>F : F + + (): E; +>E : D +} +export interface D {} +>D : D +>T : T +>F : F +>F : F + +=== tests/cases/compiler/other.js === +/** @typedef {import("./types").D} E */ +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/transformApi/transformsCorrectly.transformDeclarationFile.js b/tests/baselines/reference/transformApi/transformsCorrectly.transformDeclarationFile.js new file mode 100644 index 00000000000..18afd15ab56 --- /dev/null +++ b/tests/baselines/reference/transformApi/transformsCorrectly.transformDeclarationFile.js @@ -0,0 +1 @@ +declare var newName: any; diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 28ccc78540c..0b72c3fd8a7 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json index c75fe3c9dc6..4d5c6053bcc 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index 25c75a4b556..f84c7f3abf3 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 168fcc6dfe6..b65582c7841 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index e87936a34fc..86d25e2362a 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index c00426a373b..b623b1198c2 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 28ccc78540c..0b72c3fd8a7 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index a4d7d4ffc2c..ea9df560138 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index d01ba76d822..bab4947a8e5 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ diff --git a/tests/baselines/reference/unusedDestructuring.errors.txt b/tests/baselines/reference/unusedDestructuring.errors.txt new file mode 100644 index 00000000000..6f6bb82d9d0 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.errors.txt @@ -0,0 +1,35 @@ +tests/cases/compiler/unusedDestructuring.ts(3,7): error TS6198: All destructured elements are unused. +tests/cases/compiler/unusedDestructuring.ts(4,9): error TS6133: 'c' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(6,7): error TS6133: 'e' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,1): error TS6133: 'f' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,12): error TS6198: All destructured elements are unused. +tests/cases/compiler/unusedDestructuring.ts(8,24): error TS6133: 'c' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,32): error TS6133: 'e' is declared but its value is never read. + + +==== tests/cases/compiler/unusedDestructuring.ts (7 errors) ==== + export {}; + declare const o: any; + const { a, b } = o; + ~~~~~~~~ +!!! error TS6198: All destructured elements are unused. + const { c, d } = o; + ~ +!!! error TS6133: 'c' is declared but its value is never read. + d; + const { e } = o; + ~~~~~ +!!! error TS6133: 'e' is declared but its value is never read. + + function f({ a, b }, { c, d }, { e }) { + ~~~~~~~~~~ +!!! error TS6133: 'f' is declared but its value is never read. + ~~~~~~~~ +!!! error TS6198: All destructured elements are unused. + ~ +!!! error TS6133: 'c' is declared but its value is never read. + ~~~~~ +!!! error TS6133: 'e' is declared but its value is never read. + d; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedDestructuring.js b/tests/baselines/reference/unusedDestructuring.js new file mode 100644 index 00000000000..d025b226542 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.js @@ -0,0 +1,26 @@ +//// [unusedDestructuring.ts] +export {}; +declare const o: any; +const { a, b } = o; +const { c, d } = o; +d; +const { e } = o; + +function f({ a, b }, { c, d }, { e }) { + d; +} + + +//// [unusedDestructuring.js] +"use strict"; +exports.__esModule = true; +var a = o.a, b = o.b; +var c = o.c, d = o.d; +d; +var e = o.e; +function f(_a, _b, _c) { + var a = _a.a, b = _a.b; + var c = _b.c, d = _b.d; + var e = _c.e; + d; +} diff --git a/tests/baselines/reference/unusedDestructuring.symbols b/tests/baselines/reference/unusedDestructuring.symbols new file mode 100644 index 00000000000..0490774919b --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unusedDestructuring.ts === +export {}; +declare const o: any; +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +const { a, b } = o; +>a : Symbol(a, Decl(unusedDestructuring.ts, 2, 7)) +>b : Symbol(b, Decl(unusedDestructuring.ts, 2, 10)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +const { c, d } = o; +>c : Symbol(c, Decl(unusedDestructuring.ts, 3, 7)) +>d : Symbol(d, Decl(unusedDestructuring.ts, 3, 10)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +d; +>d : Symbol(d, Decl(unusedDestructuring.ts, 3, 10)) + +const { e } = o; +>e : Symbol(e, Decl(unusedDestructuring.ts, 5, 7)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +function f({ a, b }, { c, d }, { e }) { +>f : Symbol(f, Decl(unusedDestructuring.ts, 5, 16)) +>a : Symbol(a, Decl(unusedDestructuring.ts, 7, 12)) +>b : Symbol(b, Decl(unusedDestructuring.ts, 7, 15)) +>c : Symbol(c, Decl(unusedDestructuring.ts, 7, 22)) +>d : Symbol(d, Decl(unusedDestructuring.ts, 7, 25)) +>e : Symbol(e, Decl(unusedDestructuring.ts, 7, 32)) + + d; +>d : Symbol(d, Decl(unusedDestructuring.ts, 7, 25)) +} + diff --git a/tests/baselines/reference/unusedDestructuring.types b/tests/baselines/reference/unusedDestructuring.types new file mode 100644 index 00000000000..605f08c3bbd --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unusedDestructuring.ts === +export {}; +declare const o: any; +>o : any + +const { a, b } = o; +>a : any +>b : any +>o : any + +const { c, d } = o; +>c : any +>d : any +>o : any + +d; +>d : any + +const { e } = o; +>e : any +>o : any + +function f({ a, b }, { c, d }, { e }) { +>f : ({ a, b }: { a: any; b: any; }, { c, d }: { c: any; d: any; }, { e }: { e: any; }) => void +>a : any +>b : any +>c : any +>d : any +>e : any + + d; +>d : any +} + diff --git a/tests/baselines/reference/unusedDestructuringParameters.errors.txt b/tests/baselines/reference/unusedDestructuringParameters.errors.txt index 24a7b3838cd..a9423145cbe 100644 --- a/tests/baselines/reference/unusedDestructuringParameters.errors.txt +++ b/tests/baselines/reference/unusedDestructuringParameters.errors.txt @@ -1,5 +1,5 @@ tests/cases/compiler/unusedDestructuringParameters.ts(1,13): error TS6133: 'a' is declared but its value is never read. -tests/cases/compiler/unusedDestructuringParameters.ts(3,14): error TS6133: 'a' is declared but its value is never read. +tests/cases/compiler/unusedDestructuringParameters.ts(3,13): error TS6133: 'a' is declared but its value is never read. ==== tests/cases/compiler/unusedDestructuringParameters.ts (2 errors) ==== @@ -8,7 +8,7 @@ tests/cases/compiler/unusedDestructuringParameters.ts(3,14): error TS6133: 'a' i !!! error TS6133: 'a' is declared but its value is never read. f([1]); const f2 = ({a}) => { }; - ~ + ~~~ !!! error TS6133: 'a' is declared but its value is never read. f2({ a: 10 }); const f3 = ([_]) => { }; diff --git a/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt b/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt index e590de2989c..c3f89f32488 100644 --- a/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt @@ -1,5 +1,5 @@ -tests/cases/compiler/unusedLocalsAndObjectSpread.ts(20,15): error TS6133: 'bar' is declared but its value is never read. -tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread.ts(20,18): error TS6133: 'bar' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,21): error TS6133: 'bar' is declared but its value is never read. ==== tests/cases/compiler/unusedLocalsAndObjectSpread.ts (2 errors) ==== @@ -23,7 +23,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' const foo = { a: 1, b: 2 }; // 'a' is declared but never used const {a, ...bar} = foo; // bar should be unused - ~~~~~~ + ~~~ !!! error TS6133: 'bar' is declared but its value is never read. //console.log(bar); } @@ -32,7 +32,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' const foo = { a: 1, b: 2 }; // '_' is declared but never used const {a: _, ...bar} = foo; // bar should be unused - ~~~~~~ + ~~~ !!! error TS6133: 'bar' is declared but its value is never read. //console.log(bar); } diff --git a/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt b/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt index b980b86a61d..100f86468ad 100644 --- a/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt @@ -1,6 +1,6 @@ -tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(5,3): error TS6133: 'rest' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(5,6): error TS6133: 'rest' is declared but its value is never read. tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(8,1): error TS6133: 'foo' is declared but its value is never read. -tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,12): error TS6133: 'rest' is declared but its value is never read. ==== tests/cases/compiler/unusedLocalsAndObjectSpread2.ts (3 errors) ==== @@ -9,7 +9,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' children, // here! active: _a, // here! ...rest - ~~~~~~~ + ~~~~ !!! error TS6133: 'rest' is declared but its value is never read. } = props; @@ -20,7 +20,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' children, active: _a, ...rest - ~~~~~~~ + ~~~~ !!! error TS6133: 'rest' is declared but its value is never read. } = props; } diff --git a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt index 841e8ec95fd..60213c2227d 100644 --- a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt +++ b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt @@ -2,11 +2,12 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(1,12): error TS6133: 'a' tests/cases/compiler/unusedParametersWithUnderscore.ts(1,19): error TS6133: 'c' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(1,27): error TS6133: 'd' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(1,29): error TS6133: 'e___' is declared but its value is never read. +tests/cases/compiler/unusedParametersWithUnderscore.ts(5,13): error TS6198: All destructured elements are unused. tests/cases/compiler/unusedParametersWithUnderscore.ts(11,16): error TS6133: 'arg' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(17,13): error TS6133: 'arg' is declared but its value is never read. -==== tests/cases/compiler/unusedParametersWithUnderscore.ts (6 errors) ==== +==== tests/cases/compiler/unusedParametersWithUnderscore.ts (7 errors) ==== function f(a, _b, c, ___, d,e___, _f) { ~ !!! error TS6133: 'a' is declared but its value is never read. @@ -20,6 +21,8 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(17,13): error TS6133: 'ar function f2({_a, __b}) { + ~~~~~~~~~ +!!! error TS6198: All destructured elements are unused. } function f3([_a, ,__b]) { diff --git a/tests/cases/compiler/recursiveResolveDeclaredMembers.ts b/tests/cases/compiler/recursiveResolveDeclaredMembers.ts new file mode 100644 index 00000000000..39e5f05849b --- /dev/null +++ b/tests/cases/compiler/recursiveResolveDeclaredMembers.ts @@ -0,0 +1,12 @@ +// #23025 +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @Filename: types.ts +export interface F { + (): E; +} +export interface D {} + +// @Filename: other.js +/** @typedef {import("./types").D} E */ diff --git a/tests/cases/compiler/unusedDestructuring.ts b/tests/cases/compiler/unusedDestructuring.ts new file mode 100644 index 00000000000..9e2bb443ade --- /dev/null +++ b/tests/cases/compiler/unusedDestructuring.ts @@ -0,0 +1,13 @@ +// @noUnusedLocals: true +// @noUnusedParameters: true + +export {}; +declare const o: any; +const { a, b } = o; +const { c, d } = o; +d; +const { e } = o; + +function f({ a, b }, { c, d }, { e }) { + d; +} diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 83687e8c872..07aa3c1994f 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -629,3 +629,11 @@ class Unbounded { let y: {} | undefined | null = x; } } + +// Repro from #23940 + +interface I7 { + x: any; +} +type Foo7 = T; +declare function f7(type: K): Foo7; diff --git a/tests/cases/fourslash/codeFixAddMissingMember.ts b/tests/cases/fourslash/codeFixAddMissingMember.ts index c507d8b80c9..f5bdf567b20 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember.ts @@ -11,7 +11,6 @@ verify.codeFix({ index: 0, newFileContent: `class C { foo: number; - method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember2.ts b/tests/cases/fourslash/codeFixAddMissingMember2.ts index b19632dd104..a4b7ffb3bfe 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember2.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember2.ts @@ -11,7 +11,6 @@ verify.codeFix({ index: 1, newFileContent: `class C { [x: string]: number; - method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember3.ts b/tests/cases/fourslash/codeFixAddMissingMember3.ts index 88ea2efef66..82512406985 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember3.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember3.ts @@ -11,7 +11,6 @@ verify.codeFix({ index: 0, newFileContent: `class C { static foo: number; - static method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all.ts b/tests/cases/fourslash/codeFixAddMissingMember_all.ts index 0e7abb79cc2..5a2f8606f2d 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all.ts @@ -17,7 +17,6 @@ verify.codeFixAll({ y(): any { throw new Error("Method not implemented."); } - method() { this.x = 0; this.y(); diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts index 48455943af5..5e5a2895fdc 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts @@ -21,7 +21,6 @@ verify.codeFixAll({ y() { throw new Error("Method not implemented."); } - constructor() { this.x = undefined; } diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index 84b0ebd4414..60693a22fca 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -17,7 +17,6 @@ verify.codeFix({ static m1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - static foo0() { this.m1(1,2,3); A.m2(1,2); @@ -35,11 +34,9 @@ verify.codeFix({ static m2(arg0: any, arg1: any): any { throw new Error("Method not implemented."); } - static m1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - static foo0() { this.m1(1,2,3); A.m2(1,2); @@ -55,15 +52,12 @@ verify.codeFix({ newFileContent: `class A { static prop1: number; - static m2(arg0: any, arg1: any): any { throw new Error("Method not implemented."); } - static m1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - static foo0() { this.m1(1,2,3); A.m2(1,2); @@ -80,15 +74,12 @@ verify.codeFix({ `class A { static prop1: number; static prop2: string; - static m2(arg0: any, arg1: any): any { throw new Error("Method not implemented."); } - static m1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - static foo0() { this.m1(1,2,3); A.m2(1,2); diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts index b85e5fbfdfa..41e27dcf207 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -18,7 +18,6 @@ verify.codeFix({ foo1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - constructor() { this.foo1(1,2,3); // 7 type args @@ -37,11 +36,9 @@ verify.codeFix({ foo2(): any { throw new Error("Method not implemented."); } - foo1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - constructor() { this.foo1(1,2,3); // 7 type args @@ -60,15 +57,12 @@ verify.codeFix({ foo3(): any { throw new Error("Method not implemented."); } - foo2(): any { throw new Error("Method not implemented."); } - foo1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } - constructor() { this.foo1(1,2,3); // 7 type args diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts new file mode 100644 index 00000000000..4f3f9a3e61e --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////export {}; +////const { x, y } = o; + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`export {}; +`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts new file mode 100644 index 00000000000..af27091788d --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts @@ -0,0 +1,22 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////const { x, y } = o; +////const { a, b } = o; +////a; +////export function f({ x, y }, { a, b }) { +//// a; +////} + +verify.codeFixAll({ + fixId: "unusedIdentifier_delete", + fixAllDescription: "Delete all unused declarations", + newFileContent: +`const { a } = o; +a; +export function f({ a }) { + a; +}`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts new file mode 100644 index 00000000000..27103357d3d --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts @@ -0,0 +1,12 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////for (const { x } of o) {} + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`for (const {} of o) {}`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts new file mode 100644 index 00000000000..73fca113928 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////export {}; +////const { x: { a, b } } = o; + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`export {}; +const { } = o;`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_partlyUnused.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_partlyUnused.ts new file mode 100644 index 00000000000..6ed5973d400 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_partlyUnused.ts @@ -0,0 +1,75 @@ +/// + +// @noUnusedLocals: true + +////{ +//// const { x, y } = o; +//// x; +////} +////{ +//// const { x, y } = o; +//// y; +////} +////{ +//// const { x, y, z } = o; +//// y; +////} +////{ +//// const { x, y, z } = o; +//// x; z; +////} +////{ +//// const [x, y] = o; +//// x; +////} +////{ +//// const [x, y] = o; +//// y; +////} +////{ +//// const [x, y, z] = o; +//// y; +////} +////{ +//// const [x, y, z] = o; +//// x; z; +////} + + +verify.codeFixAll({ + fixId: "unusedIdentifier_delete", + fixAllDescription: "Delete all unused declarations", + newFileContent: +`{ + const { x } = o; + x; +} +{ + const { y } = o; + y; +} +{ + const { y } = o; + y; +} +{ + const { x, z } = o; + x; z; +} +{ + const [x,] = o; + x; +} +{ + const [, y] = o; + y; +} +{ + const [, y,] = o; + y; +} +{ + const [x,, z] = o; + x; z; +}`, +}); diff --git a/tests/cases/fourslash/completionForStringLiteral_details.ts b/tests/cases/fourslash/completionForStringLiteral_details.ts index 46832bc9025..059770023c9 100644 --- a/tests/cases/fourslash/completionForStringLiteral_details.ts +++ b/tests/cases/fourslash/completionForStringLiteral_details.ts @@ -21,7 +21,7 @@ goTo.marker("path"); verify.completionListContains("other", "other", "", "script"); goTo.marker("type"); -verify.completionListContains("a", "a", "", "type"); +verify.completionListContains("a", "a", "", "string"); goTo.marker("prop"); verify.completionListContains("x", "(property) I.x: number", "Prop doc ", "property"); diff --git a/tests/cases/fourslash/completionsPathsJsonModule.ts b/tests/cases/fourslash/completionsPathsJsonModule.ts new file mode 100644 index 00000000000..6d1a621d69a --- /dev/null +++ b/tests/cases/fourslash/completionsPathsJsonModule.ts @@ -0,0 +1,12 @@ +/// + +// @moduleResolution: node +// @resolveJsonModule: true + +// @Filename: /project/node_modules/test.json +////not read + +// @Filename: /project/index.ts +////import { } from "/**/"; + +verify.completionsAt("", ["test.json"], { isNewIdentifierLocation: true }); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsPathsJsonModuleWithAmd.ts b/tests/cases/fourslash/completionsPathsJsonModuleWithAmd.ts new file mode 100644 index 00000000000..8a2388655af --- /dev/null +++ b/tests/cases/fourslash/completionsPathsJsonModuleWithAmd.ts @@ -0,0 +1,12 @@ +/// + +// @module: amd +// @resolveJsonModule: true + +// @Filename: /project/test.json +////not read + +// @Filename: /project/index.ts +////import { } from ".//**/"; + +verify.completionsAt("", [], { isNewIdentifierLocation: true }); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsPathsJsonModuleWithoutResolveJsonModule.ts b/tests/cases/fourslash/completionsPathsJsonModuleWithoutResolveJsonModule.ts new file mode 100644 index 00000000000..432fc6e858b --- /dev/null +++ b/tests/cases/fourslash/completionsPathsJsonModuleWithoutResolveJsonModule.ts @@ -0,0 +1,11 @@ +/// + +// @moduleResolution: node + +// @Filename: /project/test.json +////not read + +// @Filename: /project/index.ts +////import { } from ".//**/"; + +verify.completionsAt("", [], { isNewIdentifierLocation: true }); diff --git a/tests/cases/fourslash/completionsPathsRelativeJsonModule.ts b/tests/cases/fourslash/completionsPathsRelativeJsonModule.ts new file mode 100644 index 00000000000..93a6ac6e4e2 --- /dev/null +++ b/tests/cases/fourslash/completionsPathsRelativeJsonModule.ts @@ -0,0 +1,12 @@ +/// + +// @moduleResolution: node +// @resolveJsonModule: true + +// @Filename: /project/test.json +////not read + +// @Filename: /project/index.ts +////import { } from ".//**/"; + +verify.completionsAt("", ["test.json"], { isNewIdentifierLocation: true }); \ No newline at end of file diff --git a/tests/cases/fourslash/esModuleInteropFindAllReferences.ts b/tests/cases/fourslash/esModuleInteropFindAllReferences.ts index 5ec348bb120..4e493e58c24 100644 --- a/tests/cases/fourslash/esModuleInteropFindAllReferences.ts +++ b/tests/cases/fourslash/esModuleInteropFindAllReferences.ts @@ -4,11 +4,11 @@ // @Filename: /abc.d.ts ////declare module "a" { -//// export const [|x|]: number; +//// export const [|{| "isWriteAccess": true, "isDefinition": true |}x|]: number; ////} // @Filename: /b.ts ////import a from "a"; ////a.[|x|]; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("const x: number"); diff --git a/tests/cases/fourslash/esModuleInteropFindAllReferences2.ts b/tests/cases/fourslash/esModuleInteropFindAllReferences2.ts index 55fc6cc89e4..e55129a357c 100644 --- a/tests/cases/fourslash/esModuleInteropFindAllReferences2.ts +++ b/tests/cases/fourslash/esModuleInteropFindAllReferences2.ts @@ -6,10 +6,10 @@ // @Filename: /a.d.ts ////export as namespace abc; -////export const [|x|]: number; +////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|]: number; // @Filename: /b.ts ////import a from "./a"; ////a.[|x|]; -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup('const x: number'); diff --git a/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts b/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts index 2a0b6a57622..61dfb1c33bc 100644 --- a/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts +++ b/tests/cases/fourslash/findAllReferPropertyAccessExpressionHeritageClause.ts @@ -2,12 +2,9 @@ //// class B {} //// function foo() { -//// return {[|B|]: B}; +//// return {[|{| "isWriteAccess": true, "isDefinition": true |}B|]: B}; //// } //// class C extends (foo()).[|B|] {} //// class C1 extends foo().[|B|] {} -const ranges = test.ranges(); -for (const range of ranges) { - verify.referencesOf(range, ranges); -} \ No newline at end of file +verify.singleReferenceGroup("(property) B: typeof B"); diff --git a/tests/cases/fourslash/findAllReferencesDynamicImport1.ts b/tests/cases/fourslash/findAllReferencesDynamicImport1.ts index ad0f261388d..c2b6f359ba6 100644 --- a/tests/cases/fourslash/findAllReferencesDynamicImport1.ts +++ b/tests/cases/fourslash/findAllReferencesDynamicImport1.ts @@ -6,4 +6,4 @@ //// import("[|./foo|]") //// var x = import("[|./foo|]") -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup('module "/tests/cases/fourslash/foo"'); diff --git a/tests/cases/fourslash/findAllReferencesDynamicImport2.ts b/tests/cases/fourslash/findAllReferencesDynamicImport2.ts index 558e2fed7ef..6644a55ec1b 100644 --- a/tests/cases/fourslash/findAllReferencesDynamicImport2.ts +++ b/tests/cases/fourslash/findAllReferencesDynamicImport2.ts @@ -1,12 +1,12 @@ /// // @Filename: foo.ts -//// export function [|bar|]() { return "bar"; } +//// export function [|{| "isWriteAccess": true, "isDefinition": true |}bar|]() { return "bar"; } //// var x = import("./foo"); //// x.then(foo => { -//// foo.[|bar|](); +//// foo.[|bar|](); //// }) -verify.rangesReferenceEachOther(); -verify.rangesAreRenameLocations(); \ No newline at end of file +verify.singleReferenceGroup("function bar(): string"); +verify.rangesAreRenameLocations(); diff --git a/tests/cases/fourslash/findAllReferencesJsDocTypeLiteral.ts b/tests/cases/fourslash/findAllReferencesJsDocTypeLiteral.ts index 5234faf9d64..48befe13442 100644 --- a/tests/cases/fourslash/findAllReferencesJsDocTypeLiteral.ts +++ b/tests/cases/fourslash/findAllReferencesJsDocTypeLiteral.ts @@ -8,9 +8,9 @@ //// * @param {string} o.x - a thing, its ok //// * @param {number} o.y - another thing //// * @param {Object} o.nested - very nested -//// * @param {boolean} o.nested.[|great|] - much greatness +//// * @param {boolean} o.nested.[|{| "isWriteAccess": true, "isDefinition": true |}great|] - much greatness //// * @param {number} o.nested.times - twice? probably!?? //// */ //// function f(o) { return o.nested.[|great|]; } -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup("(property) great: boolean"); diff --git a/tests/cases/fourslash/findAllRefsBadImport.ts b/tests/cases/fourslash/findAllRefsBadImport.ts index 89a81d81f79..e04b72df384 100644 --- a/tests/cases/fourslash/findAllRefsBadImport.ts +++ b/tests/cases/fourslash/findAllRefsBadImport.ts @@ -1,7 +1,6 @@ /// -////import { [|ab|] as [|cd|] } from "doesNotExist"; +////import { [|ab|] as [|{| "isWriteAccess": true, "isDefinition": true |}cd|] } from "doesNotExist"; const [r0, r1] = test.ranges(); -verify.referencesOf(r0, [r1]); -verify.referencesOf(r1, [r1]); +verify.referenceGroups([r0, r1], [{ definition: "import cd", ranges: [r1] }]); diff --git a/tests/cases/fourslash/findAllRefsImportType.ts b/tests/cases/fourslash/findAllRefsImportType.ts index dda48580b66..9f1fa09436e 100644 --- a/tests/cases/fourslash/findAllRefsImportType.ts +++ b/tests/cases/fourslash/findAllRefsImportType.ts @@ -9,4 +9,4 @@ // @Filename: /b.js ////type T = import("./a").[|N|]; -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup("type N = number"); diff --git a/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts b/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts index 4c672c69548..7267a816eeb 100644 --- a/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts +++ b/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts @@ -1,11 +1,13 @@ /// +// @Filename: /b.ts ////type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number; ////export = [|T|]; -////const x: [|import("./b")|] = 0; +// @Filename: /a.ts +////const x: import("[|./b|]") = 0; -// TODO: GH#23879 should just verify.rangesReferenceEachOther(); +// TODO: GH#23879 Should be `verify.singleReferenceGroup("type T = number") const [r0, r1, r2] = test.ranges(); verify.referenceGroups([r0, r1], [{ definition: "type T = number", ranges: [r0, r1] }]); -verify.referenceGroups(r2, undefined); +verify.referenceGroups(r2, [{ definition: 'module "/b"', ranges: [r2] }]); diff --git a/tests/cases/fourslash/findAllRefsThisKeyword.ts b/tests/cases/fourslash/findAllRefsThisKeyword.ts index 61dd687fd64..975b4b37ac2 100644 --- a/tests/cases/fourslash/findAllRefsThisKeyword.ts +++ b/tests/cases/fourslash/findAllRefsThisKeyword.ts @@ -25,7 +25,7 @@ ////x.[|this|]; const [global, f0, f1, g0, g1, x, y, constructor, method, propDef, propUse] = test.ranges(); -verify.referencesOf(global, [global]); +verify.singleReferenceGroup("this", [global]); verify.referenceGroups(f0, [{ definition: "(parameter) this: any", ranges: [f0, f1] }]); verify.referenceGroups(f1, [{ definition: "this: any", ranges: [f0, f1] }]); verify.referenceGroups(g0, [{ definition: "(parameter) this: any", ranges: [g0, g1] }]); diff --git a/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts b/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts index a4bab7f876c..d94f2993d1d 100644 --- a/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts +++ b/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts @@ -12,4 +12,4 @@ //// // different 'this' //// function f(this) { return this; } -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup("this"); diff --git a/tests/cases/fourslash/findAllRefsTypedef.ts b/tests/cases/fourslash/findAllRefsTypedef.ts index c7e2527405d..ecd690c10e5 100644 --- a/tests/cases/fourslash/findAllRefsTypedef.ts +++ b/tests/cases/fourslash/findAllRefsTypedef.ts @@ -5,11 +5,11 @@ // @Filename: /a.js /////** //// * @typedef I {Object} -//// * @prop [|p|] {number} +//// * @prop [|{| "isWriteAccess": true, "isDefinition": true |}p|] {number} //// */ //// /////** @type {I} */ ////let x; ////x.[|p|]; -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup("(property) p: number"); diff --git a/tests/cases/fourslash/findAllRefsTypedef_importType.ts b/tests/cases/fourslash/findAllRefsTypedef_importType.ts index a9104fb398e..ddd0cb7c966 100644 --- a/tests/cases/fourslash/findAllRefsTypedef_importType.ts +++ b/tests/cases/fourslash/findAllRefsTypedef_importType.ts @@ -11,4 +11,4 @@ /////** @type {import('./a').[|Foo|]} */ ////const x = 0; -verify.rangesReferenceEachOther(); +verify.singleReferenceGroup("type Foo = number"); diff --git a/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts b/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts index 21cc1afebb0..4bb5cb27a17 100644 --- a/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts +++ b/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts @@ -9,5 +9,5 @@ ////const x: typeof import("./a").[|T|] = 0; const [r0, r1, r2, r3] = test.ranges(); -verify.rangesReferenceEachOther([r0, r2]); -verify.rangesReferenceEachOther([r1, r3]); +verify.singleReferenceGroup("type T = 0\nconst T: 0", [r0, r2]); +verify.singleReferenceGroup("type T = 0\nconst T: 0", [r1, r3]); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index cf554831434..8a7aae00c24 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -253,12 +253,6 @@ declare namespace FourSlashInterface { noReferences(markerNameOrRange?: string | Range): void; symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void; typeOfSymbolAtLocation(range: Range, symbol: any, expected: string): void; - /** - * @deprecated, prefer 'referenceGroups' - * Like `referencesAre`, but goes to `start` first. - * `start` should be included in `references`. - */ - referencesOf(start: Range, references: Range[]): void; /** * For each of starts, asserts the ranges that are referenced from there. * This uses the 'findReferences' command instead of 'getReferencesAtPosition', so references are grouped by their definition. @@ -268,11 +262,6 @@ declare namespace FourSlashInterface { rangesAreOccurrences(isWriteAccess?: boolean): void; rangesWithSameTextAreRenameLocations(): void; rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }); - /** - * Performs `referencesOf` for every range on the whole set. - * If `ranges` is omitted, this is `test.ranges()`. - */ - rangesReferenceEachOther(ranges?: Range[]): void; findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; currentParameterHelpArgumentNameIs(name: string): void; currentParameterSpanIs(parameter: string): void; diff --git a/tests/cases/fourslash/getOccurrencesAsyncAwait.ts b/tests/cases/fourslash/getOccurrencesAsyncAwait.ts new file mode 100644 index 00000000000..f337e755096 --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesAsyncAwait.ts @@ -0,0 +1,27 @@ +/// + +////[|async|] function f() { +//// [|await|] 100; +//// [|a/**/wait|] [|await|] 200; +////class Foo { +//// async memberFunction() { +//// await 1; +//// } +////} +//// return [|await|] async function () { +//// await 300; +//// } +////} +////async function g() { +//// await 300; +//// async function f() { +//// await 400; +//// } +////} + +verify.rangesAreOccurrences(false); + +goTo.marker(); +for (const range of test.ranges()) { + verify.occurrencesAtPositionContains(range, false); +} diff --git a/tests/cases/fourslash/getOccurrencesAsyncAwait2.ts b/tests/cases/fourslash/getOccurrencesAsyncAwait2.ts new file mode 100644 index 00000000000..530f26bc18d --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesAsyncAwait2.ts @@ -0,0 +1,16 @@ +/// + +////[|a/**/sync|] function f() { +//// [|await|] 100; +//// [|await|] [|await|] 200; +//// return [|await|] async function () { +//// await 300; +//// } +////} + +verify.rangesAreOccurrences(false); + +goTo.marker(); +for (const range of test.ranges()) { + verify.occurrencesAtPositionContains(range, false); +} diff --git a/tests/cases/fourslash/getOccurrencesAsyncAwait3.ts b/tests/cases/fourslash/getOccurrencesAsyncAwait3.ts new file mode 100644 index 00000000000..972b6b8b07d --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesAsyncAwait3.ts @@ -0,0 +1,11 @@ +/// + +// Not valid TS ('await' expression is only allowed within an async function.) + +////a/**/wait 100; +////async function f() { +//// await 300; +////} + +goTo.marker(); +verify.occurrencesAtPositionCount(0); \ No newline at end of file diff --git a/tests/cases/fourslash/getOutliningSpansForImports.ts b/tests/cases/fourslash/getOutliningSpansForImports.ts new file mode 100644 index 00000000000..703c4f5891c --- /dev/null +++ b/tests/cases/fourslash/getOutliningSpansForImports.ts @@ -0,0 +1,20 @@ +/// + + +////[|import * as ns from "mod"; +//// +////import d from "mod"; +////import { a, b, c } from "mod"; +//// +////import r = require("mod");|] +//// +////// statement +////var x = 0; +//// +////// another set of imports +////[|import * as ns from "mod"; +////import d from "mod"; +////import { a, b, c } from "mod"; +////import r = require("mod");|] + +verify.outliningSpansInCurrentFile(test.ranges(), "imports"); diff --git a/tests/cases/fourslash/incrementalParsingWithJsDoc.ts b/tests/cases/fourslash/incrementalParsingWithJsDoc.ts index cd709530f9c..324cc0608ab 100644 --- a/tests/cases/fourslash/incrementalParsingWithJsDoc.ts +++ b/tests/cases/fourslash/incrementalParsingWithJsDoc.ts @@ -1,8 +1,8 @@ /// -////import a from 'a/aaaaaaa/aaaaaaa/aaaaaa/aaaaaaa'; +////[|import a from 'a/aaaaaaa/aaaaaaa/aaaaaa/aaaaaaa'; /////**/import b from 'b'; -////import c from 'c'; +////import c from 'c';|] //// ////[|/** @internal */|] ////export class LanguageIdentifier[| { }|] diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_exportEqualsClass.ts b/tests/cases/fourslash/refactorConvertToEs6Module_exportEqualsClass.ts new file mode 100644 index 00000000000..2abff8c95df --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToEs6Module_exportEqualsClass.ts @@ -0,0 +1,23 @@ +/// + +// @allowJs: true +// @target: esnext + +// @Filename: /a.js +////const b = require("./b"); +////module.exports = class C { +//// m() { +//// b.x; +//// } +////}; + +verify.codeFix({ + description: "Convert to ES6 module", + newFileContent: +`import { x } from "./b"; +export default class C { + m() { + x; + } +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_export_moduleDotExports.ts b/tests/cases/fourslash/refactorConvertToEs6Module_export_moduleDotExports.ts index 327f7b480a9..e88e3e2f173 100644 --- a/tests/cases/fourslash/refactorConvertToEs6Module_export_moduleDotExports.ts +++ b/tests/cases/fourslash/refactorConvertToEs6Module_export_moduleDotExports.ts @@ -15,11 +15,9 @@ verify.codeFix({ description: "Convert to ES6 module", newFileContent: -`export default function() { } -export default function f() { } -export default class { -} -export default class C { -} +`export default function() {} +export default function f() {} +export default class {} +export default class C {} export default 0;`, }); diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess16.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess16.ts index 03cadfbceec..5206eada35d 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess16.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess16.ts @@ -10,14 +10,12 @@ edit.applyRefactor({ actionName: "Generate 'get' and 'set' accessors", actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { - private /*RENAME*/_a: string; public get a(): string { return this._a; } public set a(value: string) { this._a = value; } - - constructor() { } + constructor(private /*RENAME*/_a: string) { } }`, }); diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess17.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess17.ts index a04932fe68d..71e058b7c83 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess17.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess17.ts @@ -10,14 +10,12 @@ edit.applyRefactor({ actionName: "Generate 'get' and 'set' accessors", actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { - private /*RENAME*/_a: string; protected get a(): string { return this._a; } protected set a(value: string) { this._a = value; } - - constructor() { } + constructor(private /*RENAME*/_a: string) { } }`, }); diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess18.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess18.ts index a48361288e0..f1203730b3c 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess18.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess18.ts @@ -10,14 +10,12 @@ edit.applyRefactor({ actionName: "Generate 'get' and 'set' accessors", actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { - private /*RENAME*/_a: string; public get a(): string { return this._a; } public set a(value: string) { this._a = value; } - - constructor() { } + constructor(private /*RENAME*/_a: string) { } }`, }); diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess22.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess22.ts index d1002c2e809..ceec2721709 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess22.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess22.ts @@ -11,16 +11,14 @@ edit.applyRefactor({ actionName: "Generate 'get' and 'set' accessors", actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { - private /*RENAME*/_a: string; public get a(): string { return this._a; } public set a(value: string) { this._a = value; } - public a_1: number; - constructor() { } + constructor(private /*RENAME*/_a: string) { } }`, }); diff --git a/tests/cases/fourslash/server/references01.ts b/tests/cases/fourslash/server/references01.ts index 528f79de5b4..02e0c16f9c7 100644 --- a/tests/cases/fourslash/server/references01.ts +++ b/tests/cases/fourslash/server/references01.ts @@ -2,14 +2,22 @@ // Global class reference. -// @Filename: referencesForGlobals_1.ts -////class [|globalClass|] { +// @Filename: /referencesForGlobals_1.ts +////class [|{| "isWriteAccess": true, "isDefinition": true |}globalClass|] { //// public f() { } ////} -// @Filename: referencesForGlobals_2.ts +// @Filename: /referencesForGlobals_2.ts /////// ////var c = [|globalClass|](); -// Must reverse ranges so that referencesForGlobals_2 goes first -- otherwise referencesForGlobals_1 won't pick it up. -verify.rangesReferenceEachOther(test.ranges().reverse()); +const [r0, r1] = test.ranges(); +goTo.rangeStart(r1); +verify.getReferencesForServerTest([ + { fileName: "/referencesForGlobals_1.ts", isDefinition: true, isWriteAccess: true, textSpan: toSpan(r0) }, + { fileName: "/referencesForGlobals_2.ts", isDefinition: false, isWriteAccess: false, textSpan: toSpan(r1) }, +]); + +function toSpan(r: FourSlashInterface.Range) { + return { start: r.pos, length: r.end - r.pos }; +} diff --git a/tests/cases/fourslash/server/referencesInConfiguredProject.ts b/tests/cases/fourslash/server/referencesInConfiguredProject.ts index 109b5e36e4d..87ca2da9931 100644 --- a/tests/cases/fourslash/server/referencesInConfiguredProject.ts +++ b/tests/cases/fourslash/server/referencesInConfiguredProject.ts @@ -2,15 +2,24 @@ // Global class reference. -// @Filename: referencesForGlobals_1.ts +// @Filename: /referencesForGlobals_1.ts ////class [|globalClass|] { //// public f() { } ////} -// @Filename: referencesForGlobals_2.ts +// @Filename: /referencesForGlobals_2.ts ////var c = [|globalClass|](); -// @Filename: tsconfig.json +// @Filename: /tsconfig.json ////{ "files": ["referencesForGlobals_1.ts", "referencesForGlobals_2.ts"] } -verify.rangesReferenceEachOther(); +const [r0, r1] = test.ranges(); +goTo.rangeStart(r1); +verify.getReferencesForServerTest([ + { fileName: "/referencesForGlobals_1.ts", isDefinition: true, isWriteAccess: true, textSpan: toSpan(r0) }, + { fileName: "/referencesForGlobals_2.ts", isDefinition: false, isWriteAccess: false, textSpan: toSpan(r1) }, +]); + +function toSpan(r: FourSlashInterface.Range) { + return { start: r.pos, length: r.end - r.pos }; +} diff --git a/tests/cases/fourslash/tsxFindAllReferences1.ts b/tests/cases/fourslash/tsxFindAllReferences1.ts index 9b55901ba68..550dd05606c 100644 --- a/tests/cases/fourslash/tsxFindAllReferences1.ts +++ b/tests/cases/fourslash/tsxFindAllReferences1.ts @@ -4,7 +4,7 @@ //// declare module JSX { //// interface Element { } //// interface IntrinsicElements { -//// [|div|]: { +//// [|{| "isWriteAccess": true, "isDefinition": true |}div|]: { //// name?: string; //// isOpen?: boolean; //// }; @@ -13,4 +13,7 @@ //// } //// var x = <[|div|] />; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup(`(property) JSX.IntrinsicElements.div: { + name?: string; + isOpen?: boolean; +}`); diff --git a/tests/cases/fourslash/tsxFindAllReferences10.ts b/tests/cases/fourslash/tsxFindAllReferences10.ts index a027671ec3d..04ac864e4f2 100644 --- a/tests/cases/fourslash/tsxFindAllReferences10.ts +++ b/tests/cases/fourslash/tsxFindAllReferences10.ts @@ -15,7 +15,7 @@ //// className?: string; //// } //// interface ButtonProps extends ClickableProps { -//// [|onClick|](event?: React.MouseEvent): void; +//// [|{| "isWriteAccess": true, "isDefinition": true |}onClick|](event?: React.MouseEvent): void; //// } //// interface LinkProps extends ClickableProps { //// goTo: string; @@ -25,9 +25,9 @@ //// declare function MainButton(props: ButtonProps | LinkProps): JSX.Element; //// let opt = ; //// let opt = ; -//// let opt = {}} />; -//// let opt = {}} ignore-prop />; +//// let opt = {}} />; +//// let opt = {}} ignore-prop />; //// let opt = ; //// let opt = ; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("(method) ButtonProps.onClick(event?: any): void"); diff --git a/tests/cases/fourslash/tsxFindAllReferences11.ts b/tests/cases/fourslash/tsxFindAllReferences11.ts index ef2f7722243..8c7e7a46909 100644 --- a/tests/cases/fourslash/tsxFindAllReferences11.ts +++ b/tests/cases/fourslash/tsxFindAllReferences11.ts @@ -23,7 +23,6 @@ //// declare function MainButton(buttonProps: ButtonProps): JSX.Element; //// declare function MainButton(linkProps: LinkProps): JSX.Element; //// declare function MainButton(props: ButtonProps | LinkProps): JSX.Element; -//// let opt = ; // r1 +//// let opt = ; -const [r1] = test.ranges(); -verify.referencesOf(r1, [r1]); \ No newline at end of file +verify.singleReferenceGroup("(property) wrong: true"); diff --git a/tests/cases/fourslash/tsxFindAllReferences2.ts b/tests/cases/fourslash/tsxFindAllReferences2.ts index 8522874865a..e7fda5ab308 100644 --- a/tests/cases/fourslash/tsxFindAllReferences2.ts +++ b/tests/cases/fourslash/tsxFindAllReferences2.ts @@ -5,12 +5,12 @@ //// interface Element { } //// interface IntrinsicElements { //// div: { -//// [|name|]?: string; +//// [|{| "isWriteAccess": true, "isDefinition": true |}name|]?: string; //// isOpen?: boolean; //// }; //// span: { n: string; }; //// } //// } -//// var x =
; +//// var x =
; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("(property) name?: string"); diff --git a/tests/cases/fourslash/tsxFindAllReferences3.ts b/tests/cases/fourslash/tsxFindAllReferences3.ts index 2f78cc08ba9..2682e01e7d1 100644 --- a/tests/cases/fourslash/tsxFindAllReferences3.ts +++ b/tests/cases/fourslash/tsxFindAllReferences3.ts @@ -9,11 +9,11 @@ //// } //// class MyClass { //// props: { -//// [|name|]?: string; +//// [|{| "isWriteAccess": true, "isDefinition": true |}name|]?: string; //// size?: number; //// } -//// -//// -//// var x = ; +//// +//// +//// var x = ; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("(property) name?: string"); diff --git a/tests/cases/fourslash/tsxFindAllReferences4.ts b/tests/cases/fourslash/tsxFindAllReferences4.ts index a1309bfaef9..c932ea57f36 100644 --- a/tests/cases/fourslash/tsxFindAllReferences4.ts +++ b/tests/cases/fourslash/tsxFindAllReferences4.ts @@ -7,13 +7,13 @@ //// } //// interface ElementAttributesProperty { props } //// } -//// class [|MyClass|] { +//// class [|{| "isWriteAccess": true, "isDefinition": true |}MyClass|] { //// props: { //// name?: string; //// size?: number; //// } -//// -//// +//// +//// //// var x = <[|MyClass|] name='hello'>; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("class MyClass"); diff --git a/tests/cases/fourslash/tsxFindAllReferences5.ts b/tests/cases/fourslash/tsxFindAllReferences5.ts index 018b7568df1..9358dd73dc5 100644 --- a/tests/cases/fourslash/tsxFindAllReferences5.ts +++ b/tests/cases/fourslash/tsxFindAllReferences5.ts @@ -15,11 +15,11 @@ //// propString: string //// optional?: boolean //// } -//// declare function [|Opt|](attributes: OptionPropBag): JSX.Element; +//// declare function [|{| "isWriteAccess": true, "isDefinition": true |}Opt|](attributes: OptionPropBag): JSX.Element; //// let opt = <[|Opt|] />; //// let opt1 = <[|Opt|] propx={100} propString />; //// let opt2 = <[|Opt|] propx={100} optional/>; //// let opt3 = <[|Opt|] wrong />; //// let opt4 = <[|Opt|] propx={100} propString="hi" />; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("function Opt(attributes: OptionPropBag): JSX.Element"); diff --git a/tests/cases/fourslash/tsxFindAllReferences6.ts b/tests/cases/fourslash/tsxFindAllReferences6.ts index ad689a53baf..c0e119a4e45 100644 --- a/tests/cases/fourslash/tsxFindAllReferences6.ts +++ b/tests/cases/fourslash/tsxFindAllReferences6.ts @@ -17,7 +17,6 @@ //// optional?: boolean //// } //// declare function Opt(attributes: OptionPropBag): JSX.Element; -//// let opt = ; //r1 +//// let opt = ; -const [r1] = test.ranges(); -verify.referencesOf(r1, [r1]); \ No newline at end of file +verify.singleReferenceGroup("(property) wrong: true"); diff --git a/tests/cases/fourslash/tsxFindAllReferences7.ts b/tests/cases/fourslash/tsxFindAllReferences7.ts index b1f5231a198..37ea60c7a38 100644 --- a/tests/cases/fourslash/tsxFindAllReferences7.ts +++ b/tests/cases/fourslash/tsxFindAllReferences7.ts @@ -11,14 +11,14 @@ //// interface ElementAttributesProperty { props; } //// } //// interface OptionPropBag { -//// [|propx|]: number +//// [|{| "isWriteAccess": true, "isDefinition": true |}propx|]: number //// propString: string //// optional?: boolean //// } //// declare function Opt(attributes: OptionPropBag): JSX.Element; //// let opt = ; -//// let opt1 = ; -//// let opt2 = ; +//// let opt1 = ; +//// let opt2 = ; //// let opt3 = ; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("(property) OptionPropBag.propx: number"); diff --git a/tests/cases/fourslash/tsxFindAllReferences8.ts b/tests/cases/fourslash/tsxFindAllReferences8.ts index 434e8cf3303..0c216488892 100644 --- a/tests/cases/fourslash/tsxFindAllReferences8.ts +++ b/tests/cases/fourslash/tsxFindAllReferences8.ts @@ -20,9 +20,9 @@ //// interface LinkProps extends ClickableProps { //// goTo: string; //// } -//// declare function [|MainButton|](buttonProps: ButtonProps): JSX.Element; -//// declare function [|MainButton|](linkProps: LinkProps): JSX.Element; -//// declare function [|MainButton|](props: ButtonProps | LinkProps): JSX.Element; +//// declare function [|{| "isWriteAccess": true, "isDefinition": true |}MainButton|](buttonProps: ButtonProps): JSX.Element; +//// declare function [|{| "isWriteAccess": true, "isDefinition": true |}MainButton|](linkProps: LinkProps): JSX.Element; +//// declare function [|{| "isWriteAccess": true, "isDefinition": true |}MainButton|](props: ButtonProps | LinkProps): JSX.Element; //// let opt = <[|MainButton|] />; //// let opt = <[|MainButton|] children="chidlren" />; //// let opt = <[|MainButton|] onClick={()=>{}} />; @@ -30,4 +30,4 @@ //// let opt = <[|MainButton|] goTo="goTo" />; //// let opt = <[|MainButton|] wrong />; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("function MainButton(buttonProps: ButtonProps): JSX.Element (+2 overloads)"); diff --git a/tests/cases/fourslash/tsxFindAllReferences9.ts b/tests/cases/fourslash/tsxFindAllReferences9.ts index 5ac6ad344d8..9fe02500ad9 100644 --- a/tests/cases/fourslash/tsxFindAllReferences9.ts +++ b/tests/cases/fourslash/tsxFindAllReferences9.ts @@ -18,7 +18,7 @@ //// onClick(event?: React.MouseEvent): void; //// } //// interface LinkProps extends ClickableProps { -//// [|goTo|]: string; +//// [|{| "isWriteAccess": true, "isDefinition": true |}goTo|]: string; //// } //// declare function MainButton(buttonProps: ButtonProps): JSX.Element; //// declare function MainButton(linkProps: LinkProps): JSX.Element; @@ -27,8 +27,8 @@ //// let opt = ; //// let opt = {}} />; //// let opt = {}} ignore-prop />; -//// let opt = ; -//// let opt = ; +//// let opt = ; +//// let opt = ; //// let opt = ; -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("(property) LinkProps.goTo: string"); diff --git a/tests/cases/fourslash/tsxFindAllReferencesUnionElementType1.ts b/tests/cases/fourslash/tsxFindAllReferencesUnionElementType1.ts index e7c2a1a567c..87adbc11873 100644 --- a/tests/cases/fourslash/tsxFindAllReferencesUnionElementType1.ts +++ b/tests/cases/fourslash/tsxFindAllReferencesUnionElementType1.ts @@ -18,7 +18,11 @@ //// return

World

; //// } -//// var [|SFCComp|] = SFC1 || SFC2; +//// var [|{| "isWriteAccess": true, "isDefinition": true |}SFCComp|] = SFC1 || SFC2; //// <[|SFCComp|] x={ "hi" } /> -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup(`var SFCComp: ((prop: { + x: number; +}) => JSX.Element) | ((prop: { + x: boolean; +}) => JSX.Element)`); diff --git a/tests/cases/fourslash/tsxFindAllReferencesUnionElementType2.ts b/tests/cases/fourslash/tsxFindAllReferencesUnionElementType2.ts index 3089279b2e7..2498c8bf996 100644 --- a/tests/cases/fourslash/tsxFindAllReferencesUnionElementType2.ts +++ b/tests/cases/fourslash/tsxFindAllReferencesUnionElementType2.ts @@ -17,7 +17,7 @@ //// private method() { } //// } -//// var [|RCComp|] = RC1 || RC2; +//// var [|{| "isWriteAccess": true, "isDefinition": true |}RCComp|] = RC1 || RC2; //// <[|RCComp|] /> -verify.rangesReferenceEachOther(); \ No newline at end of file +verify.singleReferenceGroup("var RCComp: typeof RC1");