diff --git a/package-lock.json b/package-lock.json index 45fd9b0cdabb8..e2750ce5624cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -325,9 +325,9 @@ } }, "@octokit/openapi-types": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.2.3.tgz", - "integrity": "sha512-V1ycxkR19jqbIl3evf2RQiMRBvTNRi+Iy9h20G5OP5dPfEF6GJ1DPlUeiZRxo2HJxRr+UA4i0H1nn4btBDPFrw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.0.tgz", + "integrity": "sha512-o00X2FCLiEeXZkm1Ab5nvPUdVOlrpediwWZkpizUJ/xtZQsJ4FiQ2RB/dJEmb0Nk+NIz7zyDePcSCu/Y/0M3Ew==", "dev": true }, "@octokit/plugin-paginate-rest": { @@ -583,9 +583,9 @@ "dev": true }, "@types/node": { - "version": "15.12.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.1.tgz", - "integrity": "sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw==", + "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", "dev": true }, "@types/node-fetch": { @@ -4807,6 +4807,12 @@ "unc-path-regex": "^0.1.2" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -5112,12 +5118,13 @@ "dev": true }, "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, "lru-queue": { @@ -5388,9 +5395,9 @@ "dev": true }, "mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-GRGG/q9bIaUkHJB9NL+KZNjDhMBHB30zW3bZW9qOiYr+QChyLjPzswaxFWkI1q6lGlSL28EQYzAi2vKWNkPx+g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -5401,20 +5408,20 @@ "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", + "glob": "7.1.7", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.20", + "nanoid": "3.1.23", "serialize-javascript": "5.0.1", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.1.0", + "workerpool": "6.1.4", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -5551,20 +5558,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -5596,9 +5589,9 @@ "dev": true }, "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -5813,9 +5806,9 @@ "optional": true }, "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", "dev": true }, "nanomatch": { @@ -6377,9 +6370,9 @@ "dev": true }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8516,9 +8509,9 @@ "dev": true }, "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.4.tgz", + "integrity": "sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g==", "dev": true }, "wrap-ansi": { diff --git a/scripts/createPlaygroundBuild.js b/scripts/createPlaygroundBuild.js index 8fbee8ce8a676..31fe5af92dd8e 100644 --- a/scripts/createPlaygroundBuild.js +++ b/scripts/createPlaygroundBuild.js @@ -1,5 +1,10 @@ +/* eslint-disable */ // @ts-check +/** Run via: + node scripts/createPlaygroundBuild.js + */ + // This script does two things: // // - Listens to changes to the built version of TypeScript (via a filewatcher on `built/local/typescriptServices.js`) @@ -20,11 +25,12 @@ const fs = require('fs'); const child_process = require('child_process'); const http = require('http'); const url = require('url'); +const nodeFetch = require("node-fetch").default function updateTSDist() { // This code is a direct port of a script from monaco-typescript // https://github.com/microsoft/monaco-typescript/blob/master/scripts/importTypescript.js - // Currently based on 778ace1 on Apr 25 2020 + // Currently based on cc8da6b on June 6 2021 const generatedNote = `// // **NOTE**: Do not edit directly! This file is generated using \`npm run import-typescript\` @@ -42,8 +48,10 @@ function updateTSDist() { } importLibs(); - const npmLsOutput = JSON.parse(child_process.execSync("npm ls typescript --depth=0 --json=true").toString()); - const typeScriptDependencyVersion = npmLsOutput.dependencies.typescript.version; + const npmLsOutput = JSON.parse( + child_process.execSync('npm ls typescript --depth=0 --json=true').toString() + ); + const typeScriptDependencyVersion = npmLsOutput.version; fs.writeFileSync( path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServicesMetadata.ts'), @@ -51,42 +59,68 @@ function updateTSDist() { export const typescriptVersion = "${typeScriptDependencyVersion}";\n` ); - var tsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')).toString(); + let tsServices = fs + .readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')) + .toString(); // Ensure we never run into the node system... // (this also removes require calls that trick webpack into shimming those modules...) - tsServices = ( - tsServices.replace(/\n ts\.sys =([^]*)\n \}\)\(\);/m, `\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE`) + tsServices = tsServices.replace( + /\n ts\.sys =([^]*)\n \}\)\(\);/m, + `\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE` ); // Eliminate more require() calls... - tsServices = tsServices.replace(/^( +)etwModule = require\(.*$/m, '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE'); - tsServices = tsServices.replace(/^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE'); + tsServices = tsServices.replace( + /^( +)etwModule = require\(.*$/m, + '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE' + ); + tsServices = tsServices.replace( + /^( +)var result = ts\.sys\.require\(.*$/m, + '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE' + ); + tsServices = tsServices.replace( + /^( +)fs = require\("fs"\);$/m, + '$1// MONACOCHANGE\n$1fs = undefined;\n$1// END MONACOCHANGE' + ); + tsServices = tsServices.replace( + /^( +)debugger;$/m, + '$1// MONACOCHANGE\n$1// debugger;\n$1// END MONACOCHANGE' + ); + tsServices = tsServices.replace( + /= require\("perf_hooks"\)/m, + '/* MONACOCHANGE */= {}/* END MONACOCHANGE */' + ); // Flag any new require calls (outside comments) so they can be corrected preemptively. // To avoid missing cases (or using an even more complex regex), temporarily remove comments // about require() and then check for lines actually calling require(). // \/[*/] matches the start of a comment (single or multi-line). // ^\s+\*[^/] matches (presumably) a later line of a multi-line comment. - const tsServicesNoCommentedRequire = tsServices.replace(/(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, ''); - const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm) + const tsServicesNoCommentedRequire = tsServices.replace( + /(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, + '' + ); + const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm); // Allow error messages to include references to require() in their strings - const runtimeRequires = linesWithRequire && linesWithRequire.filter(l => !l.includes(": diag(")) - - if (runtimeRequires && runtimeRequires.length) { - console.error('Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n'); - console.error(linesWithRequire.join('\n')); + const runtimeRequires = + linesWithRequire && + linesWithRequire.filter((l) => !l.includes(': diag(') && !l.includes('ts.DiagnosticCategory')); + + if (runtimeRequires && runtimeRequires.length && linesWithRequire) { + console.error( + 'Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n' + ); + console.error( + runtimeRequires.map((r) => `${r} (${tsServicesNoCommentedRequire.indexOf(r)})`).join('\n') + ); process.exit(1); } - // Make sure process.args don't get called in the browser, this - // should only happen in TS 2.6.2 - const beforeProcess = `ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify(process.argv));` - const afterProcess = `// MONACOCHANGE\n ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify([]));\n// END MONACOCHANGE` - tsServices = tsServices.replace(beforeProcess, afterProcess); - - var tsServices_amd = generatedNote + tsServices + + const tsServices_amd = + generatedNote + + tsServices + ` // MONACOCHANGE // Defining the entire module name because r.js has an issue and cannot bundle this file @@ -94,9 +128,14 @@ function updateTSDist() { define("vs/language/typescript/lib/typescriptServices", [], function() { return ts; }); // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices-amd.js'), tsServices_amd); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices-amd.js'), + stripSourceMaps(tsServices_amd) + ); - var tsServices_esm = generatedNote + tsServices + + const tsServices_esm = + generatedNote + + tsServices + ` // MONACOCHANGE export var createClassifier = ts.createClassifier; @@ -110,124 +149,57 @@ function updateTSDist() { export var TokenClass = ts.TokenClass; // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), tsServices_esm); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), + stripSourceMaps(tsServices_esm) + ); - var dtsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts')).toString(); - dtsServices += - ` + let dtsServices = fs + .readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts')) + .toString(); + dtsServices += ` // MONACOCHANGE export = ts; // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), generatedNote + dtsServices); - + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), + generatedNote + dtsServices + ); })(); function importLibs() { - function getFileName(name) { - return (name === '' ? 'lib.d.ts' : `lib.${name}.d.ts`); - } - function getVariableName(name) { - return (name === '' ? 'lib_dts' : `lib_${name.replace(/\./g, '_')}_dts`); - } function readLibFile(name) { - var srcPath = path.join(TYPESCRIPT_LIB_SOURCE, getFileName(name)); + const srcPath = path.join(TYPESCRIPT_LIB_SOURCE, name); return fs.readFileSync(srcPath).toString(); } - var queue = []; - var in_queue = {}; - - var enqueue = function (name) { - if (in_queue[name]) { - return; - } - in_queue[name] = true; - queue.push(name); - }; - - enqueue(''); - enqueue('es2015'); - - var result = []; - while (queue.length > 0) { - var name = queue.shift(); - var contents = readLibFile(name); - var lines = contents.split(/\r\n|\r|\n/); - - var output = ''; - var writeOutput = function (text) { - if (output.length === 0) { - output = text; - } else { - output += ` + ${text}`; - } - }; - var outputLines = []; - var flushOutputLines = function () { - writeOutput(`"${escapeText(outputLines.join('\n'))}"`); - outputLines = []; - }; - var deps = []; - for (let i = 0; i < lines.length; i++) { - let m = lines[i].match(/\/\/\/\s* 0) { - for (let i = result.length - 1; i >= 0; i--) { - if (result[i].deps.length === 0) { - // emit this node - strResult += `\nexport const ${result[i].name}: string = ${result[i].output};\n`; - - // mark dep as resolved - for (let j = 0; j < result.length; j++) { - for (let k = 0; k < result[j].deps.length; k++) { - if (result[j].deps[k] === result[i].name) { - result[j].deps.splice(k, 1); - break; - } - } - } - - // remove from result - result.splice(i, 1); - break; - } - } + ${generatedNote} + /** Contains all the lib files */ + export const libFileMap: Record = {} + `; + let strIndexResult = `/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + ${generatedNote} + /** Contains all the lib files */ + export const libFileSet: Record = {} + `; + const dtsFiles = fs.readdirSync(TYPESCRIPT_LIB_SOURCE).filter((f) => f.includes('lib.')); + while (dtsFiles.length > 0) { + const name = dtsFiles.shift(); + const output = readLibFile(name).replace(/\r\n/g, '\n'); + strLibResult += `libFileMap['${name}'] = "${escapeText(output)}";\n`; + strIndexResult += `libFileSet['${name}'] = true;\n`; } - strResult += ` - /** This is the DTS which is used when the target is ES6 or below */ - export const lib_es5_bundled_dts = lib_dts; - - /** This is the DTS which is used by default in monaco-typescript, and when the target is 2015 or above */ - export const lib_es2015_bundled_dts = lib_es2015_dts + "" + lib_dom_dts + "" + lib_webworker_importscripts_dts + "" + lib_scripthost_dts + ""; - ` - - var dstPath = path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.ts'); - fs.writeFileSync(dstPath, strResult); + fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.ts'), strLibResult); + fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.index.ts'), strIndexResult); } /** @@ -235,19 +207,23 @@ function updateTSDist() { */ function escapeText(text) { // See http://www.javascriptkit.com/jsref/escapesequence.shtml - var _backspace = '\b'.charCodeAt(0); - var _formFeed = '\f'.charCodeAt(0); - var _newLine = '\n'.charCodeAt(0); - var _nullChar = 0; - var _carriageReturn = '\r'.charCodeAt(0); - var _tab = '\t'.charCodeAt(0); - var _verticalTab = '\v'.charCodeAt(0); - var _backslash = '\\'.charCodeAt(0); - var _doubleQuote = '"'.charCodeAt(0); - - var startPos = 0, chrCode, replaceWith = null, resultPieces = []; - - for (var i = 0, len = text.length; i < len; i++) { + const _backspace = '\b'.charCodeAt(0); + const _formFeed = '\f'.charCodeAt(0); + const _newLine = '\n'.charCodeAt(0); + const _nullChar = 0; + const _carriageReturn = '\r'.charCodeAt(0); + const _tab = '\t'.charCodeAt(0); + const _verticalTab = '\v'.charCodeAt(0); + const _backslash = '\\'.charCodeAt(0); + const _doubleQuote = '"'.charCodeAt(0); + + const len = text.length; + let startPos = 0; + let chrCode; + let replaceWith = null; + let resultPieces = []; + + for (let i = 0; i < len; i++) { chrCode = text.charCodeAt(i); switch (chrCode) { case _backspace: @@ -288,7 +264,10 @@ function updateTSDist() { resultPieces.push(text.substring(startPos, len)); return resultPieces.join(''); } - + + function stripSourceMaps(str) { + return str.replace(/\/\/# sourceMappingURL[^\n]+/gm, ''); + } /// End of import } @@ -298,7 +277,18 @@ fs.watchFile(services, () =>{ updateTSDist() }) +// We need to re-direct non TSC JS requests back to a real copy of +// monaco, so grab the list of official releases from the TS CDN +// and use the latest stable release, as that is most likely the +// closest version to your dev build +let latestStable = "4.3.2" +nodeFetch('https://typescript.azureedge.net/indexes/releases.json').then(req => req.json()).then(releases => { + latestStable = releases.versions.pop() +}); + http.createServer(function (req, res) { + res.setHeader("Access-Control-Allow-Origin", "*") + const incoming = url.parse(req.url) if (incoming.path.endsWith("typescriptServices.js")) { // Use the built version @@ -308,7 +298,7 @@ http.createServer(function (req, res) { } else { // Redirect to the TS CDN res.writeHead(302, { - 'Location': `https://typescript.azureedge.net/cdn/3.9.2/monaco/${incoming.path}` + 'Location': `https://typescript.azureedge.net/cdn/${latestStable}/monaco${incoming.path}` }); } @@ -317,6 +307,7 @@ http.createServer(function (req, res) { console.log("Starting servers\n") console.log(" - [✓] file watcher: " + services) +updateTSDist() console.log(" - [✓] http server: http://localhost:5615") console.log("\n\nGet started: http://www.staging-typescript.org/play?ts=dev") diff --git a/scripts/open-cherry-pick-pr.ts b/scripts/open-cherry-pick-pr.ts index 3ea7df0a9b83d..0227a90dc2302 100644 --- a/scripts/open-cherry-pick-pr.ts +++ b/scripts/open-cherry-pick-pr.ts @@ -90,7 +90,7 @@ ${logText.trim()}`; base: process.env.TARGET_BRANCH, body: `This cherry-pick was triggered by a request on https://github.com/Microsoft/TypeScript/pull/${process.env.SOURCE_ISSUE} -Please review the diff and merge if no changes are unexpected.${produceLKG ? ` An LKG update commit is included seperately from the base change.` : ""} +Please review the diff and merge if no changes are unexpected.${produceLKG ? ` An LKG update commit is included separately from the base change.` : ""} You can view the cherry-pick log [here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary). cc ${reviewers.map(r => "@" + r).join(" ")}`, diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index d4d7327fbd7ee..c7911bbd61a3b 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -491,8 +491,6 @@ namespace ts { } } } - - return false; } /** @@ -517,7 +515,7 @@ namespace ts { /** * Iterate on referencing modules that export entities from affected file */ - function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => boolean) { + function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => void) { // If there was change in signature (dts output) for the changed file, // then only we need to handle pending file emit if (!state.exportedModulesMap || !state.changedFilesSet.has(affectedFile.resolvedPath)) { @@ -536,8 +534,8 @@ namespace ts { const currentPath = queue.pop()!; if (!seenFileNamesMap.has(currentPath)) { seenFileNamesMap.set(currentPath, true); - const result = fn(state, currentPath); - if (result && isChangedSignature(state, currentPath)) { + fn(state, currentPath); + if (isChangedSignature(state, currentPath)) { const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!; queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath)); } @@ -549,13 +547,11 @@ namespace ts { const seenFileAndExportsOfFile = new Set(); // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected - if (forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => + forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => exportedModules && exportedModules.has(affectedFile.resolvedPath) && forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn) - )) { - return; - } + ); // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) => @@ -568,8 +564,8 @@ namespace ts { /** * Iterate on files referencing referencedPath */ - function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => boolean) { - return forEachEntry(state.referencedMap!, (referencesInFile, filePath) => + function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void) { + forEachEntry(state.referencedMap!, (referencesInFile, filePath) => referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn) ); } @@ -577,38 +573,32 @@ namespace ts { /** * fn on file and iterate on anything that exports this file */ - function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean { + function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void) { if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) { - return false; + return; } - if (fn(state, filePath)) { - // If there are no more diagnostics from old cache, done - return true; - } + fn(state, filePath); Debug.assert(!!state.currentAffectedFilesExportedModulesMap); // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected - if (forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => + forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => exportedModules && exportedModules.has(filePath) && forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) - )) { - return true; - } + ); // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected - if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => + forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it exportedModules.has(filePath) && forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) - )) { - return true; - } + ); // Remove diagnostics of files that import this file (without going to exports of referencing files) - return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) => + + forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) => referencesInFile.has(filePath) && !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file fn(state, referencingFilePath) // Dont add to seen since this is not yet done with the export removal diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 79deeced20979..dc53e9a35a205 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -635,6 +635,7 @@ namespace ts { getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), getSuggestedSymbolForNonexistentModule, getSuggestionForNonexistentExport, + getSuggestedSymbolForNonexistentClassMember, getBaseConstraintOfType, getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined, resolveName(name, location, meaning, excludeGlobals) { @@ -4490,7 +4491,6 @@ namespace ts { // If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: () => false, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? { getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "", - getSourceFiles: () => host.getSourceFiles(), getCurrentDirectory: () => host.getCurrentDirectory(), getSymlinkCache: maybeBind(host, host.getSymlinkCache), useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames), @@ -27750,6 +27750,10 @@ namespace ts { } } + function getSuggestedSymbolForNonexistentClassMember(name: string, baseType: Type): Symbol | undefined { + return getSpellingSuggestionForName(name, arrayFrom(getMembersOfSymbol(baseType.symbol).values()), SymbolFlags.ClassMember); + } + function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { let props = getPropertiesOfType(containingType); if (typeof name !== "string") { @@ -27769,7 +27773,7 @@ namespace ts { : strName === "class" ? find(properties, x => symbolName(x) === "className") : undefined; return jsxSpecific ?? getSpellingSuggestionForName(strName, properties, SymbolFlags.Value); - } + } function getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined { const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType); @@ -37383,7 +37387,7 @@ namespace ts { const baseClassName = typeToString(baseWithThis); if (prop && !baseProp && hasOverride) { - const suggestion = getSpellingSuggestionForName(symbolName(declaredProp), arrayFrom(getMembersOfSymbol(baseType.symbol).values()), SymbolFlags.ClassMember); + const suggestion = getSuggestedSymbolForNonexistentClassMember(symbolName(declaredProp), baseType); suggestion ? error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1, baseClassName, symbolToString(suggestion)) : error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0, baseClassName); diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index cd43fe379afe8..cd14e3fd11b17 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -47,7 +47,7 @@ namespace ts.moduleSpecifiers { host: ModuleSpecifierResolutionHost, oldImportSpecifier: string, ): string | undefined { - const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier)); + const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier), {}); if (res === oldImportSpecifier) return undefined; return res; } @@ -59,9 +59,8 @@ namespace ts.moduleSpecifiers { importingSourceFileName: Path, toFileName: string, host: ModuleSpecifierResolutionHost, - preferences: UserPreferences = {}, ): string { - return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferences(preferences, compilerOptions, importingSourceFile)); + return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferences({}, compilerOptions, importingSourceFile), {}); } export function getNodeModulesPackageName( @@ -69,9 +68,10 @@ namespace ts.moduleSpecifiers { importingSourceFileName: Path, nodeModulesFileName: string, host: ModuleSpecifierResolutionHost, + preferences: UserPreferences, ): string | undefined { const info = getInfo(importingSourceFileName, host); - const modulePaths = getAllModulePaths(importingSourceFileName, nodeModulesFileName, host); + const modulePaths = getAllModulePaths(importingSourceFileName, nodeModulesFileName, host, preferences); return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions, /*packageNameOnly*/ true)); } @@ -81,10 +81,11 @@ namespace ts.moduleSpecifiers { importingSourceFileName: Path, toFileName: string, host: ModuleSpecifierResolutionHost, - preferences: Preferences + preferences: Preferences, + userPreferences: UserPreferences, ): string { const info = getInfo(importingSourceFileName, host); - const modulePaths = getAllModulePaths(importingSourceFileName, toFileName, host); + const modulePaths = getAllModulePaths(importingSourceFileName, toFileName, host, userPreferences); return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions)) || getLocalModuleSpecifier(toFileName, info, compilerOptions, host, preferences); } @@ -106,9 +107,17 @@ namespace ts.moduleSpecifiers { if (!moduleSourceFile) { return []; } - const modulePaths = getAllModulePaths(importingSourceFile.path, moduleSourceFile.originalFileName, host); - const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile); + const cache = host.getModuleSpecifierCache?.(); + const cached = cache?.get(importingSourceFile.path, moduleSourceFile.path, userPreferences); + let modulePaths; + if (cached) { + if (cached.moduleSpecifiers) return cached.moduleSpecifiers; + modulePaths = cached.modulePaths; + } + + modulePaths ||= getAllModulePathsWorker(importingSourceFile.path, moduleSourceFile.originalFileName, host); + const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile); const existingSpecifier = forEach(modulePaths, modulePath => forEach( host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)), reason => { @@ -120,7 +129,11 @@ namespace ts.moduleSpecifiers { undefined; } )); - if (existingSpecifier) return [existingSpecifier]; + if (existingSpecifier) { + const moduleSpecifiers = [existingSpecifier]; + cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, moduleSpecifiers); + return moduleSpecifiers; + } const importedFileIsInNodeModules = some(modulePaths, p => p.isInNodeModules); @@ -138,6 +151,7 @@ namespace ts.moduleSpecifiers { if (specifier && modulePath.isRedirect) { // If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar", // not "@foo/bar/path/to/file"). No other specifier will be this good, so stop looking. + cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, nodeModulesSpecifiers!); return nodeModulesSpecifiers!; } @@ -161,9 +175,11 @@ namespace ts.moduleSpecifiers { } } - return pathsSpecifiers?.length ? pathsSpecifiers : + const moduleSpecifiers = pathsSpecifiers?.length ? pathsSpecifiers : nodeModulesSpecifiers?.length ? nodeModulesSpecifiers : Debug.checkDefined(relativeSpecifiers); + cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, moduleSpecifiers); + return moduleSpecifiers; } interface Info { @@ -294,11 +310,8 @@ namespace ts.moduleSpecifiers { const result = forEach(targets, p => !(shouldFilterIgnoredPaths && containsIgnoredPath(p)) && cb(p, referenceRedirect === p)); if (result) return result; } - const links = host.getSymlinkCache - ? host.getSymlinkCache() - : discoverProbableSymlinks(host.getSourceFiles(), getCanonicalFileName, cwd); - const symlinkedDirectories = links.getSymlinkedDirectoriesByRealpath(); + const symlinkedDirectories = host.getSymlinkCache?.().getSymlinkedDirectoriesByRealpath(); const fullImportedFileName = getNormalizedAbsolutePath(importedFileName, cwd); const result = symlinkedDirectories && forEachAncestorDirectory(getDirectoryPath(fullImportedFileName), realPathDirectory => { const symlinkDirectories = symlinkedDirectories.get(ensureTrailingDirectorySeparator(toPath(realPathDirectory, cwd, getCanonicalFileName))); @@ -332,13 +345,27 @@ namespace ts.moduleSpecifiers { * Looks for existing imports that use symlinks to this module. * Symlinks will be returned first so they are preferred over the real path. */ - function getAllModulePaths(importingFileName: Path, importedFileName: string, host: ModuleSpecifierResolutionHost): readonly ModulePath[] { + function getAllModulePaths( + importingFilePath: Path, + importedFileName: string, + host: ModuleSpecifierResolutionHost, + preferences: UserPreferences, + importedFilePath = toPath(importedFileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host)) + ) { const cache = host.getModuleSpecifierCache?.(); - const getCanonicalFileName = hostGetCanonicalFileName(host); if (cache) { - const cached = cache.get(importingFileName, toPath(importedFileName, host.getCurrentDirectory(), getCanonicalFileName)); - if (typeof cached === "object") return cached; + const cached = cache.get(importingFilePath, importedFilePath, preferences); + if (cached?.modulePaths) return cached.modulePaths; + } + const modulePaths = getAllModulePathsWorker(importingFilePath, importedFileName, host); + if (cache) { + cache.setModulePaths(importingFilePath, importedFilePath, preferences, modulePaths); } + return modulePaths; + } + + function getAllModulePathsWorker(importingFileName: Path, importedFileName: string, host: ModuleSpecifierResolutionHost): readonly ModulePath[] { + const getCanonicalFileName = hostGetCanonicalFileName(host); const allFileNames = new Map(); let importedFileFromNodeModules = false; forEachFileNameOfModule( @@ -384,9 +411,6 @@ namespace ts.moduleSpecifiers { sortedPaths.push(...remainingPaths); } - if (cache) { - cache.set(importingFileName, toPath(importedFileName, host.getCurrentDirectory(), getCanonicalFileName), sortedPaths); - } return sortedPaths; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bc68220e9879a..a29b54ea34ccc 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -3721,10 +3721,13 @@ namespace ts { if (host.getSymlinkCache) { return host.getSymlinkCache(); } - return symlinks || (symlinks = discoverProbableSymlinks( - files, - getCanonicalFileName, - host.getCurrentDirectory())); + if (!symlinks) { + symlinks = createSymlinkCache(currentDirectory, getCanonicalFileName); + } + if (files && resolvedTypeReferenceDirectives && !symlinks.hasProcessedResolutions()) { + symlinks.setSymlinksFromResolutions(files, resolvedTypeReferenceDirectives); + } + return symlinks; } } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 6a9cb08fa7cec..cc58530f46633 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -380,7 +380,6 @@ namespace ts { toPath(outputFilePath, host.getCurrentDirectory(), host.getCanonicalFileName), toPath(declFileName, host.getCurrentDirectory(), host.getCanonicalFileName), host, - /*preferences*/ undefined, ); if (!pathIsRelative(specifier)) { // If some compiler option/symlink/whatever allows access to the file containing the ambient module declaration diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 9c329520c7600..65f5ec989bf22 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -85,6 +85,7 @@ namespace ts { * writeFileCallback */ writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + getCustomTransformers?: (project: string) => CustomTransformers | undefined; getModifiedTime(fileName: string): Date | undefined; setModifiedTime(fileName: string, date: Date): void; @@ -785,7 +786,7 @@ namespace ts { emit: (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => { if (targetSourceFile || emitOnlyDtsFiles) { return withProgramOrUndefined( - program => program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) + program => program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers || state.host.getCustomTransformers?.(project)) ); } executeSteps(BuildStep.SemanticDiagnostics, cancellationToken); @@ -921,7 +922,7 @@ namespace ts { (name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }), cancellationToken, /*emitOnlyDts*/ false, - customTransformers + customTransformers || state.host.getCustomTransformers?.(project) ); // Don't emit .d.ts if there are decl file errors if (declDiagnostics) { @@ -1060,7 +1061,7 @@ namespace ts { const refName = resolveProjectName(state, ref.path); return parseConfigFile(state, refName, toResolvedConfigFilePath(state, refName)); }, - customTransformers + customTransformers || state.host.getCustomTransformers?.(project) ); if (isString(outputFiles)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d6e196b707c34..5e2c4b031484e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4232,6 +4232,7 @@ namespace ts { /* @internal */ getSuggestedSymbolForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined; /* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; /* @internal */ getSuggestedSymbolForNonexistentModule(node: Identifier, target: Symbol): Symbol | undefined; + /* @internal */ getSuggestedSymbolForNonexistentClassMember(name: string, baseType: Type): Symbol | undefined; /* @internal */ getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; @@ -8171,7 +8172,6 @@ namespace ts { getGlobalTypingsCacheLocation?(): string | undefined; getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; - getSourceFiles(): readonly SourceFile[]; readonly redirectTargetsMap: RedirectTargetsMap; getProjectReferenceRedirect(fileName: string): string | undefined; isSourceOfProjectReferenceRedirect(fileName: string): boolean; @@ -8185,10 +8185,19 @@ namespace ts { isRedirect: boolean; } + /*@internal*/ + export interface ResolvedModuleSpecifierInfo { + modulePaths: readonly ModulePath[] | undefined; + moduleSpecifiers: readonly string[] | undefined; + isAutoImportable: boolean | undefined; + } + /* @internal */ export interface ModuleSpecifierCache { - get(fromFileName: Path, toFileName: Path): boolean | readonly ModulePath[] | undefined; - set(fromFileName: Path, toFileName: Path, moduleSpecifiers: boolean | readonly ModulePath[]): void; + get(fromFileName: Path, toFileName: Path, preferences: UserPreferences): Readonly | undefined; + set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void; + setIsAutoImportable(fromFileName: Path, toFileName: Path, preferences: UserPreferences, isAutoImportable: boolean): void; + setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[]): void; clear(): void; count(): number; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3b949fa7807a9..e602c2ae43e5a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6199,12 +6199,25 @@ namespace ts { setSymlinkedFile(symlinkPath: Path, real: string): void; /*@internal*/ setSymlinkedDirectoryFromSymlinkedFile(symlink: string, real: string): void; + /** + * @internal + * Uses resolvedTypeReferenceDirectives from program instead of from files, since files + * don't include automatic type reference directives. Must be called only when + * `hasProcessedResolutions` returns false (once per cache instance). + */ + setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ReadonlyESMap | undefined): void; + /** + * @internal + * Whether `setSymlinksFromResolutions` has already been called. + */ + hasProcessedResolutions(): boolean; } export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache { let symlinkedDirectories: ESMap | undefined; let symlinkedDirectoriesByRealpath: MultiMap | undefined; let symlinkedFiles: ESMap | undefined; + let hasProcessedResolutions = false; return { getSymlinkedFiles: () => symlinkedFiles, getSymlinkedDirectories: () => symlinkedDirectories, @@ -6233,28 +6246,28 @@ namespace ts { }); } }, + setSymlinksFromResolutions(files, typeReferenceDirectives) { + Debug.assert(!hasProcessedResolutions); + hasProcessedResolutions = true; + for (const file of files) { + file.resolvedModules?.forEach(resolution => processResolution(this, resolution)); + } + typeReferenceDirectives?.forEach(resolution => processResolution(this, resolution)); + }, + hasProcessedResolutions: () => hasProcessedResolutions, }; - } - - export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache { - const cache = createSymlinkCache(cwd, getCanonicalFileName); - const symlinks = flatMap(files, sf => { - const pairs = sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res => - res?.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)); - return concatenate(pairs, sf.resolvedTypeReferenceDirectiveNames && arrayFrom(mapDefinedIterator(sf.resolvedTypeReferenceDirectiveNames.values(), res => - res?.originalPath && res.resolvedFileName ? [res.resolvedFileName, res.originalPath] as const : undefined))); - }); - for (const [resolvedPath, originalPath] of symlinks) { - cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedPath); - const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName) || emptyArray; + function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined) { + if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return; + const { resolvedFileName, originalPath } = resolution; + cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); + const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName) || emptyArray; if (commonResolved && commonOriginal) { cache.setSymlinkedDirectory( commonOriginal, { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) }); } } - return cache; } function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName): [string, string] | undefined { diff --git a/src/harness/client.ts b/src/harness/client.ts index 97e05c563b914..46c90b94b3c81 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -196,18 +196,16 @@ namespace ts.server { // Not passing along 'preferences' because server should already have those from the 'configure' command const args: protocol.CompletionsRequestArgs = this.createFileLocationRequestArgs(fileName, position); - const request = this.processRequest(CommandNames.Completions, args); - const response = this.processResponse(request); + const request = this.processRequest(CommandNames.CompletionInfo, args); + const response = this.processResponse(request); return { - isGlobalCompletion: false, - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: response.body!.map(entry => { // TODO: GH#18217 + isGlobalCompletion: response.body!.isGlobalCompletion, + isMemberCompletion: response.body!.isMemberCompletion, + isNewIdentifierLocation: response.body!.isNewIdentifierLocation, + entries: response.body!.entries.map(entry => { // TODO: GH#18217 if (entry.replacementSpan !== undefined) { - const { name, kind, kindModifiers, sortText, replacementSpan, hasAction, source, data, isRecommended } = entry; - // TODO: GH#241 - const res: CompletionEntry = { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName), hasAction, source, data: data as any, isRecommended }; + const res: CompletionEntry = { ...entry, data: entry.data as any, replacementSpan: this.decodeSpan(entry.replacementSpan, fileName) }; return res; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d4c4277aba377..7378656c00115 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -589,8 +589,11 @@ namespace ts.server { ); } - interface ScriptInfoInNodeModulesWatcher extends FileWatcher { - refCount: number; + interface NodeModulesWatcher extends FileWatcher { + /** How many watchers of this directory were for closed ScriptInfo */ + refreshScriptInfoRefCount: number; + /** List of project names whose module specifier cache should be cleared when package.jsons change */ + affectedModuleSpecifierCacheProjects?: Set; } function getDetailWatchInfo(watchType: WatchType, project: Project | NormalizedPath | undefined) { @@ -676,7 +679,7 @@ namespace ts.server { */ /*@internal*/ readonly filenameToScriptInfo = new Map(); - private readonly scriptInfoInNodeModulesWatchers = new Map(); + private readonly nodeModulesWatchers = new Map(); /** * Contains all the deleted script info's version information so that * it does not reset when creating script info again @@ -2626,59 +2629,87 @@ namespace ts.server { } } - private watchClosedScriptInfoInNodeModules(dir: Path): ScriptInfoInNodeModulesWatcher { - // Watch only directory - const existing = this.scriptInfoInNodeModulesWatchers.get(dir); - if (existing) { - existing.refCount++; - return existing; - } - - const watchDir = dir + "/node_modules" as Path; + private createNodeModulesWatcher(dir: Path) { const watcher = this.watchFactory.watchDirectory( - watchDir, + dir, fileOrDirectory => { const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory)); if (!fileOrDirectoryPath) return; - // Has extension - Debug.assert(result.refCount > 0); - if (watchDir === fileOrDirectoryPath) { - this.refreshScriptInfosInDirectory(watchDir); + // Clear module specifier cache for any projects whose cache was affected by + // dependency package.jsons in this node_modules directory + const basename = getBaseFileName(fileOrDirectoryPath); + if (result.affectedModuleSpecifierCacheProjects?.size && ( + basename === "package.json" || basename === "node_modules" + )) { + result.affectedModuleSpecifierCacheProjects.forEach(projectName => { + this.findProject(projectName)?.getModuleSpecifierCache()?.clear(); + }); } - else { - const info = this.getScriptInfoForPath(fileOrDirectoryPath); - if (info) { - if (isScriptInfoWatchedFromNodeModules(info)) { - this.refreshScriptInfo(info); - } + + // Refresh closed script info after an npm install + if (result.refreshScriptInfoRefCount) { + if (dir === fileOrDirectoryPath) { + this.refreshScriptInfosInDirectory(dir); } - // Folder - else if (!hasExtension(fileOrDirectoryPath)) { - this.refreshScriptInfosInDirectory(fileOrDirectoryPath); + else { + const info = this.getScriptInfoForPath(fileOrDirectoryPath); + if (info) { + if (isScriptInfoWatchedFromNodeModules(info)) { + this.refreshScriptInfo(info); + } + } + // Folder + else if (!hasExtension(fileOrDirectoryPath)) { + this.refreshScriptInfosInDirectory(fileOrDirectoryPath); + } } } }, WatchDirectoryFlags.Recursive, this.hostConfiguration.watchOptions, - WatchType.NodeModulesForClosedScriptInfo + WatchType.NodeModules ); - const result: ScriptInfoInNodeModulesWatcher = { + const result: NodeModulesWatcher = { + refreshScriptInfoRefCount: 0, + affectedModuleSpecifierCacheProjects: undefined, close: () => { - if (result.refCount === 1) { + if (!result.refreshScriptInfoRefCount && !result.affectedModuleSpecifierCacheProjects?.size) { watcher.close(); - this.scriptInfoInNodeModulesWatchers.delete(dir); - } - else { - result.refCount--; + this.nodeModulesWatchers.delete(dir); } }, - refCount: 1 }; - this.scriptInfoInNodeModulesWatchers.set(dir, result); + this.nodeModulesWatchers.set(dir, result); return result; } + /*@internal*/ + watchPackageJsonsInNodeModules(dir: Path, project: Project): FileWatcher { + const watcher = this.nodeModulesWatchers.get(dir) || this.createNodeModulesWatcher(dir); + (watcher.affectedModuleSpecifierCacheProjects ||= new Set()).add(project.getProjectName()); + + return { + close: () => { + watcher.affectedModuleSpecifierCacheProjects?.delete(project.getProjectName()); + watcher.close(); + }, + }; + } + + private watchClosedScriptInfoInNodeModules(dir: Path): FileWatcher { + const watchDir = dir + "/node_modules" as Path; + const watcher = this.nodeModulesWatchers.get(watchDir) || this.createNodeModulesWatcher(watchDir); + watcher.refreshScriptInfoRefCount++; + + return { + close: () => { + watcher.refreshScriptInfoRefCount--; + watcher.close(); + }, + }; + } + private getModifiedTime(info: ScriptInfo) { return (this.host.getModifiedTime!(info.path) || missingFileModifiedTime).getTime(); } @@ -2954,7 +2985,11 @@ namespace ts.server { this.logger.info("Format host information updated"); } if (args.preferences) { - const { lazyConfiguredProjectsFromExternalProject, includePackageJsonAutoImports } = this.hostConfiguration.preferences; + const { + lazyConfiguredProjectsFromExternalProject, + includePackageJsonAutoImports, + } = this.hostConfiguration.preferences; + this.hostConfiguration.preferences = { ...this.hostConfiguration.preferences, ...args.preferences }; if (lazyConfiguredProjectsFromExternalProject && !this.hostConfiguration.preferences.lazyConfiguredProjectsFromExternalProject) { // Load configured projects for external projects that are pending reload diff --git a/src/server/project.ts b/src/server/project.ts index d3bb46302b82d..a812d29eeac92 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -254,7 +254,7 @@ namespace ts.server { /*@internal*/ private changedFilesForExportMapCache: Set | undefined; /*@internal*/ - private moduleSpecifierCache = createModuleSpecifierCache(); + private moduleSpecifierCache = createModuleSpecifierCache(this); /*@internal*/ private symlinks: SymlinkCache | undefined; /*@internal*/ @@ -353,10 +353,15 @@ namespace ts.server { /*@internal*/ getSymlinkCache(): SymlinkCache { - return this.symlinks || (this.symlinks = discoverProbableSymlinks( - this.program?.getSourceFiles() || emptyArray, - this.getCanonicalFileName, - this.getCurrentDirectory())); + if (!this.symlinks) { + this.symlinks = createSymlinkCache(this.getCurrentDirectory(), this.getCanonicalFileName); + } + if (this.program && !this.symlinks.hasProcessedResolutions()) { + this.symlinks.setSymlinksFromResolutions( + this.program.getSourceFiles(), + this.program.getResolvedTypeReferenceDirectives()); + } + return this.symlinks; } // Method of LanguageServiceHost @@ -785,6 +790,7 @@ namespace ts.server { this.resolutionCache.clear(); this.resolutionCache = undefined!; this.cachedUnresolvedImportsPerFile = undefined!; + this.moduleSpecifierCache = undefined!; this.directoryStructureHost = undefined!; this.projectErrors = undefined; @@ -1389,6 +1395,7 @@ namespace ts.server { this.cachedUnresolvedImportsPerFile.clear(); this.lastCachedUnresolvedImportsList = undefined; this.resolutionCache.clear(); + this.moduleSpecifierCache.clear(); } this.markAsDirty(); } @@ -1730,6 +1737,11 @@ namespace ts.server { this.projectService.openFiles, (_, fileName) => this.projectService.tryGetDefaultProjectForFile(toNormalizedPath(fileName)) === this); } + + /*@internal*/ + watchNodeModulesForPackageJsonChanges(directoryPath: string) { + return this.projectService.watchPackageJsonsInNodeModules(this.toPath(directoryPath), this); + } } function getUnresolvedImports(program: Program, cachedUnresolvedImportsPerFile: ESMap): SortedReadonlyArray { diff --git a/src/server/watchType.ts b/src/server/watchType.ts index 8cc5014b0e57b..2b9edafb09870 100644 --- a/src/server/watchType.ts +++ b/src/server/watchType.ts @@ -4,17 +4,18 @@ namespace ts { export interface WatchTypeRegistry { ClosedScriptInfo: "Closed Script info", ConfigFileForInferredRoot: "Config file for the inferred project root", - NodeModulesForClosedScriptInfo: "node_modules for closed script infos in them", + NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache", MissingSourceMapFile: "Missing source map file", NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root", MissingGeneratedFile: "Missing generated file", - PackageJsonFile: "package.json file for import suggestions" + PackageJsonFile: "package.json file for import suggestions", + NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation", } WatchType.ClosedScriptInfo = "Closed Script info"; WatchType.ConfigFileForInferredRoot = "Config file for the inferred project root"; - WatchType.NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them"; + WatchType.NodeModules = "node_modules for closed script infos and package.jsons affecting module specifier cache"; WatchType.MissingSourceMapFile = "Missing source map file"; WatchType.NoopConfigFileForInferredRoot = "Noop Config file for the inferred project root"; WatchType.MissingGeneratedFile = "Missing generated file"; WatchType.PackageJsonFile = "package.json file for import suggestions"; -} \ No newline at end of file +} diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 787c4d70ac63c..35c99fd5283b7 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -7,6 +7,7 @@ namespace ts.codefix { Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2.code, + Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1.code, // for JSX class components Diagnostics.No_overload_matches_this_call.code, // for JSX FC @@ -73,6 +74,14 @@ namespace ts.codefix { const props = checker.getContextualTypeForArgumentAtIndex(tag, 0); suggestedSymbol = checker.getSuggestedSymbolForNonexistentJSXAttribute(node, props!); } + else if (hasSyntacticModifier(parent, ModifierFlags.Override) && isClassElement(parent) && parent.name === node) { + const baseDeclaration = findAncestor(node, isClassLike); + const baseTypeNode = baseDeclaration ? getEffectiveBaseTypeNode(baseDeclaration) : undefined; + const baseType = baseTypeNode ? checker.getTypeAtLocation(baseTypeNode) : undefined; + if (baseType) { + suggestedSymbol = checker.getSuggestedSymbolForNonexistentClassMember(getTextOfNode(node), baseType); + } + } else { const meaning = getMeaningFromLocation(node); const name = getTextOfNode(node); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 0653d0a10db27..dd452ffaf7053 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -62,11 +62,13 @@ namespace ts.codefix { const symbolName = getNameForExportedSymbol(exportedSymbol, getEmitScriptTarget(compilerOptions)); const checker = program.getTypeChecker(); const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker)); - const exportInfos = getAllReExportingModules(sourceFile, symbol, moduleSymbol, symbolName, host, program, useAutoImportProvider); + const exportInfos = getAllReExportingModules(sourceFile, symbol, moduleSymbol, symbolName, host, program, preferences, useAutoImportProvider); const preferTypeOnlyImport = !!usageIsTypeOnly && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error; const useRequire = shouldUseRequire(sourceFile, program); const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, /*position*/ undefined, preferTypeOnlyImport, useRequire, host, preferences); - addImport({ fixes: [fix], symbolName }); + if (fix) { + addImport({ fixes: [fix], symbolName }); + } } function addImport(info: FixesInfo) { @@ -200,16 +202,16 @@ namespace ts.codefix { const compilerOptions = program.getCompilerOptions(); const exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name)) ? [getSymbolExportInfoForSymbol(exportedSymbol, moduleSymbol, program, host)] - : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true); + : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, preferences, /*useAutoImportProvider*/ true); const useRequire = shouldUseRequire(sourceFile, program); const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position)); - const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences); + const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences)); return { moduleSpecifier: fix.moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) }; } function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], moduleSymbol: Symbol, symbolName: string, program: Program, position: number | undefined, preferTypeOnlyImport: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) { Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol), "Some exportInfo should match the specified moduleSymbol"); - return getBestFix(getImportFixes(exportInfos, symbolName, position, preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences), sourceFile, host); + return getBestFix(getImportFixes(exportInfos, symbolName, position, preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences), sourceFile, host, preferences); } function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction { @@ -237,7 +239,7 @@ namespace ts.codefix { } } - function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, useAutoImportProvider: boolean): readonly SymbolExportInfo[] { + function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, preferences: UserPreferences, useAutoImportProvider: boolean): readonly SymbolExportInfo[] { const result: SymbolExportInfo[] = []; const compilerOptions = program.getCompilerOptions(); const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => { @@ -265,7 +267,7 @@ namespace ts.codefix { return result; function isImportable(program: Program, moduleFile: SourceFile | undefined, isFromPackageJson: boolean) { - return !moduleFile || isImportableFile(program, importingFile, moduleFile, /*packageJsonFilter*/ undefined, getModuleSpecifierResolutionHost(isFromPackageJson), host.getModuleSpecifierCache?.()); + return !moduleFile || isImportableFile(program, importingFile, moduleFile, preferences, /*packageJsonFilter*/ undefined, getModuleSpecifierResolutionHost(isFromPackageJson), host.getModuleSpecifierCache?.()); } } @@ -274,8 +276,8 @@ namespace ts.codefix { program: Program, host: LanguageServiceHost, preferences: UserPreferences - ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string } { - return getBestFix(getNewImportFixes(program, importingFile, /*position*/ undefined, /*preferTypeOnlyImport*/ false, /*useRequire*/ false, exportInfo, host, preferences), importingFile, host); + ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string } | undefined { + return getBestFix(getNewImportFixes(program, importingFile, /*position*/ undefined, /*preferTypeOnlyImport*/ false, /*useRequire*/ false, exportInfo, host, preferences), importingFile, host, preferences); } export function getSymbolToExportInfoMap(importingFile: SourceFile, host: LanguageServiceHost, program: Program) { @@ -521,20 +523,21 @@ namespace ts.codefix { const info = errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code ? getFixesInfoForUMDImport(context, symbolToken) : isIdentifier(symbolToken) ? getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider) : undefined; - return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.host) }; + return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.host, context.preferences) }; } - function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, host: LanguageServiceHost): readonly ImportFix[] { - const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, host); + function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, host: LanguageServiceHost, preferences: UserPreferences): readonly ImportFix[] { + const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host); return sort(fixes, (a, b) => compareValues(a.kind, b.kind) || compareModuleSpecifiers(a, b, allowsImportingSpecifier)); } - function getBestFix(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost): T { + function getBestFix(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost, preferences: UserPreferences): T | undefined { + if (!some(fixes)) return; // These will always be placed first if available, and are better than other kinds if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) { return fixes[0]; } - const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, host); + const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host); return fixes.reduce((best, fix) => compareModuleSpecifiers(fix, best, allowsImportingSpecifier) === Comparison.LessThan ? fix : best ); @@ -621,7 +624,7 @@ namespace ts.codefix { const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && isValidTypeOnlyAliasUseSite(symbolToken); const useRequire = shouldUseRequire(sourceFile, program); - const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host); + const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host, preferences); const fixes = arrayFrom(flatMapIterator(exportInfos.entries(), ([_, exportInfos]) => getImportFixes(exportInfos, symbolName, symbolToken.getStart(sourceFile), preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences))); return { fixes, symbolName }; @@ -646,19 +649,20 @@ namespace ts.codefix { fromFile: SourceFile, program: Program, useAutoImportProvider: boolean, - host: LanguageServiceHost + host: LanguageServiceHost, + preferences: UserPreferences, ): ReadonlyESMap { // For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once. // Maps symbol id to info for modules providing that symbol (original export + re-exports). const originalSymbolToExportInfos = createMultiMap(); - const packageJsonFilter = createPackageJsonImportFilter(fromFile, host); + const packageJsonFilter = createPackageJsonImportFilter(fromFile, preferences, host); const moduleSpecifierCache = host.getModuleSpecifierCache?.(); const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => { return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host); }); function addSymbol(moduleSymbol: Symbol, toFile: SourceFile | undefined, exportedSymbol: Symbol, exportKind: ExportKind, program: Program, isFromPackageJson: boolean): void { const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson); - if (toFile && isImportableFile(program, fromFile, toFile, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) || + if (toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) || !toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) ) { const checker = program.getTypeChecker(); diff --git a/src/services/completions.ts b/src/services/completions.ts index 5227655a6eee6..21db1ed3c4570 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1245,6 +1245,7 @@ namespace ts.Completions { break; case SyntaxKind.JsxExpression: + case SyntaxKind.JsxSpreadAttribute: // For `
`, `parent` will be `{true}` and `previousToken` will be `}` if (previousToken.kind === SyntaxKind.CloseBraceToken && currentToken.kind === SyntaxKind.GreaterThanToken) { isJsxIdentifierExpected = true; @@ -1594,6 +1595,7 @@ namespace ts.Completions { function tryGetImportCompletionSymbols(): GlobalsSearch { if (!importCompletionNode) return GlobalsSearch.Continue; + isNewIdentifierLocation = true; collectAutoImports(/*resolveModuleSpecifiers*/ true); return GlobalsSearch.Success; } @@ -1753,7 +1755,7 @@ namespace ts.Completions { const lowerCaseTokenText = previousToken && isIdentifier(previousToken) ? previousToken.text.toLowerCase() : ""; const exportInfo = codefix.getSymbolToExportInfoMap(sourceFile, host, program); const packageJsonAutoImportProvider = host.getPackageJsonAutoImportProvider?.(); - const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, host); + const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, preferences, host); exportInfo.forEach((info, key) => { const symbolName = key.substring(0, key.indexOf("|")); if (!detailsEntryId && isStringANonContextualKeyword(symbolName)) return; @@ -1762,7 +1764,7 @@ namespace ts.Completions { // If we don't need to resolve module specifiers, we can use any re-export that is importable at all // (We need to ensure that at least one is importable to show a completion.) const { moduleSpecifier, exportInfo } = resolveModuleSpecifiers - ? codefix.getModuleSpecifierForBestExportInfo(info, sourceFile, program, host, preferences) + ? codefix.getModuleSpecifierForBestExportInfo(info, sourceFile, program, host, preferences) || {} : { moduleSpecifier: undefined, exportInfo: find(info, isImportableExportInfo) }; if (!exportInfo) return; const moduleFile = tryCast(exportInfo.moduleSymbol.valueDeclaration, isSourceFile); @@ -1802,6 +1804,7 @@ namespace ts.Completions { info.isFromPackageJson ? packageJsonAutoImportProvider! : program, sourceFile, moduleFile, + preferences, packageJsonFilter, getModuleSpecifierResolutionHost(info.isFromPackageJson), moduleSpecifierCache); diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 6987fdd491911..75cf29f9d79fe 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -177,7 +177,11 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { function convertToBlock(body: ConciseBody): Block { if (isExpression(body)) { - return factory.createBlock([factory.createReturnStatement(body)], /* multiLine */ true); + const returnStatement = factory.createReturnStatement(body); + const file = body.getSourceFile(); + suppressLeadingAndTrailingTrivia(returnStatement); + copyTrailingAsLeadingComments(body, returnStatement, file, /* commentKind */ undefined, /* hasTrailingNewLine */ true); + return factory.createBlock([returnStatement], /* multiLine */ true); } else { return body; diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index c85a825a958de..ebf72aa7589d3 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -215,7 +215,6 @@ namespace ts.refactor.extractSymbol { export const cannotExtractReadonlyPropertyInitializerOutsideConstructor = createMessage("Cannot move initialization of read-only class property outside of the constructor"); export const cannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts"); export const cannotAccessVariablesFromNestedScopes = createMessage("Cannot access variables from nested scopes"); - export const cannotExtractToOtherFunctionLike = createMessage("Cannot extract method to a function-like scope that is not a function"); export const cannotExtractToJSClass = createMessage("Cannot extract constant to a class scope in JS"); export const cannotExtractToExpressionArrowFunction = createMessage("Cannot extract constant to an arrow function without a block"); } @@ -1624,10 +1623,7 @@ namespace ts.refactor.extractSymbol { usagesPerScope.push({ usages: new Map(), typeParameterUsages: new Map(), substitutions: new Map() }); substitutionsPerScope.push(new Map()); - functionErrorsPerScope.push( - isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration - ? [createDiagnosticForNode(scope, Messages.cannotExtractToOtherFunctionLike)] - : []); + functionErrorsPerScope.push([]); const constantErrors = []; if (expressionDiagnostic) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index d64bfc85129b0..bbd5125d9544b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1845,7 +1845,6 @@ namespace ts { getSymlinkCache: maybeBind(host, host.getSymlinkCache) || program.getSymlinkCache, getModuleSpecifierCache: maybeBind(host, host.getModuleSpecifierCache), getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation), - getSourceFiles: () => program.getSourceFiles(), redirectTargetsMap: program.redirectTargetsMap, getProjectReferenceRedirect: fileName => program.getProjectReferenceRedirect(fileName), isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName), @@ -2895,7 +2894,7 @@ namespace ts { allowsImportingSpecifier: (moduleSpecifier: string) => boolean; } - export function createPackageJsonImportFilter(fromFile: SourceFile, host: LanguageServiceHost): PackageJsonImportFilter { + export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter { const packageJsons = ( (host.getPackageJsonsVisibleToFile && host.getPackageJsonsVisibleToFile(fromFile.fileName)) || getPackageJsonsVisibleToFile(fromFile.fileName, host) ).filter(p => p.parseable); @@ -2981,6 +2980,7 @@ namespace ts { fromFile.path, importedFileName, moduleSpecifierResolutionHost, + preferences, ); if (!specifier) { @@ -3276,47 +3276,112 @@ namespace ts { } } - export function createModuleSpecifierCache(): ModuleSpecifierCache { - let cache: ESMap | undefined; - let importingFileName: Path | undefined; - const wrapped: ModuleSpecifierCache = { - get(fromFileName, toFileName) { - if (!cache || fromFileName !== importingFileName) return undefined; + export interface ModuleSpecifierResolutionCacheHost { + watchNodeModulesForPackageJsonChanges(directoryPath: string): FileWatcher; + } + + export function createModuleSpecifierCache(host: ModuleSpecifierResolutionCacheHost): ModuleSpecifierCache { + let containedNodeModulesWatchers: ESMap | undefined; + let cache: ESMap | undefined; + let currentKey: string | undefined; + const result: ModuleSpecifierCache = { + get(fromFileName, toFileName, preferences) { + if (!cache || currentKey !== key(fromFileName, preferences)) return undefined; return cache.get(toFileName); }, - set(fromFileName, toFileName, moduleSpecifiers) { - if (cache && fromFileName !== importingFileName) { - cache.clear(); + set(fromFileName, toFileName, preferences, modulePaths, moduleSpecifiers) { + ensureCache(fromFileName, preferences).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isAutoImportable*/ true)); + + // If any module specifiers were generated based off paths in node_modules, + // a package.json file in that package was read and is an input to the cached. + // Instead of watching each individual package.json file, set up a wildcard + // directory watcher for any node_modules referenced and clear the cache when + // it sees any changes. + if (moduleSpecifiers) { + for (const p of modulePaths) { + if (p.isInNodeModules) { + // No trailing slash + const nodeModulesPath = p.path.substring(0, p.path.indexOf(nodeModulesPathPart) + nodeModulesPathPart.length - 1); + if (!containedNodeModulesWatchers?.has(nodeModulesPath)) { + (containedNodeModulesWatchers ||= new Map()).set( + nodeModulesPath, + host.watchNodeModulesForPackageJsonChanges(nodeModulesPath), + ); + } + } + } + } + }, + setModulePaths(fromFileName, toFileName, preferences, modulePaths) { + const cache = ensureCache(fromFileName, preferences); + const info = cache.get(toFileName); + if (info) { + info.modulePaths = modulePaths; + } + else { + cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isAutoImportable*/ undefined)); + } + }, + setIsAutoImportable(fromFileName, toFileName, preferences, isAutoImportable) { + const cache = ensureCache(fromFileName, preferences); + const info = cache.get(toFileName); + if (info) { + info.isAutoImportable = isAutoImportable; + } + else { + cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isAutoImportable)); } - importingFileName = fromFileName; - (cache ||= new Map()).set(toFileName, moduleSpecifiers); }, clear() { - cache = undefined; - importingFileName = undefined; + containedNodeModulesWatchers?.forEach(watcher => watcher.close()); + cache?.clear(); + containedNodeModulesWatchers?.clear(); + currentKey = undefined; }, count() { return cache ? cache.size : 0; } }; if (Debug.isDebugging) { - Object.defineProperty(wrapped, "__cache", { get: () => cache }); + Object.defineProperty(result, "__cache", { get: () => cache }); + } + return result; + + function ensureCache(fromFileName: Path, preferences: UserPreferences) { + const newKey = key(fromFileName, preferences); + if (cache && (currentKey !== newKey)) { + result.clear(); + } + currentKey = newKey; + return cache ||= new Map(); + } + + function key(fromFileName: Path, preferences: UserPreferences) { + return `${fromFileName},${preferences.importModuleSpecifierEnding},${preferences.importModuleSpecifierPreference}`; + } + + function createInfo( + modulePaths: readonly ModulePath[] | undefined, + moduleSpecifiers: readonly string[] | undefined, + isAutoImportable: boolean | undefined, + ): ResolvedModuleSpecifierInfo { + return { modulePaths, moduleSpecifiers, isAutoImportable }; } - return wrapped; } export function isImportableFile( program: Program, from: SourceFile, to: SourceFile, + preferences: UserPreferences, packageJsonFilter: PackageJsonImportFilter | undefined, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost, moduleSpecifierCache: ModuleSpecifierCache | undefined, ): boolean { if (from === to) return false; - const cachedResult = moduleSpecifierCache?.get(from.path, to.path); - if (cachedResult !== undefined) { - return !!cachedResult; + const cachedResult = moduleSpecifierCache?.get(from.path, to.path, preferences); + if (cachedResult?.isAutoImportable !== undefined) { + return cachedResult.isAutoImportable; } const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost); @@ -3336,9 +3401,9 @@ namespace ts { ); if (packageJsonFilter) { - const isImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost); - moduleSpecifierCache?.set(from.path, to.path, isImportable); - return isImportable; + const isAutoImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost); + moduleSpecifierCache?.setIsAutoImportable(from.path, to.path, preferences, isAutoImportable); + return isAutoImportable; } return hasImportablePath; diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 4a6858ef8c5c8..11ab6b78234b3 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -141,6 +141,7 @@ "unittests/tsbuildWatch/noEmit.ts", "unittests/tsbuildWatch/noEmitOnError.ts", "unittests/tsbuildWatch/programUpdates.ts", + "unittests/tsbuildWatch/publicApi.ts", "unittests/tsbuildWatch/reexport.ts", "unittests/tsbuildWatch/watchEnvironment.ts", "unittests/tsc/composite.ts", @@ -212,6 +213,7 @@ "unittests/tsserver/session.ts", "unittests/tsserver/skipLibCheck.ts", "unittests/tsserver/smartSelection.ts", + "unittests/tsserver/symlinkCache.ts", "unittests/tsserver/symLinks.ts", "unittests/tsserver/syntacticServer.ts", "unittests/tsserver/syntaxOperations.ts", diff --git a/src/testRunner/unittests/services/extract/functions.ts b/src/testRunner/unittests/services/extract/functions.ts index 6f7eecce05a64..c0e0a4b21f5ec 100644 --- a/src/testRunner/unittests/services/extract/functions.ts +++ b/src/testRunner/unittests/services/extract/functions.ts @@ -352,6 +352,11 @@ function parsePrimaryExpression(): any { `function F() { [#|function G() { }|] }`); + // Arrow function + testExtractFunction("extractFunction34", + `const F = () => { + [#|function G() { }|] +};`); testExtractFunction("extractFunction_RepeatedSubstitution", `namespace X { diff --git a/src/testRunner/unittests/tsbuildWatch/publicApi.ts b/src/testRunner/unittests/tsbuildWatch/publicApi.ts new file mode 100644 index 0000000000000..bca1472e0d893 --- /dev/null +++ b/src/testRunner/unittests/tsbuildWatch/publicApi.ts @@ -0,0 +1,115 @@ +namespace ts.tscWatch { + it("unittests:: tsbuildWatch:: watchMode:: Public API with custom transformers", () => { + const solution: File = { + path: `${projectRoot}/tsconfig.json`, + content: JSON.stringify({ + references: [ + { path: "./shared/tsconfig.json" }, + { path: "./webpack/tsconfig.json" } + ], + files: [] + }) + }; + const sharedConfig: File = { + path: `${projectRoot}/shared/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { composite: true }, + }) + }; + const sharedIndex: File = { + path: `${projectRoot}/shared/index.ts`, + content: `export function f1() { } +export class c { } +export enum e { } +// leading +export function f2() { } // trailing` + }; + const webpackConfig: File = { + path: `${projectRoot}/webpack/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { composite: true, }, + references: [{ path: "../shared/tsconfig.json" }] + }) + }; + const webpackIndex: File = { + path: `${projectRoot}/webpack/index.ts`, + content: `export function f2() { } +export class c2 { } +export enum e2 { } +// leading +export function f22() { } // trailing` + }; + const commandLineArgs = ["--b", "--w"]; + const { sys, baseline, oldSnap } = createBaseline(createWatchedSystem([libFile, solution, sharedConfig, sharedIndex, webpackConfig, webpackIndex], { currentDirectory: projectRoot })); + const { cb, getPrograms } = commandLineCallbacks(sys); + const buildHost = createSolutionBuilderWithWatchHost( + sys, + /*createProgram*/ undefined, + createDiagnosticReporter(sys, /*pretty*/ true), + createBuilderStatusReporter(sys, /*pretty*/ true), + createWatchStatusReporter(sys, /*pretty*/ true) + ); + buildHost.afterProgramEmitAndDiagnostics = cb; + buildHost.afterEmitBundle = cb; + buildHost.getCustomTransformers = getCustomTransformers; + const builder = createSolutionBuilderWithWatch(buildHost, [solution.path], { verbose: true }); + builder.build(); + runWatchBaseline({ + scenario: "publicApi", + subScenario: "with custom transformers", + commandLineArgs, + sys, + baseline, + oldSnap, + getPrograms, + changes: [ + { + caption: "change to shared", + change: sys => sys.prependFile(sharedIndex.path, "export function fooBar() {}"), + timeouts: sys => { + sys.checkTimeoutQueueLengthAndRun(1); // Shared + sys.checkTimeoutQueueLengthAndRun(1); // webpack + sys.checkTimeoutQueueLengthAndRun(1); // solution + sys.checkTimeoutQueueLength(0); + } + } + ], + watchOrSolution: builder + }); + + function getCustomTransformers(project: string): CustomTransformers { + const before: TransformerFactory = context => { + return file => visitEachChild(file, visit, context); + function visit(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + return visitFunction(node as FunctionDeclaration); + default: + return visitEachChild(node, visit, context); + } + } + function visitFunction(node: FunctionDeclaration) { + addSyntheticLeadingComment(node, SyntaxKind.MultiLineCommentTrivia, `@before${project}`, /*hasTrailingNewLine*/ true); + return node; + } + }; + + const after: TransformerFactory = context => { + return file => visitEachChild(file, visit, context); + function visit(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.VariableStatement: + return visitVariableStatement(node as VariableStatement); + default: + return visitEachChild(node, visit, context); + } + } + function visitVariableStatement(node: VariableStatement) { + addSyntheticLeadingComment(node, SyntaxKind.SingleLineCommentTrivia, `@after${project}`); + return node; + } + }; + return { before: [before], after: [after] }; + } + }); +} \ No newline at end of file diff --git a/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts b/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts index f109cab9d1f6e..fd57438a2691e 100644 --- a/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts +++ b/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts @@ -4,23 +4,27 @@ namespace ts.projectSystem { content: `{ "dependencies": { "mobx": "*" } }` }; const aTs: File = { - path: "/a.ts", + path: "/src/a.ts", content: "export const foo = 0;", }; const bTs: File = { - path: "/b.ts", + path: "/src/b.ts", content: "foo", }; + const cTs: File = { + path: "/src/c.ts", + content: "import ", + }; const bSymlink: SymLink = { - path: "/b-link.ts", + path: "/src/b-link.ts", symLink: "./b.ts", }; const tsconfig: File = { path: "/tsconfig.json", - content: "{}", + content: `{ "include": ["src"] }`, }; const ambientDeclaration: File = { - path: "/ambient.d.ts", + path: "/src/ambient.d.ts", content: "declare module 'ambient' {}" }; const mobxDts: File = { @@ -31,50 +35,111 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: moduleSpecifierCache", () => { it("caches importability within a file", () => { const { moduleSpecifierCache } = setup(); - assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path)); + assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable); + }); + + it("caches module specifiers within a file", () => { + const { moduleSpecifierCache, triggerCompletions } = setup(); + // Completion at an import statement will calculate and cache module specifiers + triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 }); + const mobxCache = moduleSpecifierCache.get(cTs.path as Path, mobxDts.path as Path, {}); + assert.deepEqual(mobxCache, { + modulePaths: [{ + path: mobxDts.path, + isInNodeModules: true, + isRedirect: false + }], + moduleSpecifiers: ["mobx"], + isAutoImportable: true, + }); + }); + + it("invalidates module specifiers when changes happen in contained node_modules directories", () => { + const { host, moduleSpecifierCache, triggerCompletions } = setup(); + // Completion at an import statement will calculate and cache module specifiers + triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 }); + checkWatchedDirectories(host, ["/src", "/node_modules"], /*recursive*/ true); + host.writeFile("/node_modules/.staging/mobx-12345678/package.json", "{}"); + host.runQueuedTimeoutCallbacks(); + assert.equal(moduleSpecifierCache.count(), 0); }); it("does not invalidate the cache when new files are added", () => { const { host, moduleSpecifierCache } = setup(); host.writeFile("/src/a2.ts", aTs.content); host.runQueuedTimeoutCallbacks(); - assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path)); + assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable); }); it("invalidates the cache when symlinks are added or removed", () => { const { host, moduleSpecifierCache } = setup(); - host.renameFile(bSymlink.path, "/b-link2.ts"); + host.renameFile(bSymlink.path, "/src/b-link2.ts"); host.runQueuedTimeoutCallbacks(); assert.equal(moduleSpecifierCache.count(), 0); }); - it("invalidates the cache when package.json changes", () => { + it("invalidates the cache when local package.json changes", () => { const { host, moduleSpecifierCache } = setup(); host.writeFile("/package.json", `{}`); host.runQueuedTimeoutCallbacks(); - assert.isUndefined(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path)); + assert.equal(moduleSpecifierCache.count(), 0); + }); + + it("invalidates the cache when module resolution settings change", () => { + const { host, moduleSpecifierCache } = setup(); + host.writeFile(tsconfig.path, `{ "compilerOptions": { "moduleResolution": "classic" }, "include": ["src"] }`); + host.runQueuedTimeoutCallbacks(); + assert.equal(moduleSpecifierCache.count(), 0); + }); + + it("invalidates the cache when user preferences change", () => { + const { moduleSpecifierCache, session, triggerCompletions } = setup(); + const preferences: UserPreferences = { importModuleSpecifierPreference: "project-relative" }; + + assert.ok(getWithPreferences({})); + executeSessionRequest(session, protocol.CommandTypes.Configure, { preferences }); + // Nothing changes yet + assert.ok(getWithPreferences({})); + assert.isUndefined(getWithPreferences(preferences)); + // Completions will request (getting nothing) and set the cache with new preferences + triggerCompletions({ file: bTs.path, line: 1, offset: 3 }); + assert.isUndefined(getWithPreferences({})); + assert.ok(getWithPreferences(preferences)); + + // Test other affecting preference + executeSessionRequest(session, protocol.CommandTypes.Configure, { + preferences: { importModuleSpecifierEnding: "js" }, + }); + triggerCompletions({ file: bTs.path, line: 1, offset: 3 }); + assert.isUndefined(getWithPreferences(preferences)); + + function getWithPreferences(preferences: UserPreferences) { + return moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, preferences); + } }); }); function setup() { - const host = createServerHost([aTs, bTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]); + const host = createServerHost([aTs, bTs, cTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]); const session = createSession(host); - openFilesForSession([aTs, bTs], session); + openFilesForSession([aTs, bTs, cTs], session); const projectService = session.getProjectService(); const project = configuredProjectAt(projectService, 0); - triggerCompletions(); - return { host, project, projectService, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions }; + executeSessionRequest(session, protocol.CommandTypes.Configure, { + preferences: { + includeCompletionsForImportStatements: true, + includeCompletionsForModuleExports: true, + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: true, + }, + }); + triggerCompletions({ file: bTs.path, line: 1, offset: 3 }); + + return { host, project, projectService, session, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions }; - function triggerCompletions() { - const requestLocation: protocol.FileLocationRequestArgs = { - file: bTs.path, - line: 1, - offset: 3, - }; + function triggerCompletions(requestLocation: protocol.FileLocationRequestArgs) { executeSessionRequest(session, protocol.CommandTypes.CompletionInfo, { ...requestLocation, - includeExternalModuleExports: true, - prefix: "foo", }); } } diff --git a/src/testRunner/unittests/tsserver/projectReferences.ts b/src/testRunner/unittests/tsserver/projectReferences.ts index 2eba7833b76b0..fccad6ac94939 100644 --- a/src/testRunner/unittests/tsserver/projectReferences.ts +++ b/src/testRunner/unittests/tsserver/projectReferences.ts @@ -655,7 +655,7 @@ ${usage}` ); verify( - "when using arrow function as object literal property", + "when using arrow function as object literal property types", `export const foo = { bar: () => { } };`, `shared.foo.bar();`, "bar" @@ -676,8 +676,10 @@ instance.fly();`, "fly" ); + verify( - "when using arrow function as object literal property is loaded through indirect assignment with original declaration local to project is treated as local", + // when using arrow function as object literal property is loaded through indirect assignment with original declaration local to project is treated as local + "when using arrow function as object literal property", `const local = { bar: () => { } }; export const foo = local;`, `shared.foo.bar();`, diff --git a/src/testRunner/unittests/tsserver/symlinkCache.ts b/src/testRunner/unittests/tsserver/symlinkCache.ts new file mode 100644 index 0000000000000..39160c5399f23 --- /dev/null +++ b/src/testRunner/unittests/tsserver/symlinkCache.ts @@ -0,0 +1,80 @@ +namespace ts.projectSystem { + const appTsconfigJson: File = { + path: "/packages/app/tsconfig.json", + content: ` + { + "compilerOptions": { + "module": "commonjs", + "outDir": "dist", + "rootDir": "src", + "baseUrl": "." + } + "references": [{ "path": "../dep" }] + }` + }; + + const appSrcIndexTs: File = { + path: "/packages/app/src/index.ts", + content: `import "dep/does/not/exist";` + }; + + const depPackageJson: File = { + path: "/packages/dep/package.json", + content: `{ "name": "dep", "main": "dist/index.js", "types": "dist/index.d.ts" }` + }; + + const depTsconfigJson: File = { + path: "/packages/dep/tsconfig.json", + content: ` + { + "compilerOptions": { "outDir": "dist", "rootDir": "src", "module": "commonjs" } + }` + }; + + const depSrcIndexTs: File = { + path: "/packages/dep/src/index.ts", + content: ` + import "./sub/folder";` + }; + + const depSrcSubFolderIndexTs: File = { + path: "/packages/dep/src/sub/folder/index.ts", + content: `export const dep = 0;` + }; + + const link: SymLink = { + path: "/packages/app/node_modules/dep", + symLink: "../../dep", + }; + + describe("unittests:: tsserver:: symlinkCache", () => { + it("contains symlinks discovered by project references resolution after program creation", () => { + const { session, projectService } = setup(); + openFilesForSession([appSrcIndexTs], session); + const project = projectService.configuredProjects.get(appTsconfigJson.path)!; + assert.deepEqual( + project.getSymlinkCache()?.getSymlinkedDirectories()?.get(link.path + "/" as Path), + { real: "/packages/dep/", realPath: "/packages/dep/" as Path } + ); + }); + }); + + function setup() { + const host = createServerHost([ + appTsconfigJson, + appSrcIndexTs, + depPackageJson, + depTsconfigJson, + depSrcIndexTs, + depSrcSubFolderIndexTs, + link, + ]); + const session = createSession(host); + const projectService = session.getProjectService(); + return { + host, + projectService, + session, + }; + } +} diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e4d59e91e786c..e6cf8f2c8910f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5281,6 +5281,7 @@ declare namespace ts { * writeFileCallback */ writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + getCustomTransformers?: (project: string) => CustomTransformers | undefined; getModifiedTime(fileName: string): Date | undefined; setModifiedTime(fileName: string, date: Date): void; deleteFile(fileName: string): void; @@ -10000,7 +10001,7 @@ declare namespace ts.server { errors: Diagnostic[] | undefined; } export class ProjectService { - private readonly scriptInfoInNodeModulesWatchers; + private readonly nodeModulesWatchers; /** * Contains all the deleted script info's version information so that * it does not reset when creating script info again @@ -10150,6 +10151,7 @@ declare namespace ts.server { private createInferredProject; getScriptInfo(uncheckedFileName: string): ScriptInfo | undefined; private watchClosedScriptInfo; + private createNodeModulesWatcher; private watchClosedScriptInfoInNodeModules; private getModifiedTime; private refreshScriptInfo; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 967e947f1f444..14908f24da37f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5281,6 +5281,7 @@ declare namespace ts { * writeFileCallback */ writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + getCustomTransformers?: (project: string) => CustomTransformers | undefined; getModifiedTime(fileName: string): Date | undefined; setModifiedTime(fileName: string, date: Date): void; deleteFile(fileName: string): void; diff --git a/tests/baselines/reference/extractFunction/extractFunction10.ts b/tests/baselines/reference/extractFunction/extractFunction10.ts index 99651f03e88d1..539a0be78b861 100644 --- a/tests/baselines/reference/extractFunction/extractFunction10.ts +++ b/tests/baselines/reference/extractFunction/extractFunction10.ts @@ -9,6 +9,21 @@ namespace A { } } } +// ==SCOPE::Extract to inner function in method 'a'== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return /*RENAME*/newFunction(); + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } + } +} // ==SCOPE::Extract to method in class 'C'== namespace A { export interface I { x: number }; diff --git a/tests/baselines/reference/extractFunction/extractFunction11.ts b/tests/baselines/reference/extractFunction/extractFunction11.ts index baca3914b039c..b1c60592269ad 100644 --- a/tests/baselines/reference/extractFunction/extractFunction11.ts +++ b/tests/baselines/reference/extractFunction/extractFunction11.ts @@ -11,6 +11,23 @@ namespace A { } } } +// ==SCOPE::Extract to inner function in method 'a'== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + return /*RENAME*/newFunction(); + + function newFunction() { + let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10; + } + } + } +} // ==SCOPE::Extract to method in class 'C'== namespace A { let y = 1; diff --git a/tests/baselines/reference/extractFunction/extractFunction12.ts b/tests/baselines/reference/extractFunction/extractFunction12.ts index 49e87151b9ba7..f9215e9b36fe3 100644 --- a/tests/baselines/reference/extractFunction/extractFunction12.ts +++ b/tests/baselines/reference/extractFunction/extractFunction12.ts @@ -13,6 +13,25 @@ namespace A { } } } +// ==SCOPE::Extract to inner function in method 'a'== +namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + return /*RENAME*/newFunction(); + + function newFunction() { + let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return a1.x + 10; + } + } + } +} // ==SCOPE::Extract to method in class 'C'== namespace A { let y = 1; diff --git a/tests/baselines/reference/extractFunction/extractFunction13.ts b/tests/baselines/reference/extractFunction/extractFunction13.ts index e39476160c567..647c72a009f5e 100644 --- a/tests/baselines/reference/extractFunction/extractFunction13.ts +++ b/tests/baselines/reference/extractFunction/extractFunction13.ts @@ -14,6 +14,26 @@ } } } +// ==SCOPE::Extract to inner function in arrow function== +(u1a: U1a, u1b: U1b) => { + function F1(t1a: T1a, t1b: T1b) { + (u2a: U2a, u2b: U2b) => { + function F2(t2a: T2a, t2b: T2b) { + (u3a: U3a, u3b: U3b) => { + /*RENAME*/newFunction(); + + function newFunction() { + t1a.toString(); + t2a.toString(); + u1a.toString(); + u2a.toString(); + u3a.toString(); + } + } + } + } + } +} // ==SCOPE::Extract to inner function in function 'F2'== (u1a: U1a, u1b: U1b) => { function F1(t1a: T1a, t1b: T1b) { diff --git a/tests/baselines/reference/extractFunction/extractFunction17.ts b/tests/baselines/reference/extractFunction/extractFunction17.ts index 45d9953fb9504..5f86f013cde1e 100644 --- a/tests/baselines/reference/extractFunction/extractFunction17.ts +++ b/tests/baselines/reference/extractFunction/extractFunction17.ts @@ -4,6 +4,16 @@ class C { /*[#|*/t1.toString()/*|]*/; } } +// ==SCOPE::Extract to inner function in method 'M'== +class C { + M(t1: T1, t2: T2) { + /*RENAME*/newFunction(); + + function newFunction() { + t1.toString(); + } + } +} // ==SCOPE::Extract to method in class 'C'== class C { M(t1: T1, t2: T2) { diff --git a/tests/baselines/reference/extractFunction/extractFunction18.ts b/tests/baselines/reference/extractFunction/extractFunction18.ts index bdfcce6bbdc69..4f5e08264cd3f 100644 --- a/tests/baselines/reference/extractFunction/extractFunction18.ts +++ b/tests/baselines/reference/extractFunction/extractFunction18.ts @@ -4,6 +4,16 @@ class C { /*[#|*/t1.toString()/*|]*/; } } +// ==SCOPE::Extract to inner function in method 'M'== +class C { + M(t1: T1, t2: T2) { + /*RENAME*/newFunction(); + + function newFunction() { + t1.toString(); + } + } +} // ==SCOPE::Extract to method in class 'C'== class C { M(t1: T1, t2: T2) { diff --git a/tests/baselines/reference/extractFunction/extractFunction20.js b/tests/baselines/reference/extractFunction/extractFunction20.js index 17bef1c604450..db847b528d64d 100644 --- a/tests/baselines/reference/extractFunction/extractFunction20.js +++ b/tests/baselines/reference/extractFunction/extractFunction20.js @@ -5,6 +5,17 @@ const _ = class { return a1.x + 10;/*|]*/ } } +// ==SCOPE::Extract to inner function in method 'a'== +const _ = class { + a() { + return /*RENAME*/newFunction(); + + function newFunction() { + let a1 = { x: 1 }; + return a1.x + 10; + } + } +} // ==SCOPE::Extract to method in anonymous class expression== const _ = class { a() { diff --git a/tests/baselines/reference/extractFunction/extractFunction20.ts b/tests/baselines/reference/extractFunction/extractFunction20.ts index ce09d4457d30a..041727e60dfbf 100644 --- a/tests/baselines/reference/extractFunction/extractFunction20.ts +++ b/tests/baselines/reference/extractFunction/extractFunction20.ts @@ -5,6 +5,17 @@ const _ = class { return a1.x + 10;/*|]*/ } } +// ==SCOPE::Extract to inner function in method 'a'== +const _ = class { + a() { + return /*RENAME*/newFunction(); + + function newFunction() { + let a1 = { x: 1 }; + return a1.x + 10; + } + } +} // ==SCOPE::Extract to method in anonymous class expression== const _ = class { a() { diff --git a/tests/baselines/reference/extractFunction/extractFunction26.js b/tests/baselines/reference/extractFunction/extractFunction26.js index 2c821c05ad107..1c95e4d1006df 100644 --- a/tests/baselines/reference/extractFunction/extractFunction26.js +++ b/tests/baselines/reference/extractFunction/extractFunction26.js @@ -6,6 +6,18 @@ class C { } M3() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + M3() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction26.ts b/tests/baselines/reference/extractFunction/extractFunction26.ts index 300686c12abca..088b0144bc1c5 100644 --- a/tests/baselines/reference/extractFunction/extractFunction26.ts +++ b/tests/baselines/reference/extractFunction/extractFunction26.ts @@ -6,6 +6,18 @@ class C { } M3() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + M3() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction27.js b/tests/baselines/reference/extractFunction/extractFunction27.js index 702127b9d76ca..a23c67d410f26 100644 --- a/tests/baselines/reference/extractFunction/extractFunction27.js +++ b/tests/baselines/reference/extractFunction/extractFunction27.js @@ -7,6 +7,19 @@ class C { constructor() { } M3() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + constructor() { } + M3() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction27.ts b/tests/baselines/reference/extractFunction/extractFunction27.ts index 1acbe67707a30..e20b70e3e765b 100644 --- a/tests/baselines/reference/extractFunction/extractFunction27.ts +++ b/tests/baselines/reference/extractFunction/extractFunction27.ts @@ -7,6 +7,19 @@ class C { constructor() { } M3() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + constructor() { } + M3() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction28.js b/tests/baselines/reference/extractFunction/extractFunction28.js index cf0742626dd98..46b237b744402 100644 --- a/tests/baselines/reference/extractFunction/extractFunction28.js +++ b/tests/baselines/reference/extractFunction/extractFunction28.js @@ -7,6 +7,19 @@ class C { M3() { } constructor() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + M3() { } + constructor() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction28.ts b/tests/baselines/reference/extractFunction/extractFunction28.ts index f15d7956f1c00..3553c416af1dc 100644 --- a/tests/baselines/reference/extractFunction/extractFunction28.ts +++ b/tests/baselines/reference/extractFunction/extractFunction28.ts @@ -7,6 +7,19 @@ class C { M3() { } constructor() { } } +// ==SCOPE::Extract to inner function in method 'M2'== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + M3() { } + constructor() { } +} // ==SCOPE::Extract to method in class 'C'== class C { M1() { } diff --git a/tests/baselines/reference/extractFunction/extractFunction31.ts b/tests/baselines/reference/extractFunction/extractFunction31.ts index 2dea17689b6fb..05a74deb8628b 100644 --- a/tests/baselines/reference/extractFunction/extractFunction31.ts +++ b/tests/baselines/reference/extractFunction/extractFunction31.ts @@ -10,6 +10,22 @@ namespace N { }/*|]*/ } } +// ==SCOPE::Extract to inner function in arrow function== +namespace N { + + export const value = 1; + + () => { + var f: () => number; + /*RENAME*/newFunction(); + + function newFunction() { + f = function(): number { + return value; + }; + } + } +} // ==SCOPE::Extract to function in namespace 'N'== namespace N { diff --git a/tests/baselines/reference/extractFunction/extractFunction32.ts b/tests/baselines/reference/extractFunction/extractFunction32.ts index acd5ac50804d2..75a16495cfcb1 100644 --- a/tests/baselines/reference/extractFunction/extractFunction32.ts +++ b/tests/baselines/reference/extractFunction/extractFunction32.ts @@ -11,6 +11,23 @@ namespace N { }/*|]*/ } } +// ==SCOPE::Extract to inner function in arrow function== +namespace N { + + export const value = 1; + + () => { + var c = /*RENAME*/newFunction() + + function newFunction() { + return class { + M() { + return value; + } + }; + } + } +} // ==SCOPE::Extract to function in namespace 'N'== namespace N { diff --git a/tests/baselines/reference/extractFunction/extractFunction34.js b/tests/baselines/reference/extractFunction/extractFunction34.js new file mode 100644 index 0000000000000..e97dec0b986c2 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction34.js @@ -0,0 +1,20 @@ +// ==ORIGINAL== +const F = () => { + /*[#|*/function G() { }/*|]*/ +}; +// ==SCOPE::Extract to inner function in arrow function== +const F = () => { + /*RENAME*/newFunction(); + + function newFunction() { + function G() { } + } +}; +// ==SCOPE::Extract to function in global scope== +const F = () => { + /*RENAME*/newFunction(); +}; + +function newFunction() { + function G() { } +} diff --git a/tests/baselines/reference/extractFunction/extractFunction34.ts b/tests/baselines/reference/extractFunction/extractFunction34.ts new file mode 100644 index 0000000000000..e97dec0b986c2 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction34.ts @@ -0,0 +1,20 @@ +// ==ORIGINAL== +const F = () => { + /*[#|*/function G() { }/*|]*/ +}; +// ==SCOPE::Extract to inner function in arrow function== +const F = () => { + /*RENAME*/newFunction(); + + function newFunction() { + function G() { } + } +}; +// ==SCOPE::Extract to function in global scope== +const F = () => { + /*RENAME*/newFunction(); +}; + +function newFunction() { + function G() { } +} diff --git a/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.js b/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.js index f0c42044996d7..e5b3ba0efb94c 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.js +++ b/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.js @@ -5,6 +5,17 @@ export default class { /*[#|*/1 + 1/*|]*/; } } +// ==SCOPE::Extract to inner function in method 'M'== + +export default class { + M() { + /*RENAME*/newFunction(); + + function newFunction() { + 1 + 1; + } + } +} // ==SCOPE::Extract to method in anonymous class declaration== export default class { diff --git a/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.ts b/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.ts index 9905cd08c743f..7ea8c9cebdb66 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_NamelessClass.ts @@ -5,6 +5,17 @@ export default class { /*[#|*/1 + 1/*|]*/; } } +// ==SCOPE::Extract to inner function in method 'M'== + +export default class { + M() { + /*RENAME*/newFunction(); + + function newFunction() { + 1 + 1; + } + } +} // ==SCOPE::Extract to method in anonymous class declaration== export default class { diff --git a/tests/baselines/reference/override_js4.errors.txt b/tests/baselines/reference/override_js4.errors.txt new file mode 100644 index 0000000000000..5b9b5b97f33b9 --- /dev/null +++ b/tests/baselines/reference/override_js4.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/override/a.js(7,5): error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'A'. Did you mean 'doSomething'? + + +==== tests/cases/conformance/override/a.js (1 errors) ==== + class A { + doSomething() {} + } + + class B extends A { + /** @override */ + doSomethang() {} + ~~~~~~~~~~~ +!!! error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'A'. Did you mean 'doSomething'? + } + \ No newline at end of file diff --git a/tests/baselines/reference/override_js4.symbols b/tests/baselines/reference/override_js4.symbols new file mode 100644 index 0000000000000..a35e801b73bf6 --- /dev/null +++ b/tests/baselines/reference/override_js4.symbols @@ -0,0 +1,17 @@ +=== tests/cases/conformance/override/a.js === +class A { +>A : Symbol(A, Decl(a.js, 0, 0)) + + doSomething() {} +>doSomething : Symbol(A.doSomething, Decl(a.js, 0, 9)) +} + +class B extends A { +>B : Symbol(B, Decl(a.js, 2, 1)) +>A : Symbol(A, Decl(a.js, 0, 0)) + + /** @override */ + doSomethang() {} +>doSomethang : Symbol(B.doSomethang, Decl(a.js, 4, 19)) +} + diff --git a/tests/baselines/reference/override_js4.types b/tests/baselines/reference/override_js4.types new file mode 100644 index 0000000000000..270d34fafa23c --- /dev/null +++ b/tests/baselines/reference/override_js4.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/override/a.js === +class A { +>A : A + + doSomething() {} +>doSomething : () => void +} + +class B extends A { +>B : B +>A : A + + /** @override */ + doSomethang() {} +>doSomethang : () => void +} + diff --git a/tests/baselines/reference/tsbuild/watchMode/publicApi/with-custom-transformers.js b/tests/baselines/reference/tsbuild/watchMode/publicApi/with-custom-transformers.js new file mode 100644 index 0000000000000..cb6cf6f589af1 --- /dev/null +++ b/tests/baselines/reference/tsbuild/watchMode/publicApi/with-custom-transformers.js @@ -0,0 +1,383 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + +//// [/user/username/projects/myproject/tsconfig.json] +{"references":[{"path":"./shared/tsconfig.json"},{"path":"./webpack/tsconfig.json"}],"files":[]} + +//// [/user/username/projects/myproject/shared/tsconfig.json] +{"compilerOptions":{"composite":true}} + +//// [/user/username/projects/myproject/shared/index.ts] +export function f1() { } +export class c { } +export enum e { } +// leading +export function f2() { } // trailing + +//// [/user/username/projects/myproject/webpack/tsconfig.json] +{"compilerOptions":{"composite":true},"references":[{"path":"../shared/tsconfig.json"}]} + +//// [/user/username/projects/myproject/webpack/index.ts] +export function f2() { } +export class c2 { } +export enum e2 { } +// leading +export function f22() { } // trailing + + +/a/lib/tsc.js --b --w +Output:: +[12:00:31 AM] Projects in this build: + * shared/tsconfig.json + * webpack/tsconfig.json + * tsconfig.json + +[12:00:32 AM] Project 'shared/tsconfig.json' is out of date because output file 'shared/index.js' does not exist + +[12:00:33 AM] Building project '/user/username/projects/myproject/shared/tsconfig.json'... + +[12:00:42 AM] Project 'webpack/tsconfig.json' is out of date because output file 'webpack/index.js' does not exist + +[12:00:43 AM] Building project '/user/username/projects/myproject/webpack/tsconfig.json'... + +[12:00:52 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/shared/index.ts"] +Program options: {"composite":true,"configFilePath":"/user/username/projects/myproject/shared/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/shared/index.ts + +Semantic diagnostics in builder refreshed for:: +/a/lib/lib.d.ts +/user/username/projects/myproject/shared/index.ts + +Program root files: ["/user/username/projects/myproject/webpack/index.ts"] +Program options: {"composite":true,"configFilePath":"/user/username/projects/myproject/webpack/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/webpack/index.ts + +Semantic diagnostics in builder refreshed for:: +/a/lib/lib.d.ts +/user/username/projects/myproject/webpack/index.ts + +WatchedFiles:: +/user/username/projects/myproject/shared/tsconfig.json: + {"fileName":"/user/username/projects/myproject/shared/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/shared/index.ts: + {"fileName":"/user/username/projects/myproject/shared/index.ts","pollingInterval":250} +/user/username/projects/myproject/webpack/tsconfig.json: + {"fileName":"/user/username/projects/myproject/webpack/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/webpack/index.ts: + {"fileName":"/user/username/projects/myproject/webpack/index.ts","pollingInterval":250} +/user/username/projects/myproject/tsconfig.json: + {"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} + +FsWatches:: + +FsWatchesRecursive:: +/user/username/projects/myproject/shared: + {"directoryName":"/user/username/projects/myproject/shared","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} +/user/username/projects/myproject/webpack: + {"directoryName":"/user/username/projects/myproject/webpack","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/shared/index.js] +"use strict"; +exports.__esModule = true; +exports.f2 = exports.e = exports.c = exports.f1 = void 0; +/*@before/user/username/projects/myproject/shared/tsconfig.json*/ +function f1() { } +exports.f1 = f1; +//@after/user/username/projects/myproject/shared/tsconfig.json +var c = /** @class */ (function () { + function c() { + } + return c; +}()); +exports.c = c; +//@after/user/username/projects/myproject/shared/tsconfig.json +var e; +(function (e) { +})(e = exports.e || (exports.e = {})); +// leading +/*@before/user/username/projects/myproject/shared/tsconfig.json*/ +function f2() { } // trailing +exports.f2 = f2; + + +//// [/user/username/projects/myproject/shared/index.d.ts] +export declare function f1(): void; +export declare class c { +} +export declare enum e { +} +export declare function f2(): void; + + +//// [/user/username/projects/myproject/shared/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../../../../a/lib/lib.d.ts","./index.ts"],"fileInfos":[{"version":"-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }","affectsGlobalScope":true},"8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing"],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2]},"version":"FakeTSVersion"} + +//// [/user/username/projects/myproject/shared/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ], + "fileInfos": { + "../../../../../a/lib/lib.d.ts": { + "version": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "signature": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "affectsGlobalScope": true + }, + "./index.ts": { + "version": "8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing", + "signature": "8649344783-export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ] + }, + "version": "FakeTSVersion", + "size": 745 +} + +//// [/user/username/projects/myproject/webpack/index.js] +"use strict"; +exports.__esModule = true; +exports.f22 = exports.e2 = exports.c2 = exports.f2 = void 0; +/*@before/user/username/projects/myproject/webpack/tsconfig.json*/ +function f2() { } +exports.f2 = f2; +//@after/user/username/projects/myproject/webpack/tsconfig.json +var c2 = /** @class */ (function () { + function c2() { + } + return c2; +}()); +exports.c2 = c2; +//@after/user/username/projects/myproject/webpack/tsconfig.json +var e2; +(function (e2) { +})(e2 = exports.e2 || (exports.e2 = {})); +// leading +/*@before/user/username/projects/myproject/webpack/tsconfig.json*/ +function f22() { } // trailing +exports.f22 = f22; + + +//// [/user/username/projects/myproject/webpack/index.d.ts] +export declare function f2(): void; +export declare class c2 { +} +export declare enum e2 { +} +export declare function f22(): void; + + +//// [/user/username/projects/myproject/webpack/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../../../../a/lib/lib.d.ts","./index.ts"],"fileInfos":[{"version":"-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }","affectsGlobalScope":true},"20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing"],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2]},"version":"FakeTSVersion"} + +//// [/user/username/projects/myproject/webpack/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ], + "fileInfos": { + "../../../../../a/lib/lib.d.ts": { + "version": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "signature": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "affectsGlobalScope": true + }, + "./index.ts": { + "version": "20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing", + "signature": "20140662566-export function f2() { }\nexport class c2 { }\nexport enum e2 { }\n// leading\nexport function f22() { } // trailing" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ] + }, + "version": "FakeTSVersion", + "size": 749 +} + + +Change:: change to shared + +Input:: +//// [/user/username/projects/myproject/shared/index.ts] +export function fooBar() {}export function f1() { } +export class c { } +export enum e { } +// leading +export function f2() { } // trailing + + +Output:: +>> Screen clear +[12:00:55 AM] File change detected. Starting incremental compilation... + +[12:00:56 AM] Project 'shared/tsconfig.json' is out of date because oldest output 'shared/index.js' is older than newest input 'shared/index.ts' + +[12:00:57 AM] Building project '/user/username/projects/myproject/shared/tsconfig.json'... + +[12:01:10 AM] Project 'webpack/tsconfig.json' is out of date because oldest output 'webpack/index.js' is older than newest input 'shared/tsconfig.json' + +[12:01:11 AM] Building project '/user/username/projects/myproject/webpack/tsconfig.json'... + +[12:01:13 AM] Updating unchanged output timestamps of project '/user/username/projects/myproject/webpack/tsconfig.json'... + +[12:01:14 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/shared/index.ts"] +Program options: {"composite":true,"configFilePath":"/user/username/projects/myproject/shared/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/shared/index.ts + +Semantic diagnostics in builder refreshed for:: +/user/username/projects/myproject/shared/index.ts + +Program root files: ["/user/username/projects/myproject/webpack/index.ts"] +Program options: {"composite":true,"configFilePath":"/user/username/projects/myproject/webpack/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/webpack/index.ts + +Semantic diagnostics in builder refreshed for:: + +WatchedFiles:: +/user/username/projects/myproject/shared/tsconfig.json: + {"fileName":"/user/username/projects/myproject/shared/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/shared/index.ts: + {"fileName":"/user/username/projects/myproject/shared/index.ts","pollingInterval":250} +/user/username/projects/myproject/webpack/tsconfig.json: + {"fileName":"/user/username/projects/myproject/webpack/tsconfig.json","pollingInterval":250} +/user/username/projects/myproject/webpack/index.ts: + {"fileName":"/user/username/projects/myproject/webpack/index.ts","pollingInterval":250} +/user/username/projects/myproject/tsconfig.json: + {"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} + +FsWatches:: + +FsWatchesRecursive:: +/user/username/projects/myproject/shared: + {"directoryName":"/user/username/projects/myproject/shared","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} +/user/username/projects/myproject/webpack: + {"directoryName":"/user/username/projects/myproject/webpack","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/shared/index.js] +"use strict"; +exports.__esModule = true; +exports.f2 = exports.e = exports.c = exports.f1 = exports.fooBar = void 0; +/*@before/user/username/projects/myproject/shared/tsconfig.json*/ +function fooBar() { } +exports.fooBar = fooBar; +/*@before/user/username/projects/myproject/shared/tsconfig.json*/ +function f1() { } +exports.f1 = f1; +//@after/user/username/projects/myproject/shared/tsconfig.json +var c = /** @class */ (function () { + function c() { + } + return c; +}()); +exports.c = c; +//@after/user/username/projects/myproject/shared/tsconfig.json +var e; +(function (e) { +})(e = exports.e || (exports.e = {})); +// leading +/*@before/user/username/projects/myproject/shared/tsconfig.json*/ +function f2() { } // trailing +exports.f2 = f2; + + +//// [/user/username/projects/myproject/shared/index.d.ts] +export declare function fooBar(): void; +export declare function f1(): void; +export declare class c { +} +export declare enum e { +} +export declare function f2(): void; + + +//// [/user/username/projects/myproject/shared/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../../../../a/lib/lib.d.ts","./index.ts"],"fileInfos":[{"version":"-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }","affectsGlobalScope":true},{"version":"14127205977-export function fooBar() {}export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing","signature":"1966424426-export declare function fooBar(): void;\nexport declare function f1(): void;\nexport declare class c {\n}\nexport declare enum e {\n}\nexport declare function f2(): void;\n"}],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2]},"version":"FakeTSVersion"} + +//// [/user/username/projects/myproject/shared/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ], + "fileInfos": { + "../../../../../a/lib/lib.d.ts": { + "version": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "signature": "-7698705165-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }", + "affectsGlobalScope": true + }, + "./index.ts": { + "version": "14127205977-export function fooBar() {}export function f1() { }\nexport class c { }\nexport enum e { }\n// leading\nexport function f2() { } // trailing", + "signature": "1966424426-export declare function fooBar(): void;\nexport declare function f1(): void;\nexport declare class c {\n}\nexport declare enum e {\n}\nexport declare function f2(): void;\n" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../../../../a/lib/lib.d.ts", + "./index.ts" + ] + }, + "version": "FakeTSVersion", + "size": 983 +} + +//// [/user/username/projects/myproject/webpack/index.js] file changed its modified time +//// [/user/username/projects/myproject/webpack/index.d.ts] file changed its modified time +//// [/user/username/projects/myproject/webpack/tsconfig.tsbuildinfo] file changed its modified time diff --git a/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js b/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js index ed371ee59a548..a5b8f28ac9430 100644 --- a/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js +++ b/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js @@ -18,8 +18,8 @@ DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/src 1 u Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/src 1 undefined Config: /users/username/projects/myproject/tsconfig.json WatchType: Wild card directory Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded Starting updateGraphWorker: Project: /users/username/projects/myproject/tsconfig.json -DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them +DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined Project: /users/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined Project: /users/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info diff --git a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js index cd9a7a1343134..584478548a45f 100644 --- a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js +++ b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js @@ -222,8 +222,8 @@ Scheduled: /user/username/projects/myproject/tsconfig.json, Cancelled earlier on Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one Running: /user/username/projects/myproject/tsconfig.json Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms Project '/user/username/projects/myproject/tsconfig.json' (Configured) Files (3) diff --git a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js index 1346caf1e8b87..9fb71d1254428 100644 --- a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js +++ b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js @@ -245,8 +245,8 @@ Scheduled: /user/username/projects/myproject/tsconfig.json, Cancelled earlier on Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one Running: /user/username/projects/myproject/tsconfig.json Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms Project '/user/username/projects/myproject/tsconfig.json' (Configured) Files (3) diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js index e6199451d9dcf..d7b7c45b64f85 100644 --- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js +++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js @@ -96,4 +96,6 @@ Open files: Projects: /user/username/projects/myproject/app/src/program/tsconfig.json response:{"responseRequired":false} request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"} +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true} \ No newline at end of file diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js index 74bfa4b020392..6d2e6ff6b9038 100644 --- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js +++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js @@ -95,4 +95,6 @@ Open files: Projects: /user/username/projects/myproject/app/src/program/tsconfig.json response:{"responseRequired":false} request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"} +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true} \ No newline at end of file diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js index 74bfa4b020392..6d2e6ff6b9038 100644 --- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js +++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js @@ -95,4 +95,6 @@ Open files: Projects: /user/username/projects/myproject/app/src/program/tsconfig.json response:{"responseRequired":false} request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"} +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true} \ No newline at end of file diff --git a/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-is-loaded-through-indirect-assignment-with-original-declaration-local-to-project-is-treated-as-local.js b/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-types.js similarity index 59% rename from tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-is-loaded-through-indirect-assignment-with-original-declaration-local-to-project-is-treated-as-local.js rename to tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-types.js index 002fd13a1027c..05dbc65c8940c 100644 --- a/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-is-loaded-through-indirect-assignment-with-original-declaration-local-to-project-is-treated-as-local.js +++ b/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property-types.js @@ -106,4 +106,75 @@ Project '/user/username/projects/solution/shared/tsconfig.json' (Configured) ----------------------------------------------- Search path: /user/username/projects/solution/shared/src For info: /user/username/projects/solution/shared/src/index.ts :: Config file name: /user/username/projects/solution/shared/tsconfig.json -response:{"response":{"refs":[{"file":"/user/username/projects/solution/shared/src/index.ts","start":{"line":1,"offset":17},"end":{"line":1,"offset":20},"contextStart":{"line":1,"offset":17},"contextEnd":{"line":1,"offset":31},"lineText":"const local = { bar: () => { } };","isWriteAccess":true,"isDefinition":true},{"file":"/user/username/projects/solution/api/src/server.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false}],"symbolName":"bar","symbolStartOffset":12,"symbolDisplayString":"(property) bar: () => void"},"responseRequired":true} \ No newline at end of file +Loading configured project /user/username/projects/solution/tsconfig.json +Config: /user/username/projects/solution/tsconfig.json : { + "rootNames": [], + "options": { + "configFilePath": "/user/username/projects/solution/tsconfig.json" + }, + "projectReferences": [ + { + "path": "/user/username/projects/solution/api", + "originalPath": "./api" + }, + { + "path": "/user/username/projects/solution/app", + "originalPath": "./app" + } + ] +} +Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded +Starting updateGraphWorker: Project: /user/username/projects/solution/tsconfig.json +Config: /user/username/projects/solution/app/tsconfig.json : { + "rootNames": [ + "/user/username/projects/solution/app/src/app.ts" + ], + "options": { + "composite": true, + "outDir": "/user/username/projects/solution/app/dist", + "rootDir": "/user/username/projects/solution/app/src", + "configFilePath": "/user/username/projects/solution/app/tsconfig.json" + }, + "projectReferences": [ + { + "path": "/user/username/projects/solution/shared", + "originalPath": "../shared" + } + ] +} +FileWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/tsconfig.json 2000 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Config file +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src 1 undefined Config: /user/username/projects/solution/app/tsconfig.json WatchType: Wild card directory +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src 1 undefined Config: /user/username/projects/solution/app/tsconfig.json WatchType: Wild card directory +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Type roots +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Type roots +Finishing updateGraphWorker: Project: /user/username/projects/solution/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Different program with same set of files +Creating configuration project /user/username/projects/solution/app/tsconfig.json +Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded +FileWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src/app.ts 500 undefined WatchType: Closed Script info +Starting updateGraphWorker: Project: /user/username/projects/solution/app/tsconfig.json +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/shared 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Failed Lookup Locations +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/shared 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Failed Lookup Locations +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots +Finishing updateGraphWorker: Project: /user/username/projects/solution/app/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Project '/user/username/projects/solution/app/tsconfig.json' (Configured) + Files (3) + /a/lib/lib.d.ts + /user/username/projects/solution/shared/src/index.ts + /user/username/projects/solution/app/src/app.ts + + + ../../../../../a/lib/lib.d.ts + Default library for target 'es3' + ../shared/src/index.ts + Imported via "../../shared/dist" from file 'src/app.ts' + src/app.ts + Matched by include pattern 'src' in 'tsconfig.json' + +----------------------------------------------- +Search path: /user/username/projects/solution/shared/src +For info: /user/username/projects/solution/shared/src/index.ts :: Config file name: /user/username/projects/solution/shared/tsconfig.json +response:{"response":{"refs":[{"file":"/user/username/projects/solution/shared/src/index.ts","start":{"line":1,"offset":22},"end":{"line":1,"offset":25},"contextStart":{"line":1,"offset":22},"contextEnd":{"line":1,"offset":36},"lineText":"export const foo = { bar: () => { } };","isWriteAccess":true,"isDefinition":true},{"file":"/user/username/projects/solution/api/src/server.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false},{"file":"/user/username/projects/solution/app/src/app.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false}],"symbolName":"bar","symbolStartOffset":12,"symbolDisplayString":"(property) bar: () => void"},"responseRequired":true} \ No newline at end of file diff --git a/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property.js b/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property.js index 05dbc65c8940c..002fd13a1027c 100644 --- a/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property.js +++ b/tests/baselines/reference/tsserver/projectReferences/special-handling-of-localness-when-using-arrow-function-as-object-literal-property.js @@ -106,75 +106,4 @@ Project '/user/username/projects/solution/shared/tsconfig.json' (Configured) ----------------------------------------------- Search path: /user/username/projects/solution/shared/src For info: /user/username/projects/solution/shared/src/index.ts :: Config file name: /user/username/projects/solution/shared/tsconfig.json -Loading configured project /user/username/projects/solution/tsconfig.json -Config: /user/username/projects/solution/tsconfig.json : { - "rootNames": [], - "options": { - "configFilePath": "/user/username/projects/solution/tsconfig.json" - }, - "projectReferences": [ - { - "path": "/user/username/projects/solution/api", - "originalPath": "./api" - }, - { - "path": "/user/username/projects/solution/app", - "originalPath": "./app" - } - ] -} -Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded -Starting updateGraphWorker: Project: /user/username/projects/solution/tsconfig.json -Config: /user/username/projects/solution/app/tsconfig.json : { - "rootNames": [ - "/user/username/projects/solution/app/src/app.ts" - ], - "options": { - "composite": true, - "outDir": "/user/username/projects/solution/app/dist", - "rootDir": "/user/username/projects/solution/app/src", - "configFilePath": "/user/username/projects/solution/app/tsconfig.json" - }, - "projectReferences": [ - { - "path": "/user/username/projects/solution/shared", - "originalPath": "../shared" - } - ] -} -FileWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/tsconfig.json 2000 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Config file -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src 1 undefined Config: /user/username/projects/solution/app/tsconfig.json WatchType: Wild card directory -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src 1 undefined Config: /user/username/projects/solution/app/tsconfig.json WatchType: Wild card directory -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Type roots -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/tsconfig.json WatchType: Type roots -Finishing updateGraphWorker: Project: /user/username/projects/solution/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms -Different program with same set of files -Creating configuration project /user/username/projects/solution/app/tsconfig.json -Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded -FileWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/src/app.ts 500 undefined WatchType: Closed Script info -Starting updateGraphWorker: Project: /user/username/projects/solution/app/tsconfig.json -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/shared 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Failed Lookup Locations -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/shared 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Failed Lookup Locations -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/app/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/solution/node_modules/@types 1 undefined Project: /user/username/projects/solution/app/tsconfig.json WatchType: Type roots -Finishing updateGraphWorker: Project: /user/username/projects/solution/app/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms -Project '/user/username/projects/solution/app/tsconfig.json' (Configured) - Files (3) - /a/lib/lib.d.ts - /user/username/projects/solution/shared/src/index.ts - /user/username/projects/solution/app/src/app.ts - - - ../../../../../a/lib/lib.d.ts - Default library for target 'es3' - ../shared/src/index.ts - Imported via "../../shared/dist" from file 'src/app.ts' - src/app.ts - Matched by include pattern 'src' in 'tsconfig.json' - ------------------------------------------------ -Search path: /user/username/projects/solution/shared/src -For info: /user/username/projects/solution/shared/src/index.ts :: Config file name: /user/username/projects/solution/shared/tsconfig.json -response:{"response":{"refs":[{"file":"/user/username/projects/solution/shared/src/index.ts","start":{"line":1,"offset":22},"end":{"line":1,"offset":25},"contextStart":{"line":1,"offset":22},"contextEnd":{"line":1,"offset":36},"lineText":"export const foo = { bar: () => { } };","isWriteAccess":true,"isDefinition":true},{"file":"/user/username/projects/solution/api/src/server.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false},{"file":"/user/username/projects/solution/app/src/app.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false}],"symbolName":"bar","symbolStartOffset":12,"symbolDisplayString":"(property) bar: () => void"},"responseRequired":true} \ No newline at end of file +response:{"response":{"refs":[{"file":"/user/username/projects/solution/shared/src/index.ts","start":{"line":1,"offset":17},"end":{"line":1,"offset":20},"contextStart":{"line":1,"offset":17},"contextEnd":{"line":1,"offset":31},"lineText":"const local = { bar: () => { } };","isWriteAccess":true,"isDefinition":true},{"file":"/user/username/projects/solution/api/src/server.ts","start":{"line":2,"offset":12},"end":{"line":2,"offset":15},"lineText":"shared.foo.bar();","isWriteAccess":false,"isDefinition":false}],"symbolName":"bar","symbolStartOffset":12,"symbolDisplayString":"(property) bar: () => void"},"responseRequired":true} \ No newline at end of file diff --git a/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js b/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js index c9833a1189cd4..a8baf59905f81 100644 --- a/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js +++ b/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js @@ -64,8 +64,8 @@ Elapsed:: *ms DirectoryWatcher:: Triggered with /a/b/projects/temp/node_modules/ Running: /dev/null/inferredProject1* Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one Starting updateGraphWorker: Project: /dev/null/inferredProject1* -DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos in them -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos in them +DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 2 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms Project '/dev/null/inferredProject1*' (Inferred) Files (3) diff --git a/tests/cases/conformance/override/override_js4.ts b/tests/cases/conformance/override/override_js4.ts new file mode 100644 index 0000000000000..f5a7f1b6c41eb --- /dev/null +++ b/tests/cases/conformance/override/override_js4.ts @@ -0,0 +1,14 @@ +// @noImplicitOverride: true +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: a.js +class A { + doSomething() {} +} + +class B extends A { + /** @override */ + doSomethang() {} +} diff --git a/tests/cases/fourslash/codeFixSpelling10.ts b/tests/cases/fourslash/codeFixSpelling10.ts new file mode 100644 index 0000000000000..3f81eebeff143 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpelling10.ts @@ -0,0 +1,16 @@ +/// + +// @noImplicitOverride: true +////class A { +//// doSomething() {} +////} +//// +////class B extends A { +//// override [|doSomethang|]() {} +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Change_spelling_to_0.message, "doSomething"], + newRangeContent: "doSomething" +}); diff --git a/tests/cases/fourslash/codeFixSpelling11.ts b/tests/cases/fourslash/codeFixSpelling11.ts new file mode 100644 index 0000000000000..dfea5370f01c8 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpelling11.ts @@ -0,0 +1,15 @@ +/// + +// @noImplicitOverride: true +////abstract class A { +//// abstract doSomething(): void; +////} +////abstract class B extends A { +//// abstract override [|doSomethang|](): number; +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Change_spelling_to_0.message, "doSomething"], + newRangeContent: "doSomething" +}); diff --git a/tests/cases/fourslash/codeFixSpelling12.ts b/tests/cases/fourslash/codeFixSpelling12.ts new file mode 100644 index 0000000000000..0b44201f8e386 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpelling12.ts @@ -0,0 +1,20 @@ +/// + +// @noImplicitOverride: true +// @allowJs: true +// @checkJs: true + +// @Filename: a.js +////class A { +//// doSomething() {} +////} +////class B extends A { +//// /** @override */ +//// [|doSomethang|]() {} +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Change_spelling_to_0.message, "doSomething"], + newRangeContent: "doSomething" +}); diff --git a/tests/cases/fourslash/extract-method20.ts b/tests/cases/fourslash/extract-method20.ts index bd137c55d1975..c64895dfc4d72 100644 --- a/tests/cases/fourslash/extract-method20.ts +++ b/tests/cases/fourslash/extract-method20.ts @@ -10,6 +10,6 @@ //// } goTo.select('a', 'b') -verify.not.refactorAvailable('Extract Symbol', 'function_scope_0'); +verify.refactorAvailable('Extract Symbol', 'function_scope_0'); verify.refactorAvailable('Extract Symbol', 'function_scope_1'); verify.not.refactorAvailable('Extract Symbol', 'function_scope_2'); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 2ceb0eb0897ab..26c3c220b7243 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -625,6 +625,8 @@ declare namespace FourSlashInterface { readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsForImportStatements?: boolean; readonly includeCompletionsWithSnippetText?: boolean; + readonly includeCompletionsWithInsertText?: boolean; + /** @deprecated use `includeCompletionsWithInsertText` */ readonly includeInsertTextCompletions?: boolean; readonly includeAutomaticOptionalChainCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; diff --git a/tests/cases/fourslash/importStatementCompletions1.ts b/tests/cases/fourslash/importStatementCompletions1.ts index c28aa81c7922b..6e08d5d3d46b9 100644 --- a/tests/cases/fourslash/importStatementCompletions1.ts +++ b/tests/cases/fourslash/importStatementCompletions1.ts @@ -23,6 +23,7 @@ [0, 1, 2, 3, 4, 5].forEach(marker => { verify.completions({ + isNewIdentifierLocation: true, marker: "" + marker, exact: [{ name: "foo", @@ -65,6 +66,7 @@ [6, 7, 8, 9, 10, 11, 12].forEach(marker => { verify.completions({ + isNewIdentifierLocation: true, marker: "" + marker, exact: [], preferences: { diff --git a/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts b/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts index a6d2083489b89..4d41ee5702119 100644 --- a/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts +++ b/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts @@ -10,6 +10,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts b/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts index c6a5b8e25e1f6..6d0d6ecd4dbf1 100644 --- a/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts +++ b/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts @@ -10,6 +10,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts b/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts index 78bad2241b0c8..f92eecec4937c 100644 --- a/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts +++ b/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts @@ -10,6 +10,7 @@ //// import style/**/ verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [], preferences: { diff --git a/tests/cases/fourslash/importStatementCompletions_noSnippet.ts b/tests/cases/fourslash/importStatementCompletions_noSnippet.ts index 8f87dba53bb39..7545caf97a537 100644 --- a/tests/cases/fourslash/importStatementCompletions_noSnippet.ts +++ b/tests/cases/fourslash/importStatementCompletions_noSnippet.ts @@ -7,6 +7,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_quotes.ts b/tests/cases/fourslash/importStatementCompletions_quotes.ts index 845dd0a205caf..e39b96a530266 100644 --- a/tests/cases/fourslash/importStatementCompletions_quotes.ts +++ b/tests/cases/fourslash/importStatementCompletions_quotes.ts @@ -8,6 +8,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_semicolons.ts b/tests/cases/fourslash/importStatementCompletions_semicolons.ts index fb61be82d790a..6674caee82965 100644 --- a/tests/cases/fourslash/importStatementCompletions_semicolons.ts +++ b/tests/cases/fourslash/importStatementCompletions_semicolons.ts @@ -8,6 +8,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/jsxAriaLikeCompletions.ts b/tests/cases/fourslash/jsxAriaLikeCompletions.ts new file mode 100644 index 0000000000000..8e325cc595111 --- /dev/null +++ b/tests/cases/fourslash/jsxAriaLikeCompletions.ts @@ -0,0 +1,21 @@ +/// +//@Filename: file.tsx +////declare var React: any; +////declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { "aria-whatever"?: string } +//// } +//// interface ElementAttributesProperty { props: any } +////} +////const a =
; + + +verify.completions( + { + marker: "1", + exact: [ + {name: "aria-whatever", sortText: completion.SortText.OptionalMember} + ] + } +); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment1.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment1.ts new file mode 100644 index 0000000000000..2acda7909b52a --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment1.ts @@ -0,0 +1,19 @@ +/// + +////const foo = /*a*/()/*b*/ => /** +//// * comment +//// */ +////1 + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function() { + /** + * comment + */ + return 1; +}` +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment2.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment2.ts new file mode 100644 index 0000000000000..8790718d61b13 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnonymous_Comment2.ts @@ -0,0 +1,15 @@ +/// + +////const foo = /*a*/()/*b*/ => // comment +////1 + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function() { + // comment + return 1; +}` +}); diff --git a/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts b/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts new file mode 100644 index 0000000000000..61eb984c7ced5 --- /dev/null +++ b/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts @@ -0,0 +1,15 @@ +/// + +// @Filename: /project/tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts +//// export declare function Component(): void; + +// @Filename: /project/index.ts +//// Component/**/ + +// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react + +goTo.marker(""); +verify.importFixAtPosition([`import { Component } from "react";\r\n\r\nComponent`]); diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts new file mode 100644 index 0000000000000..9e6d16ced2eab --- /dev/null +++ b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts @@ -0,0 +1,31 @@ +/// + +// @Filename: /project/tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts +//// export declare function Component(): void; + +// @Filename: /project/index.ts +//// [|import Com/**/|] + +// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react + +goTo.marker(""); +verify.completions({ + isNewIdentifierLocation: true, + marker: "", + exact: [{ + name: "Component", + source: "react", + insertText: `import { Component$1 } from "react";`, + isSnippet: true, + replacementSpan: test.ranges()[0], + sourceDisplay: "react", + }], + preferences: { + includeCompletionsForImportStatements: true, + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: true, + } +});