From d6a20316fea8d12ac8d71c1f0b36ae35a5a89938 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:37:55 +0200 Subject: [PATCH 01/12] breaking: svelte-check v4 (#2453) - breaking: make TypeScript a peer dependency - breaking: require node 18 or later - breaking: require Svelte 4 or later (devDependencies pinned to Svelte 3 because other packages in this repo still use it. Theoretically we still support Svelte 3 with svelte-check v4 but this gives us the opportunity to adjust that later without a major) - chore: switch from fast-glob to fdir (#2433) closes #2397 fixes #2364 --------- Co-authored-by: Simon Holthausen Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/language-server/package.json | 2 +- .../src/lib/documents/configLoader.ts | 27 ++- .../test/lib/documents/configLoader.test.ts | 44 ++-- packages/svelte-check/package.json | 16 +- packages/svelte-check/rollup.config.mjs | 1 - packages/svelte-check/src/index.ts | 46 +++- packages/svelte-check/src/options.ts | 20 +- pnpm-lock.yaml | 199 ++---------------- 8 files changed, 138 insertions(+), 217 deletions(-) diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 680e21419..f79ba9ede 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -50,7 +50,7 @@ "@vscode/emmet-helper": "2.8.4", "chokidar": "^3.4.1", "estree-walker": "^2.0.1", - "fast-glob": "^3.2.7", + "fdir": "^6.2.0", "lodash": "^4.17.21", "prettier": "~3.2.5", "prettier-plugin-svelte": "^3.2.2", diff --git a/packages/language-server/src/lib/documents/configLoader.ts b/packages/language-server/src/lib/documents/configLoader.ts index 4d7e583c6..49413a6f8 100644 --- a/packages/language-server/src/lib/documents/configLoader.ts +++ b/packages/language-server/src/lib/documents/configLoader.ts @@ -4,7 +4,7 @@ import { CompileOptions } from 'svelte/types/compiler/interfaces'; // @ts-ignore import { PreprocessorGroup } from 'svelte/types/compiler/preprocess'; import { importSveltePreprocess } from '../../importPackage'; -import _glob from 'fast-glob'; +import { fdir } from 'fdir'; import _path from 'path'; import _fs from 'fs'; import { pathToFileURL, URL } from 'url'; @@ -47,6 +47,8 @@ const _dynamicImport = new Function('modulePath', 'return import(modulePath)') a modulePath: URL ) => Promise; +const configRegex = /\/svelte\.config\.(js|cjs|mjs)$/; + /** * Loads svelte.config.{js,cjs,mjs} files. Provides both a synchronous and asynchronous * interface to get a config file because snapshots need access to it synchronously. @@ -61,7 +63,7 @@ export class ConfigLoader { private disabled = false; constructor( - private globSync: typeof _glob.sync, + private globSync: typeof fdir, private fs: Pick, private path: Pick, private dynamicImport: typeof _dynamicImport @@ -84,12 +86,19 @@ export class ConfigLoader { Logger.log('Trying to load configs for', directory); try { - const pathResults = this.globSync('**/svelte.config.{js,cjs,mjs}', { - cwd: directory, - // the second pattern is necessary because else fast-glob treats .tmp/../node_modules/.. as a valid match for some reason - ignore: ['**/node_modules/**', '**/.*/**'], - onlyFiles: true - }); + const pathResults = new this.globSync({}) + .withPathSeparator('/') + .exclude((_, path) => { + // no / at the start, path could start with node_modules + return path.includes('node_modules/') || path.includes('/.') || path[0] === '.'; + }) + .filter((path, isDir) => { + return !isDir && configRegex.test(path); + }) + .withRelativePaths() + .crawl(directory) + .sync(); + const someConfigIsImmediateFileInDirectory = pathResults.length > 0 && pathResults.some((res) => !this.path.dirname(res)); if (!someConfigIsImmediateFileInDirectory) { @@ -296,4 +305,4 @@ export class ConfigLoader { } } -export const configLoader = new ConfigLoader(_glob.sync, _fs, _path, _dynamicImport); +export const configLoader = new ConfigLoader(fdir, _fs, _path, _dynamicImport); diff --git a/packages/language-server/test/lib/documents/configLoader.test.ts b/packages/language-server/test/lib/documents/configLoader.test.ts index 1825a2ec9..dbdfb677a 100644 --- a/packages/language-server/test/lib/documents/configLoader.test.ts +++ b/packages/language-server/test/lib/documents/configLoader.test.ts @@ -19,6 +19,29 @@ describe('ConfigLoader', () => { return path.join(...filePath.split('/')); } + function mockFdir(results: string[] | (() => string[])): any { + return class { + withPathSeparator() { + return this; + } + exclude() { + return this; + } + filter() { + return this; + } + withRelativePaths() { + return this; + } + crawl() { + return this; + } + sync() { + return typeof results === 'function' ? results() : results; + } + }; + } + async function assertFindsConfig( configLoader: ConfigLoader, filePath: string, @@ -32,7 +55,7 @@ describe('ConfigLoader', () => { it('should load all config files below and the one inside/above given directory', async () => { const configLoader = new ConfigLoader( - (() => ['svelte.config.js', 'below/svelte.config.js']) as any, + mockFdir(['svelte.config.js', 'below/svelte.config.js']), { existsSync: () => true }, path, (module: URL) => Promise.resolve({ default: { preprocess: module.toString() } }) @@ -63,7 +86,7 @@ describe('ConfigLoader', () => { it('finds first above if none found inside/below directory', async () => { const configLoader = new ConfigLoader( - () => [], + mockFdir([]), { existsSync: (p) => typeof p === 'string' && p.endsWith(path.join('some', 'svelte.config.js')) @@ -78,7 +101,7 @@ describe('ConfigLoader', () => { it('adds fallback if no config found', async () => { const configLoader = new ConfigLoader( - () => [], + mockFdir([]), { existsSync: () => false }, path, (module: URL) => Promise.resolve({ default: { preprocess: module.toString() } }) @@ -98,14 +121,14 @@ describe('ConfigLoader', () => { let firstGlobCall = true; let nrImportCalls = 0; const configLoader = new ConfigLoader( - (() => { + mockFdir(() => { if (firstGlobCall) { firstGlobCall = false; return ['svelte.config.js']; } else { return []; } - }) as any, + }), { existsSync: (p) => typeof p === 'string' && @@ -139,11 +162,8 @@ describe('ConfigLoader', () => { }); it('can deal with missing config', () => { - const configLoader = new ConfigLoader( - () => [], - { existsSync: () => false }, - path, - () => Promise.resolve('unimportant') + const configLoader = new ConfigLoader(mockFdir([]), { existsSync: () => false }, path, () => + Promise.resolve('unimportant') ); assert.deepStrictEqual( configLoader.getConfig(normalizePath('/some/file.svelte')), @@ -153,7 +173,7 @@ describe('ConfigLoader', () => { it('should await config', async () => { const configLoader = new ConfigLoader( - () => [], + mockFdir([]), { existsSync: () => true }, path, (module: URL) => Promise.resolve({ default: { preprocess: module.toString() } }) @@ -167,7 +187,7 @@ describe('ConfigLoader', () => { it('should not load config when disabled', async () => { const moduleLoader = spy(); const configLoader = new ConfigLoader( - () => [], + mockFdir([]), { existsSync: () => true }, path, moduleLoader diff --git a/packages/svelte-check/package.json b/packages/svelte-check/package.json index 9d8309c46..361c2c33f 100644 --- a/packages/svelte-check/package.json +++ b/packages/svelte-check/package.json @@ -1,7 +1,7 @@ { "name": "svelte-check", "description": "Svelte Code Checker Terminal Interface", - "version": "3.0.0", + "version": "4.0.0", "main": "./dist/src/index.js", "bin": "./bin/svelte-check", "author": "The Svelte Community", @@ -22,16 +22,19 @@ "url": "https://github.com/sveltejs/language-tools/issues" }, "homepage": "https://github.com/sveltejs/language-tools#readme", + "engines": { + "node": ">= 18.0.0" + }, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "chokidar": "^3.4.1", + "fdir": "^6.2.0", "picocolors": "^1.0.0", - "sade": "^1.7.4", - "svelte-preprocess": "^5.1.3", - "typescript": "^5.0.3" + "sade": "^1.7.4" }, "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" }, "scripts": { "build": "rollup -c && node ./dist/src/index.js --workspace ./test --tsconfig ./tsconfig.json", @@ -46,11 +49,12 @@ "@rollup/plugin-typescript": "^10.0.0", "@types/sade": "^1.7.2", "builtin-modules": "^3.3.0", - "fast-glob": "^3.2.7", "rollup": "3.7.5", "rollup-plugin-cleanup": "^3.2.0", "rollup-plugin-copy": "^3.4.0", + "svelte": "^3.57.0", "svelte-language-server": "workspace:*", + "typescript": "^5.5.2", "vscode-languageserver": "8.0.2", "vscode-languageserver-protocol": "3.17.2", "vscode-languageserver-types": "3.17.2", diff --git a/packages/svelte-check/rollup.config.mjs b/packages/svelte-check/rollup.config.mjs index 2488f2bae..e5346edee 100644 --- a/packages/svelte-check/rollup.config.mjs +++ b/packages/svelte-check/rollup.config.mjs @@ -65,7 +65,6 @@ export default [ 'sade', 'svelte', 'svelte/compiler', - 'svelte-preprocess', '@jridgewell/trace-mapping' // import-fresh removed some time ago, no dependency uses it anymore. // if it creeps back in check if the dependency uses a version that diff --git a/packages/svelte-check/src/index.ts b/packages/svelte-check/src/index.ts index 6e64414ed..866d23bf3 100644 --- a/packages/svelte-check/src/index.ts +++ b/packages/svelte-check/src/index.ts @@ -4,7 +4,7 @@ import { watch } from 'chokidar'; import * as fs from 'fs'; -import glob from 'fast-glob'; +import { fdir } from 'fdir'; import * as path from 'path'; import { SvelteCheck, SvelteCheckOptions } from 'svelte-language-server'; import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-protocol'; @@ -30,11 +30,47 @@ async function openAllDocuments( filePathsToIgnore: string[], svelteCheck: SvelteCheck ) { - const files = await glob('**/*.svelte', { - cwd: workspaceUri.fsPath, - ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)) + const offset = workspaceUri.fsPath.length + 1; + // We support a very limited subset of glob patterns: You can only have ** at the end or the start + const ignored: Array<(path: string) => boolean> = filePathsToIgnore.map((i) => { + if (i.endsWith('**')) i = i.slice(0, -2); + + if (i.startsWith('**')) { + i = i.slice(2); + + if (i.includes('*')) + throw new Error( + 'Invalid svelte-check --ignore pattern: Only ** at the start or end is supported' + ); + + return (path) => path.includes(i); + } + + if (i.includes('*')) + throw new Error( + 'Invalid svelte-check --ignore pattern: Only ** at the start or end is supported' + ); + + return (path) => path.startsWith(i); }); - const absFilePaths = files.map((f) => path.resolve(workspaceUri.fsPath, f)); + const isIgnored = (path: string) => { + path = path.slice(offset); + for (const i of ignored) { + if (i(path)) { + return true; + } + } + return false; + }; + const absFilePaths = await new fdir() + .filter((path) => path.endsWith('.svelte') && !isIgnored(path)) + .exclude((_, path) => { + return path.includes('/node_modules/') || path.includes('/.'); + }) + .withPathSeparator('/') + .withFullPaths() + .crawl(workspaceUri.fsPath) + .withPromise(); for (const absFilePath of absFilePaths) { const text = fs.readFileSync(absFilePath, 'utf-8'); diff --git a/packages/svelte-check/src/options.ts b/packages/svelte-check/src/options.ts index bf270e3c4..b43dd7695 100644 --- a/packages/svelte-check/src/options.ts +++ b/packages/svelte-check/src/options.ts @@ -67,13 +67,19 @@ export function parseOptions(cb: (opts: SvelteCheckCliOptions) => any) { ) .action((opts) => { const workspaceUri = getWorkspaceUri(opts); + const tsconfig = getTsconfig(opts, workspaceUri.fsPath); + + if (opts.ignore && tsconfig) { + throwError('`--ignore` only has an effect when using `--no-tsconfig`'); + } + cb({ workspaceUri, outputFormat: getOutputFormat(opts), watch: !!opts.watch, preserveWatchOutput: !!opts.preserveWatchOutput, - tsconfig: getTsconfig(opts, workspaceUri.fsPath), - filePathsToIgnore: getFilepathsToIgnore(opts), + tsconfig, + filePathsToIgnore: opts.ignore?.split(',') || [], failOnWarnings: !!opts['fail-on-warnings'], compilerWarnings: getCompilerWarnings(opts), diagnosticSources: getDiagnosticSources(opts), @@ -141,11 +147,15 @@ function getTsconfig(myArgs: Record, workspacePath: string) { tsconfig = path.join(workspacePath, tsconfig); } if (tsconfig && !fs.existsSync(tsconfig)) { - throw new Error('Could not find tsconfig/jsconfig file at ' + myArgs.tsconfig); + throwError('Could not find tsconfig/jsconfig file at ' + myArgs.tsconfig); } return tsconfig; } +function throwError(msg: string) { + throw new Error('Invalid svelte-check CLI args: ' + msg); +} + function getCompilerWarnings(opts: Record) { return stringToObj(opts['compiler-warnings']); @@ -180,10 +190,6 @@ function getDiagnosticSources(opts: Record): DiagnosticSource[] { : diagnosticSources; } -function getFilepathsToIgnore(opts: Record): string[] { - return opts.ignore?.split(',') || []; -} - const thresholds = ['warning', 'error'] as const; type Threshold = (typeof thresholds)[number]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1e587871..2ebe4e95d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,9 +36,9 @@ importers: estree-walker: specifier: ^2.0.1 version: 2.0.2 - fast-glob: - specifier: ^3.2.7 - version: 3.2.12 + fdir: + specifier: ^6.2.0 + version: 6.2.0 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -118,21 +118,15 @@ importers: chokidar: specifier: ^3.4.1 version: 3.5.3 + fdir: + specifier: ^6.2.0 + version: 6.2.0 picocolors: specifier: ^1.0.0 version: 1.0.0 sade: specifier: ^1.7.4 version: 1.8.1 - svelte: - specifier: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 - version: 3.57.0 - svelte-preprocess: - specifier: ^5.1.3 - version: 5.1.3(svelte@3.57.0)(typescript@5.4.5) - typescript: - specifier: ^5.0.3 - version: 5.4.5 devDependencies: '@rollup/plugin-commonjs': specifier: ^24.0.0 @@ -148,16 +142,13 @@ importers: version: 5.0.2(rollup@3.7.5) '@rollup/plugin-typescript': specifier: ^10.0.0 - version: 10.0.1(rollup@3.7.5)(tslib@2.5.2)(typescript@5.4.5) + version: 10.0.1(rollup@3.7.5)(tslib@2.5.2)(typescript@5.5.2) '@types/sade': specifier: ^1.7.2 version: 1.7.4 builtin-modules: specifier: ^3.3.0 version: 3.3.0 - fast-glob: - specifier: ^3.2.7 - version: 3.2.12 rollup: specifier: 3.7.5 version: 3.7.5 @@ -167,9 +158,15 @@ importers: rollup-plugin-copy: specifier: ^3.4.0 version: 3.4.0 + svelte: + specifier: ^3.57.0 + version: 3.57.0 svelte-language-server: specifier: workspace:* version: link:../language-server + typescript: + specifier: ^5.5.2 + version: 5.5.2 vscode-languageserver: specifier: 8.0.2 version: 8.0.2 @@ -489,9 +486,6 @@ packages: '@types/prettier@2.7.2': resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} - '@types/pug@2.0.6': - resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} - '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -589,9 +583,6 @@ packages: browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -683,10 +674,6 @@ packages: resolution: {integrity: sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==} engines: {node: '>=8'} - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -709,9 +696,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -742,6 +726,14 @@ packages: fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + fdir@6.2.0: + resolution: {integrity: sha512-9XaWcDl0riOX5j2kYfy0kKdg7skw3IY6kA4LFT8Tk2yF9UdrADUy8D6AJuBLtf7ISm/MksumwAHE3WVbMRyCLw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -946,10 +938,6 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} - magic-string@0.30.7: - resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} - engines: {node: '>=12'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -961,10 +949,6 @@ packages: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -976,13 +960,6 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mocha@9.2.2: resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} engines: {node: '>= 12.0.0'} @@ -1100,10 +1077,6 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -1140,9 +1113,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - sander@0.5.1: - resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} - semver@7.5.1: resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==} engines: {node: '>=10'} @@ -1170,10 +1140,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sorcery@0.11.0: - resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} - hasBin: true - source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -1196,10 +1162,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1220,43 +1182,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-preprocess@5.1.3: - resolution: {integrity: sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==} - engines: {node: '>= 16.0.0', pnpm: ^8.0.0} - peerDependencies: - '@babel/core': ^7.10.2 - coffeescript: ^2.5.1 - less: ^3.11.3 || ^4.0.0 - postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - pug: ^3.0.0 - sass: ^1.26.8 - stylus: ^0.55.0 - sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 - svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 - typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' - peerDependenciesMeta: - '@babel/core': - optional: true - coffeescript: - optional: true - less: - optional: true - postcss: - optional: true - postcss-load-config: - optional: true - pug: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - typescript: - optional: true - svelte@3.57.0: resolution: {integrity: sha512-WMXEvF+RtAaclw0t3bPDTUe19pplMlfyKDsixbHQYgCWi9+O9VN0kXU1OppzrB9gPAvz4NALuoca2LfW2bOjTQ==} engines: {node: '>= 8'} @@ -1292,11 +1217,6 @@ packages: typescript-auto-import-cache@0.3.3: resolution: {integrity: sha512-ojEC7+Ci1ij9eE6hp8Jl9VUNnsEKzztktP5gtYNRMrTmfXVwA1PITYYAkpxCvvupdSYa/Re51B6KMcv1CTZEUA==} - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.5.2: resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} engines: {node: '>=14.17'} @@ -1497,15 +1417,6 @@ snapshots: optionalDependencies: rollup: 3.7.5 - '@rollup/plugin-typescript@10.0.1(rollup@3.7.5)(tslib@2.5.2)(typescript@5.4.5)': - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.7.5) - resolve: 1.22.2 - typescript: 5.4.5 - optionalDependencies: - rollup: 3.7.5 - tslib: 2.5.2 - '@rollup/plugin-typescript@10.0.1(rollup@3.7.5)(tslib@2.5.2)(typescript@5.5.2)': dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.7.5) @@ -1584,8 +1495,6 @@ snapshots: '@types/prettier@2.7.2': {} - '@types/pug@2.0.6': {} - '@types/resolve@1.20.2': {} '@types/sade@1.7.4': @@ -1676,8 +1585,6 @@ snapshots: browser-stdout@1.3.1: {} - buffer-crc32@0.2.13: {} - buffer-from@1.1.2: {} builtin-modules@3.3.0: {} @@ -1770,8 +1677,6 @@ snapshots: rimraf: 3.0.2 slash: 3.0.0 - detect-indent@6.1.0: {} - diff@4.0.2: {} diff@5.0.0: {} @@ -1789,8 +1694,6 @@ snapshots: emoji-regex@8.0.0: {} - es6-promise@3.3.1: {} - escalade@3.1.1: {} escape-string-regexp@1.0.5: {} @@ -1815,6 +1718,8 @@ snapshots: dependencies: reusify: 1.0.4 + fdir@6.2.0: {} + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 @@ -2016,10 +1921,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - magic-string@0.30.7: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - make-error@1.3.6: {} merge2@1.4.1: {} @@ -2029,8 +1930,6 @@ snapshots: braces: 3.0.2 picomatch: 2.3.1 - min-indent@1.0.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -2043,12 +1942,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimist@1.2.8: {} - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mocha@9.2.2: dependencies: '@ungap/promise-all-settled': 1.1.2 @@ -2172,10 +2065,6 @@ snapshots: reusify@1.0.4: {} - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -2216,13 +2105,6 @@ snapshots: safe-buffer@5.2.1: {} - sander@0.5.1: - dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.11 - mkdirp: 0.5.6 - rimraf: 2.7.1 - semver@7.5.1: dependencies: lru-cache: 6.0.0 @@ -2250,13 +2132,6 @@ snapshots: slash@3.0.0: {} - sorcery@0.11.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - buffer-crc32: 0.2.13 - minimist: 1.2.8 - sander: 0.5.1 - source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -2278,10 +2153,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - strip-json-comments@3.1.1: {} supports-color@5.5.0: @@ -2298,28 +2169,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-preprocess@5.1.3(svelte@3.57.0)(typescript@5.4.5): - dependencies: - '@types/pug': 2.0.6 - detect-indent: 6.1.0 - magic-string: 0.30.7 - sorcery: 0.11.0 - strip-indent: 3.0.0 - svelte: 3.57.0 - optionalDependencies: - typescript: 5.4.5 - - svelte-preprocess@5.1.3(svelte@3.57.0)(typescript@5.5.2): - dependencies: - '@types/pug': 2.0.6 - detect-indent: 6.1.0 - magic-string: 0.30.7 - sorcery: 0.11.0 - strip-indent: 3.0.0 - svelte: 3.57.0 - optionalDependencies: - typescript: 5.5.2 - svelte@3.57.0: {} tiny-glob@0.2.9: @@ -2357,8 +2206,6 @@ snapshots: dependencies: semver: 7.5.1 - typescript@5.4.5: {} - typescript@5.5.2: {} unist-util-stringify-position@3.0.3: From 8f5904aa6012c58729195a41e7d4cfb70db152a5 Mon Sep 17 00:00:00 2001 From: "Lyu, Wei-Da" <36730922+jasonlyu123@users.noreply.github.com> Date: Tue, 27 Aug 2024 18:45:13 +0800 Subject: [PATCH 02/12] breaking: project reference support (#2463) - closes #2148 - fixes #1234 - due to the change of how files are resolved (breaking change) - fixes #1976 - less options are now forced (breaking change) - fixes #2154 (breaking change) --- .../plugins/typescript/LSAndTSDocResolver.ts | 73 +- .../src/plugins/typescript/SnapshotManager.ts | 5 + .../plugins/typescript/TypeScriptPlugin.ts | 9 +- .../features/CallHierarchyProvider.ts | 11 +- .../features/CodeActionsProvider.ts | 22 +- .../typescript/features/CompletionProvider.ts | 14 +- .../FindComponentReferencesProvider.ts | 7 +- .../features/FindFileReferencesProvider.ts | 11 +- .../features/FindReferencesProvider.ts | 8 +- .../features/ImplementationProvider.ts | 4 +- .../typescript/features/InlayHintProvider.ts | 4 +- .../typescript/features/RenameProvider.ts | 6 +- .../features/TypeDefinitionProvider.ts | 4 +- .../features/UpdateImportsProvider.ts | 76 ++- .../src/plugins/typescript/features/utils.ts | 22 +- .../src/plugins/typescript/module-loader.ts | 70 +- .../src/plugins/typescript/service.ts | 632 +++++++++++++----- .../src/plugins/typescript/serviceCache.ts | 6 +- .../src/plugins/typescript/svelte-sys.ts | 8 +- packages/language-server/src/server.ts | 5 +- packages/language-server/src/svelte-check.ts | 48 +- .../features/DiagnosticsProvider.test.ts | 2 +- .../features/FindReferencesProvider.test.ts | 6 +- .../typescript/features/HoverProvider.test.ts | 6 +- .../features/ImplemenationProvider.test.ts | 2 +- .../features/TypeDefinitionProvider.test.ts | 2 +- .../features/UpdateImportsProvider.test.ts | 5 +- .../project-reference/nested/expectedv2.json | 10 + .../project-reference/nested/imported.ts | 5 + .../project-reference/nested/input.svelte | 8 + .../nested/tsconfig_sub2.json | 7 + .../nested/tsconfig_sub3.json | 10 + .../project-reference/paths/expectedv2.json | 10 + .../project-reference/paths/imported.ts | 5 + .../project-reference/paths/input.svelte | 8 + .../project-reference/paths/tsconfig_sub.json | 11 + .../fixtures/project-reference/tsconfig.json | 11 + .../diagnostics/fixtures/tsconfig.json | 4 +- .../inlayHints/fixtures/tsconfig.json | 12 + .../plugins/typescript/module-loader.test.ts | 144 +--- .../test/plugins/typescript/service.test.ts | 277 +++++++- .../test/plugins/typescript/test-utils.ts | 56 +- .../testfiles/diagnostics/tsconfig.json | 2 + .../typescript/testfiles/tsconfig.json | 2 + packages/svelte-check/src/writers.ts | 21 +- packages/svelte-check/test/tsconfig.json | 2 +- 46 files changed, 1181 insertions(+), 492 deletions(-) create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/expectedv2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/imported.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/input.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub3.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/expectedv2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/imported.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/input.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/tsconfig_sub.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/tsconfig.json create mode 100644 packages/language-server/test/plugins/typescript/features/inlayHints/fixtures/tsconfig.json diff --git a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts index 3843e8a89..3b0a76c06 100644 --- a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts +++ b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts @@ -1,6 +1,10 @@ import { dirname, join } from 'path'; import ts from 'typescript'; -import { RelativePattern, TextDocumentContentChangeEvent } from 'vscode-languageserver'; +import { + PublishDiagnosticsParams, + RelativePattern, + TextDocumentContentChangeEvent +} from 'vscode-languageserver'; import { Document, DocumentManager } from '../../lib/documents'; import { LSConfigManager } from '../../ls-config'; import { @@ -37,6 +41,7 @@ interface LSAndTSDocResolverOptions { tsconfigPath?: string; onProjectReloaded?: () => void; + reportConfigError?: (diagnostic: PublishDiagnosticsParams) => void; watch?: boolean; tsSystem?: ts.System; watchDirectory?: (patterns: RelativePattern[]) => void; @@ -50,14 +55,10 @@ export class LSAndTSDocResolver { private readonly configManager: LSConfigManager, private readonly options?: LSAndTSDocResolverOptions ) { - const handleDocumentChange = (document: Document) => { - // This refreshes the document in the ts language service - this.getSnapshot(document); - }; docManager.on( 'documentChange', debounceSameArg( - handleDocumentChange, + this.updateSnapshot.bind(this), (newDoc, prevDoc) => newDoc.uri === prevDoc?.uri, 1000 ) @@ -68,7 +69,11 @@ export class LSAndTSDocResolver { // where multiple files and their dependencies // being loaded in a short period of times docManager.on('documentOpen', (document) => { - handleDocumentChange(document); + if (document.openedByClient) { + this.getOrCreateSnapshot(document); + } else { + this.updateSnapshot(document); + } docManager.lockDocument(document.uri); }); @@ -121,7 +126,8 @@ export class LSAndTSDocResolver { watchDirectory: this.options?.watchDirectory ? this.watchDirectory.bind(this) : undefined, - nonRecursiveWatchPattern: this.options?.nonRecursiveWatchPattern + nonRecursiveWatchPattern: this.options?.nonRecursiveWatchPattern, + reportConfigError: this.options?.reportConfigError }; } @@ -151,18 +157,20 @@ export class LSAndTSDocResolver { private lsDocumentContext: LanguageServiceDocumentContext; private readonly watchedDirectories: FileSet; - async getLSForPath(path: string) { - return (await this.getTSService(path)).getService(); - } - async getLSAndTSDoc(document: Document): Promise<{ tsDoc: SvelteDocumentSnapshot; lang: ts.LanguageService; userPreferences: ts.UserPreferences; + lsContainer: LanguageServiceContainer; }> { const { tsDoc, lsContainer, userPreferences } = await this.getLSAndTSDocWorker(document); - return { tsDoc, lang: lsContainer.getService(), userPreferences }; + return { + tsDoc, + lang: lsContainer.getService(), + userPreferences, + lsContainer + }; } /** @@ -181,7 +189,7 @@ export class LSAndTSDocResolver { private async getLSAndTSDocWorker(document: Document) { const lsContainer = await this.getTSService(document.getFilePath() || ''); - const tsDoc = await this.getSnapshot(document); + const tsDoc = await this.getOrCreateSnapshot(document); const userPreferences = this.getUserPreferences(tsDoc); return { tsDoc, lsContainer, userPreferences }; @@ -192,13 +200,21 @@ export class LSAndTSDocResolver { * the ts service it primarily belongs into. * The update is mirrored in all other services, too. */ - async getSnapshot(document: Document): Promise; - async getSnapshot(pathOrDoc: string | Document): Promise; - async getSnapshot(pathOrDoc: string | Document) { + async getOrCreateSnapshot(document: Document): Promise; + async getOrCreateSnapshot(pathOrDoc: string | Document): Promise; + async getOrCreateSnapshot(pathOrDoc: string | Document) { const filePath = typeof pathOrDoc === 'string' ? pathOrDoc : pathOrDoc.getFilePath() || ''; const tsService = await this.getTSService(filePath); return tsService.updateSnapshot(pathOrDoc); } + private async updateSnapshot(document: Document) { + const filePath = document.getFilePath(); + if (!filePath) { + return; + } + // ensure no new service is created + await this.updateExistingFile(filePath, (service) => service.updateSnapshot(document)); + } /** * Updates snapshot path in all existing ts services and retrieves snapshot @@ -217,7 +233,7 @@ export class LSAndTSDocResolver { }); } else { // This may not be a file but a directory, still try - await this.getSnapshot(newPath); + await this.getOrCreateSnapshot(newPath); } } @@ -280,19 +296,11 @@ export class LSAndTSDocResolver { }); } - /** - * @internal Public for tests only - */ - async getSnapshotManager(filePath: string): Promise { - return (await this.getTSService(filePath)).snapshotManager; - } - async getTSService(filePath?: string): Promise { if (this.options?.tsconfigPath) { - return getServiceForTsconfig( - this.options?.tsconfigPath, - dirname(this.options.tsconfigPath), - this.lsDocumentContext + return this.getTSServiceByConfigPath( + this.options.tsconfigPath, + dirname(this.options.tsconfigPath) ); } if (!filePath) { @@ -301,6 +309,13 @@ export class LSAndTSDocResolver { return getService(filePath, this.workspaceUris, this.lsDocumentContext); } + async getTSServiceByConfigPath( + tsconfigPath: string, + workspacePath: string + ): Promise { + return getServiceForTsconfig(tsconfigPath, workspacePath, this.lsDocumentContext); + } + private getUserPreferences(tsDoc: DocumentSnapshot): ts.UserPreferences { const configLang = tsDoc.scriptKind === ts.ScriptKind.TS || tsDoc.scriptKind === ts.ScriptKind.TSX diff --git a/packages/language-server/src/plugins/typescript/SnapshotManager.ts b/packages/language-server/src/plugins/typescript/SnapshotManager.ts index 6bf64f0b6..85b152e49 100644 --- a/packages/language-server/src/plugins/typescript/SnapshotManager.ts +++ b/packages/language-server/src/plugins/typescript/SnapshotManager.ts @@ -262,6 +262,11 @@ export class SnapshotManager { return Array.from(this.projectFileToOriginalCasing.values()); } + isProjectFile(fileName: string): boolean { + fileName = normalizePath(fileName); + return this.projectFileToOriginalCasing.has(this.getCanonicalFileName(fileName)); + } + private logStatistics() { const date = new Date(); // Don't use setInterval because that will keep tests running forever diff --git a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts index bc97f6175..264fe6665 100644 --- a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts +++ b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts @@ -168,7 +168,10 @@ export class TypeScriptPlugin this.completionProvider, configManager ); - this.updateImportsProvider = new UpdateImportsProviderImpl(this.lsAndTsDocResolver); + this.updateImportsProvider = new UpdateImportsProviderImpl( + this.lsAndTsDocResolver, + ts.sys.useCaseSensitiveFileNames + ); this.diagnosticsProvider = new DiagnosticsProviderImpl( this.lsAndTsDocResolver, configManager @@ -383,7 +386,7 @@ export class TypeScriptPlugin } async getDefinitions(document: Document, position: Position): Promise { - const { lang, tsDoc } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); + const { lang, tsDoc, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); const defs = lang.getDefinitionAndBoundSpan( tsDoc.filePath, @@ -394,7 +397,7 @@ export class TypeScriptPlugin return []; } - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); const result = await Promise.all( diff --git a/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts b/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts index b58e797a2..3d60b40ac 100644 --- a/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts @@ -41,7 +41,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider { position: Position, cancellationToken?: CancellationToken ): Promise { - const { lang, tsDoc } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); + const { lang, tsDoc, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); if (cancellationToken?.isCancellationRequested) { return null; @@ -52,7 +52,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider { const itemsArray = Array.isArray(items) ? items : items ? [items] : []; - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); const program = lang.getProgram(); @@ -251,8 +251,9 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider { return null; } - const lang = await this.lsAndTsDocResolver.getLSForPath(filePath); - const tsDoc = await this.lsAndTsDocResolver.getSnapshot(filePath); + const lsContainer = await this.lsAndTsDocResolver.getTSService(filePath); + const lang = lsContainer.getService(); + const tsDoc = await this.lsAndTsDocResolver.getOrCreateSnapshot(filePath); if (cancellationToken?.isCancellationRequested) { return null; @@ -260,7 +261,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider { const program = lang.getProgram(); - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); const isComponentModulePosition = diff --git a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts index 287f21664..59def877c 100644 --- a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts @@ -46,7 +46,6 @@ import { import { CompletionsProviderImpl } from './CompletionProvider'; import { findClosestContainingNode, - findContainingNode, FormatCodeBasis, getFormatCodeBasis, getNewScriptStartTag, @@ -56,6 +55,7 @@ import { } from './utils'; import { DiagnosticCode } from './DiagnosticsProvider'; import { createGetCanonicalFileName } from '../../../utils'; +import { LanguageServiceContainer } from '../service'; /** * TODO change this to protocol constant if it's part of the protocol @@ -156,7 +156,7 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { return codeAction; } - const { lang, tsDoc, userPreferences } = + const { lang, tsDoc, userPreferences, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); if (cancellationToken?.isCancellationRequested) { return codeAction; @@ -180,10 +180,11 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { const isImportFix = codeAction.data.fixName === FIX_IMPORT_FIX_NAME; const virtualDocInfo = isImportFix - ? await this.createVirtualDocumentForCombinedImportCodeFix( + ? this.createVirtualDocumentForCombinedImportCodeFix( document, getDiagnostics(), tsDoc, + lsContainer, lang ) : undefined; @@ -218,7 +219,7 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { await this.lsAndTsDocResolver.deleteSnapshot(virtualDocPath); } - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); const fixActions: ts.CodeFixAction[] = [ { fixName: codeAction.data.fixName, @@ -259,10 +260,11 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { * Do not use this in regular code action * This'll cause TypeScript to rebuild and invalidate caches every time. It'll be slow */ - private async createVirtualDocumentForCombinedImportCodeFix( + private createVirtualDocumentForCombinedImportCodeFix( document: Document, diagnostics: Diagnostic[], tsDoc: DocumentSnapshot, + lsContainer: LanguageServiceContainer, lang: ts.LanguageService ) { const virtualUri = document.uri + '.__virtual__.svelte'; @@ -314,10 +316,8 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { const virtualDoc = new Document(virtualUri, newText); virtualDoc.openedByClient = true; // let typescript know about the virtual document - // getLSAndTSDoc instead of getSnapshot so that project dirty state is correctly tracked by us - // otherwise, sometime the applied code fix might not be picked up by the language service - // because we think the project is still dirty and doesn't update the project version - await this.lsAndTsDocResolver.getLSAndTSDoc(virtualDoc); + lsContainer.openVirtualDocument(virtualDoc); + lsContainer.getService(); return { virtualDoc, @@ -553,7 +553,7 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { context: CodeActionContext, cancellationToken: CancellationToken | undefined ) { - const { lang, tsDoc, userPreferences } = await this.getLSAndTSDoc(document); + const { lang, tsDoc, userPreferences, lsContainer } = await this.getLSAndTSDoc(document); if (cancellationToken?.isCancellationRequested) { return []; @@ -613,7 +613,7 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { ); } - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); const codeActionsPromises = codeFixes.map(async (fix) => { diff --git a/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts b/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts index ffb044938..e7b5bec75 100644 --- a/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts @@ -49,6 +49,7 @@ import { isPartOfImportStatement } from './utils'; import { isInTag as svelteIsInTag } from '../svelte-ast-utils'; +import { LanguageServiceContainer } from '../service'; export interface CompletionResolveInfo extends Pick, @@ -170,7 +171,7 @@ export class CompletionsProviderImpl implements CompletionsProvider 0 ? [] - : await this.getCustomElementCompletions(lang, document, tsDoc, position); + : this.getCustomElementCompletions(lang, lsContainer, document, tsDoc, position); const formatSettings = await this.configManager.getFormatCodeSettingsForFile( document, @@ -474,12 +475,13 @@ export class CompletionsProviderImpl implements CompletionsProvider { + ): CompletionItem[] | undefined { const offset = document.offsetAt(position); const tag = getNodeIfIsInHTMLStartTag(document.html, offset); @@ -499,9 +501,7 @@ export class CompletionsProviderImpl implements CompletionsProvider ref.definition.kind === ts.ScriptElementKind.alias)) { @@ -124,7 +124,7 @@ export class FindReferencesProviderImpl implements FindReferencesProvider { let storeReferences: ts.ReferencedSymbolEntry[] = []; const storeReference = references.find( (ref) => - ref.fileName === tsDoc.filePath && + normalizePath(ref.fileName) === tsDoc.filePath && isTextSpanInGeneratedCode(tsDoc.getFullText(), ref.textSpan) && is$storeVariableIn$storeDeclaration(tsDoc.getFullText(), ref.textSpan.start) ); diff --git a/packages/language-server/src/plugins/typescript/features/ImplementationProvider.ts b/packages/language-server/src/plugins/typescript/features/ImplementationProvider.ts index e4a91c1bb..693afaa49 100644 --- a/packages/language-server/src/plugins/typescript/features/ImplementationProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/ImplementationProvider.ts @@ -18,7 +18,7 @@ export class ImplementationProviderImpl implements ImplementationProvider { position: Position, cancellationToken?: CancellationToken ): Promise { - const { tsDoc, lang } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); + const { tsDoc, lang, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); if (cancellationToken?.isCancellationRequested) { return null; @@ -27,7 +27,7 @@ export class ImplementationProviderImpl implements ImplementationProvider { const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position)); const implementations = lang.getImplementationAtPosition(tsDoc.filePath, offset); - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); if (!implementations) { diff --git a/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts b/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts index 9ca919900..a11fb0f43 100644 --- a/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts @@ -41,7 +41,7 @@ export class InlayHintProviderImpl implements InlayHintProvider { return null; } - const { tsDoc, lang } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); + const { tsDoc, lang, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); const inlayHints = lang.provideInlayHints( tsDoc.filePath, @@ -59,7 +59,7 @@ export class InlayHintProviderImpl implements InlayHintProvider { const renderFunctionReturnTypeLocation = renderFunction && this.getTypeAnnotationPosition(renderFunction); - const snapshotMap = new SnapshotMap(this.lsAndTsDocResolver); + const snapshotMap = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshotMap.set(tsDoc.filePath, tsDoc); const convertPromises = inlayHints diff --git a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts index 281ae659f..3b9caf86e 100644 --- a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts @@ -65,7 +65,7 @@ export class RenameProviderImpl implements RenameProvider { position: Position, newName: string ): Promise { - const { lang, tsDoc } = await this.getLSAndTSDoc(document); + const { lang, tsDoc, lsContainer } = await this.getLSAndTSDoc(document); const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position)); @@ -85,7 +85,7 @@ export class RenameProviderImpl implements RenameProvider { return null; } - const docs = new SnapshotMap(this.lsAndTsDocResolver); + const docs = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); docs.set(tsDoc.filePath, tsDoc); let convertedRenameLocations: TsRenameLocation[] = await this.mapAndFilterRenameLocations( @@ -536,7 +536,7 @@ export class RenameProviderImpl implements RenameProvider { } private getSnapshot(filePath: string) { - return this.lsAndTsDocResolver.getSnapshot(filePath); + return this.lsAndTsDocResolver.getOrCreateSnapshot(filePath); } private checkShortHandBindingOrSlotLetLocation( diff --git a/packages/language-server/src/plugins/typescript/features/TypeDefinitionProvider.ts b/packages/language-server/src/plugins/typescript/features/TypeDefinitionProvider.ts index 93674fd9e..3c2ecf625 100644 --- a/packages/language-server/src/plugins/typescript/features/TypeDefinitionProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/TypeDefinitionProvider.ts @@ -10,11 +10,11 @@ export class TypeDefinitionProviderImpl implements TypeDefinitionProvider { constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {} async getTypeDefinition(document: Document, position: Position): Promise { - const { tsDoc, lang } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); + const { tsDoc, lang, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document); const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position)); const typeDefs = lang.getTypeDefinitionAtPosition(tsDoc.filePath, offset); - const snapshots = new SnapshotMap(this.lsAndTsDocResolver); + const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); snapshots.set(tsDoc.filePath, tsDoc); if (!typeDefs) { diff --git a/packages/language-server/src/plugins/typescript/features/UpdateImportsProvider.ts b/packages/language-server/src/plugins/typescript/features/UpdateImportsProvider.ts index aa46594ff..54aa9a060 100644 --- a/packages/language-server/src/plugins/typescript/features/UpdateImportsProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/UpdateImportsProvider.ts @@ -6,14 +6,27 @@ import { WorkspaceEdit } from 'vscode-languageserver'; import { mapRangeToOriginal } from '../../../lib/documents'; -import { urlToPath } from '../../../utils'; +import { + createGetCanonicalFileName, + GetCanonicalFileName, + normalizePath, + urlToPath +} from '../../../utils'; import { FileRename, UpdateImportsProvider } from '../../interfaces'; import { LSAndTSDocResolver } from '../LSAndTSDocResolver'; +import { forAllServices, LanguageServiceContainer } from '../service'; import { convertRange } from '../utils'; import { isKitTypePath, SnapshotMap } from './utils'; export class UpdateImportsProviderImpl implements UpdateImportsProvider { - constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {} + constructor( + private readonly lsAndTsDocResolver: LSAndTSDocResolver, + useCaseSensitiveFileNames: boolean + ) { + this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + } + + private getCanonicalFileName: GetCanonicalFileName; async updateImports(fileRename: FileRename): Promise { // TODO does this handle folder moves/renames correctly? old/new path isn't a file then @@ -23,7 +36,47 @@ export class UpdateImportsProviderImpl implements UpdateImportsProvider { return null; } - const ls = await this.getLSForPath(newPath); + const services: LanguageServiceContainer[] = []; + await forAllServices((ls) => { + services.push(ls); + }); + + const documentChanges = new Map(); + for (const service of services) { + await this.updateImportForSingleService(oldPath, newPath, service, documentChanges); + } + + return { + documentChanges: Array.from(documentChanges.values()) + }; + } + + async updateImportForSingleService( + oldPath: string, + newPath: string, + lsContainer: LanguageServiceContainer, + documentChanges: Map + ) { + const ls = lsContainer.getService(); + const program = ls.getProgram(); + if (!program) { + return; + } + + const canonicalOldPath = this.getCanonicalFileName(normalizePath(oldPath)); + const canonicalNewPath = this.getCanonicalFileName(normalizePath(newPath)); + const hasFile = program.getSourceFiles().some((sf) => { + const normalizedFileName = this.getCanonicalFileName(normalizePath(sf.fileName)); + return ( + normalizedFileName.startsWith(canonicalOldPath) || + normalizedFileName.startsWith(canonicalNewPath) + ); + }); + + if (!hasFile) { + return; + } + const oldPathTsProgramCasing = ls.getProgram()?.getSourceFile(oldPath)?.fileName ?? oldPath; // `getEditsForFileRename` might take a while const fileChanges = ls @@ -75,12 +128,15 @@ export class UpdateImportsProviderImpl implements UpdateImportsProvider { return change; }); - const docs = new SnapshotMap(this.lsAndTsDocResolver); - const documentChanges = await Promise.all( + const docs = new SnapshotMap(this.lsAndTsDocResolver, lsContainer); + await Promise.all( updateImportsChanges.map(async (change) => { + if (documentChanges.has(change.fileName)) { + return; + } const snapshot = await docs.retrieve(change.fileName); - return TextDocumentEdit.create( + const edit = TextDocumentEdit.create( OptionalVersionedTextDocumentIdentifier.create(snapshot.getURL(), null), change.textChanges.map((edit) => { const range = mapRangeToOriginal( @@ -90,13 +146,9 @@ export class UpdateImportsProviderImpl implements UpdateImportsProvider { return TextEdit.replace(range, edit.newText); }) ); + + documentChanges.set(change.fileName, edit); }) ); - - return { documentChanges }; - } - - private async getLSForPath(path: string) { - return this.lsAndTsDocResolver.getLSForPath(path); } } diff --git a/packages/language-server/src/plugins/typescript/features/utils.ts b/packages/language-server/src/plugins/typescript/features/utils.ts index f8ee48a21..dba13177a 100644 --- a/packages/language-server/src/plugins/typescript/features/utils.ts +++ b/packages/language-server/src/plugins/typescript/features/utils.ts @@ -12,6 +12,7 @@ import { LSAndTSDocResolver } from '../LSAndTSDocResolver'; import { or } from '../../../utils'; import { FileMap } from '../../../lib/documents/fileCollection'; import { LSConfig } from '../../../ls-config'; +import { LanguageServiceContainer } from '../service'; type NodePredicate = (node: ts.Node) => boolean; @@ -144,7 +145,10 @@ export function getStoreOffsetOf$storeDeclaration(text: string, $storeVarStart: export class SnapshotMap { private map = new FileMap(); - constructor(private resolver: LSAndTSDocResolver) {} + constructor( + private resolver: LSAndTSDocResolver, + private sourceLs: LanguageServiceContainer + ) {} set(fileName: string, snapshot: DocumentSnapshot) { this.map.set(fileName, snapshot); @@ -156,12 +160,18 @@ export class SnapshotMap { async retrieve(fileName: string) { let snapshot = this.get(fileName); - if (!snapshot) { - const snap = await this.resolver.getSnapshot(fileName); - this.set(fileName, snap); - snapshot = snap; + if (snapshot) { + return snapshot; } - return snapshot; + + const snap = + this.sourceLs.snapshotManager.get(fileName) ?? + // should not happen in most cases, + // the file should be in the project otherwise why would we know about it + (await this.resolver.getOrCreateSnapshot(fileName)); + + this.set(fileName, snap); + return snap; } } diff --git a/packages/language-server/src/plugins/typescript/module-loader.ts b/packages/language-server/src/plugins/typescript/module-loader.ts index 91e59827b..915e50b26 100644 --- a/packages/language-server/src/plugins/typescript/module-loader.ts +++ b/packages/language-server/src/plugins/typescript/module-loader.ts @@ -166,7 +166,8 @@ export function createSvelteModuleLoader( getSnapshot: (fileName: string) => DocumentSnapshot, compilerOptions: ts.CompilerOptions, tsSystem: ts.System, - tsModule: typeof ts + tsModule: typeof ts, + getModuleResolutionHost: () => ts.ModuleResolutionHost | undefined ) { const getCanonicalFileName = createGetCanonicalFileName(tsSystem.useCaseSensitiveFileNames); const svelteSys = createSvelteSys(tsSystem); @@ -206,9 +207,16 @@ export function createSvelteModuleLoader( const previousTriedButFailed = failedPathToContainingFile.get(path); - for (const containingFile of previousTriedButFailed ?? []) { + if (!previousTriedButFailed) { + return; + } + + for (const containingFile of previousTriedButFailed) { failedLocationInvalidated.add(containingFile); } + + tsModuleCache.clear(); + typeReferenceCache.clear(); }, resolveModuleNames, resolveTypeReferenceDirectiveReferences, @@ -221,8 +229,8 @@ export function createSvelteModuleLoader( moduleNames: string[], containingFile: string, _reusedNames: string[] | undefined, - _redirectedReference: ts.ResolvedProjectReference | undefined, - _options: ts.CompilerOptions, + redirectedReference: ts.ResolvedProjectReference | undefined, + options: ts.CompilerOptions, containingSourceFile?: ts.SourceFile | undefined ): Array { return moduleNames.map((moduleName, index) => { @@ -234,7 +242,9 @@ export function createSvelteModuleLoader( moduleName, containingFile, containingSourceFile, - index + index, + redirectedReference, + options ); resolvedModule?.failedLookupLocations?.forEach((failedLocation) => { @@ -252,60 +262,44 @@ export function createSvelteModuleLoader( name: string, containingFile: string, containingSourceFile: ts.SourceFile | undefined, - index: number + index: number, + redirectedReference: ts.ResolvedProjectReference | undefined, + option: ts.CompilerOptions ): ts.ResolvedModuleWithFailedLookupLocations { - const mode = impliedNodeFormatResolver.resolve( - name, - index, - containingSourceFile, - compilerOptions - ); - // Delegate to the TS resolver first. - // If that does not bring up anything, try the Svelte Module loader - // which is able to deal with .svelte files. - const tsResolvedModuleWithFailedLookup = tsModule.resolveModuleName( + const mode = impliedNodeFormatResolver.resolve(name, index, containingSourceFile, option); + const resolvedModuleWithFailedLookup = tsModule.resolveModuleName( name, containingFile, compilerOptions, - tsSystem, + getModuleResolutionHost() ?? svelteSys, tsModuleCache, - undefined, + redirectedReference, mode ); - const tsResolvedModule = tsResolvedModuleWithFailedLookup.resolvedModule; - if (tsResolvedModule) { - return tsResolvedModuleWithFailedLookup; + const resolvedModule = resolvedModuleWithFailedLookup.resolvedModule; + + if (!resolvedModule || !isVirtualSvelteFilePath(resolvedModule.resolvedFileName)) { + return resolvedModuleWithFailedLookup; } - const svelteResolvedModuleWithFailedLookup = tsModule.resolveModuleName( - name, - containingFile, - compilerOptions, - svelteSys, - undefined, - undefined, - mode + const resolvedFileName = svelteSys.getRealSveltePathIfExists( + resolvedModule.resolvedFileName ); - const svelteResolvedModule = svelteResolvedModuleWithFailedLookup.resolvedModule; - if ( - !svelteResolvedModule || - !isVirtualSvelteFilePath(svelteResolvedModule.resolvedFileName) - ) { - return svelteResolvedModuleWithFailedLookup; + if (!isSvelteFilePath(resolvedFileName)) { + return resolvedModuleWithFailedLookup; } - const resolvedFileName = ensureRealSvelteFilePath(svelteResolvedModule.resolvedFileName); const snapshot = getSnapshot(resolvedFileName); const resolvedSvelteModule: ts.ResolvedModuleFull = { extension: getExtensionFromScriptKind(snapshot && snapshot.scriptKind), resolvedFileName, - isExternalLibraryImport: svelteResolvedModule.isExternalLibraryImport + isExternalLibraryImport: resolvedModule.isExternalLibraryImport }; return { - ...svelteResolvedModuleWithFailedLookup, + ...resolvedModuleWithFailedLookup, resolvedModule: resolvedSvelteModule }; } diff --git a/packages/language-server/src/plugins/typescript/service.ts b/packages/language-server/src/plugins/typescript/service.ts index fa2fe0c2b..8f5720bb0 100644 --- a/packages/language-server/src/plugins/typescript/service.ts +++ b/packages/language-server/src/plugins/typescript/service.ts @@ -1,19 +1,25 @@ -import { basename, dirname, join, resolve } from 'path'; +import { dirname, join, resolve, basename } from 'path'; import ts from 'typescript'; -import { RelativePattern, TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol'; +import { + PublishDiagnosticsParams, + RelativePattern, + TextDocumentContentChangeEvent +} from 'vscode-languageserver-protocol'; import { getPackageInfo, importSvelte } from '../../importPackage'; import { Document } from '../../lib/documents'; import { configLoader } from '../../lib/documents/configLoader'; import { FileMap, FileSet } from '../../lib/documents/fileCollection'; import { Logger } from '../../logger'; -import { createGetCanonicalFileName, normalizePath, pathToUrl, urlToPath } from '../../utils'; +import { + createGetCanonicalFileName, + isNotNullOrUndefined, + normalizePath, + pathToUrl, + urlToPath +} from '../../utils'; import { DocumentSnapshot, SvelteSnapshotOptions } from './DocumentSnapshot'; import { createSvelteModuleLoader } from './module-loader'; -import { - GlobalSnapshotsManager, - ignoredBuildDirectories, - SnapshotManager -} from './SnapshotManager'; +import { GlobalSnapshotsManager, SnapshotManager } from './SnapshotManager'; import { ensureRealSvelteFilePath, findTsConfigPath, @@ -27,15 +33,13 @@ export interface LanguageServiceContainer { readonly tsconfigPath: string; readonly compilerOptions: ts.CompilerOptions; readonly configErrors: ts.Diagnostic[]; - /** - * @internal Public for tests only - */ readonly snapshotManager: SnapshotManager; getService(skipSynchronize?: boolean): ts.LanguageService; updateSnapshot(documentOrFilePath: Document | string): DocumentSnapshot; deleteSnapshot(filePath: string): void; invalidateModuleCache(filePath: string[]): void; scheduleProjectFileUpdate(watcherNewFiles: string[]): void; + ensureProjectFileUpdates(): void; updateTsOrJsFile(fileName: string, changes?: TextDocumentContentChangeEvent[]): void; /** * Checks if a file is present in the project. @@ -50,7 +54,9 @@ export interface LanguageServiceContainer { onAutoImportProviderSettingsChanged(): void; onPackageJsonChange(packageJsonPath: string): void; getTsConfigSvelteOptions(): { namespace: string }; - + getResolvedProjectReferences(): TsConfigInfo[]; + openVirtualDocument(document: Document): void; + isShimFiles(filePath: string): boolean; dispose(): void; } @@ -63,7 +69,12 @@ declare module 'typescript' { */ hasInvalidatedResolutions?: (sourceFile: string) => boolean; + /** + * @internal + */ getModuleResolutionCache?(): ts.ModuleResolutionCache; + /** @internal */ + setCompilerHost?(host: ts.CompilerHost): void; } interface ResolvedModuleWithFailedLookupLocations { @@ -82,17 +93,26 @@ declare module 'typescript' { } } +export interface TsConfigInfo { + parsedCommandLine: ts.ParsedCommandLine; + snapshotManager: SnapshotManager; + pendingProjectFileUpdate: boolean; + configFilePath: string; + extendedConfigPaths?: Set; +} + const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024; // 20 MB const services = new FileMap>(); const serviceSizeMap = new FileMap(); const configWatchers = new FileMap(); -const extendedConfigWatchers = new FileMap(); -const extendedConfigToTsConfigPath = new FileMap(); +const dependedConfigWatchers = new FileMap(); +const configPathToDependedProject = new FileMap(); const configFileModifiedTime = new FileMap(); const configFileForOpenFiles = new FileMap(); const pendingReloads = new FileSet(); const documentRegistries = new Map(); const pendingForAllServices = new Set>(); +const parsedTsConfigInfo = new FileMap(); /** * For testing only: Reset the cache for services. @@ -101,6 +121,7 @@ const pendingForAllServices = new Set>(); */ export function __resetCache() { services.clear(); + parsedTsConfigInfo.clear(); serviceSizeMap.clear(); configFileForOpenFiles.clear(); } @@ -112,7 +133,8 @@ export interface LanguageServiceDocumentContext { globalSnapshotsManager: GlobalSnapshotsManager; notifyExceedSizeLimit: (() => void) | undefined; extendedConfigCache: Map; - onProjectReloaded: (() => void) | undefined; + onProjectReloaded: ((configFileNames: string[]) => void) | undefined; + reportConfigError: ((diagnostics: PublishDiagnosticsParams) => void) | undefined; watchTsConfig: boolean; tsSystem: ts.System; projectService: ProjectService | undefined; @@ -129,13 +151,35 @@ export async function getService( docContext.tsSystem.useCaseSensitiveFileNames ); - const tsconfigPath = + const fileExistsWithCache = (fileName: string) => { + return ( + (parsedTsConfigInfo.has(fileName) && !pendingReloads.has(fileName)) || + docContext.tsSystem.fileExists(fileName) + ); + }; + + let tsconfigPath = configFileForOpenFiles.get(path) ?? - findTsConfigPath(path, workspaceUris, docContext.tsSystem.fileExists, getCanonicalFileName); + findTsConfigPath(path, workspaceUris, fileExistsWithCache, getCanonicalFileName); if (tsconfigPath) { - configFileForOpenFiles.set(path, tsconfigPath); - return getServiceForTsconfig(tsconfigPath, dirname(tsconfigPath), docContext); + /** + * Prevent infinite loop when the project reference is circular + */ + const triedTsConfig = new Set(); + const needAssign = !configFileForOpenFiles.has(path); + let service = await getConfiguredService(tsconfigPath); + if (!needAssign) { + return service; + } + + const defaultService = await findDefaultServiceForFile(service, triedTsConfig); + if (defaultService) { + configFileForOpenFiles.set(path, defaultService.tsconfigPath); + return defaultService; + } + + tsconfigPath = ''; } // Find closer boundary: workspace uri or node_modules @@ -156,6 +200,55 @@ export async function getService( docContext.tsSystem.getCurrentDirectory(), docContext ); + + function getConfiguredService(tsconfigPath: string) { + return getServiceForTsconfig(tsconfigPath, dirname(tsconfigPath), docContext); + } + + async function findDefaultServiceForFile( + service: LanguageServiceContainer, + triedTsConfig: Set + ): Promise { + service.ensureProjectFileUpdates(); + if (service.snapshotManager.isProjectFile(path)) { + return service; + } + if (triedTsConfig.has(service.tsconfigPath)) { + return; + } + + // TODO: maybe add support for ts 5.6's ancestor searching + return findDefaultFromProjectReferences(service, triedTsConfig); + } + + async function findDefaultFromProjectReferences( + service: LanguageServiceContainer, + triedTsConfig: Set + ) { + const projectReferences = service.getResolvedProjectReferences(); + if (projectReferences.length === 0) { + return undefined; + } + + let possibleSubPaths: string[] = []; + for (const ref of projectReferences) { + if (ref.snapshotManager.isProjectFile(path)) { + return getConfiguredService(ref.configFilePath); + } + + if (ref.parsedCommandLine.projectReferences?.length) { + possibleSubPaths.push(ref.configFilePath); + } + } + + for (const ref of possibleSubPaths) { + const subService = await getConfiguredService(ref); + const defaultService = await findDefaultServiceForFile(subService, triedTsConfig); + if (defaultService) { + return defaultService; + } + } + } } export async function forAllServices( @@ -182,6 +275,9 @@ export async function getServiceForTsconfig( workspacePath: string, docContext: LanguageServiceDocumentContext ): Promise { + if (tsconfigPath) { + tsconfigPath = normalizePath(tsconfigPath); + } const tsconfigPathOrWorkspacePath = tsconfigPath || workspacePath; const reloading = pendingReloads.has(tsconfigPath); @@ -190,6 +286,7 @@ export async function getServiceForTsconfig( if (reloading || !services.has(tsconfigPathOrWorkspacePath)) { if (reloading) { Logger.log('Reloading ts service at ', tsconfigPath, ' due to config updated'); + parsedTsConfigInfo.delete(tsconfigPath); } else { Logger.log('Initialize new ts service at ', tsconfigPath); } @@ -216,36 +313,31 @@ async function createLanguageService( ): Promise { const { tsSystem } = docContext; - const { - options: compilerOptions, - errors: configErrors, - fileNames: files, - raw, - extendedConfigPaths, - wildcardDirectories - } = getParsedConfig(); + const projectConfig = getParsedConfig(); + const { options: compilerOptions, raw, errors: configErrors } = projectConfig; const getCanonicalFileName = createGetCanonicalFileName(tsSystem.useCaseSensitiveFileNames); - watchWildCardDirectories(); - - // raw is the tsconfig merged with extending config - // see: https://github.com/microsoft/TypeScript/blob/08e4f369fbb2a5f0c30dee973618d65e6f7f09f8/src/compiler/commandLineParser.ts#L2537 - const snapshotManager = new SnapshotManager( - docContext.globalSnapshotsManager, - raw, - workspacePath, - tsSystem, - files, - wildcardDirectories - ); + watchWildCardDirectories(projectConfig); + + const snapshotManager = createSnapshotManager(projectConfig, tsconfigPath); // Load all configs within the tsconfig scope and the one above so that they are all loaded // by the time they need to be accessed synchronously by DocumentSnapshots. await configLoader.loadConfigs(workspacePath); - const svelteModuleLoader = createSvelteModuleLoader(getSnapshot, compilerOptions, tsSystem, ts); + const svelteModuleLoader = createSvelteModuleLoader( + getSnapshot, + compilerOptions, + tsSystem, + ts, + () => host?.getCompilerHost?.() + ); let svelteTsPath: string; + /** + * set and clear during program creation, shouldn't not be cached elsewhere + */ + let compilerHost: ts.CompilerHost | undefined; try { // For when svelte2tsx/svelte-check is part of node_modules, for example VS Code extension svelteTsPath = dirname(require.resolve(docContext.ambientTypesSource)); @@ -262,27 +354,12 @@ async function createLanguageService( ? importSvelte(tsconfigPath || workspacePath) : undefined; - const isSvelte3 = sveltePackageInfo.version.major === 3; - const svelteHtmlDeclaration = isSvelte3 - ? undefined - : join(sveltePackageInfo.path, 'svelte-html.d.ts'); - const svelteHtmlFallbackIfNotExist = - svelteHtmlDeclaration && tsSystem.fileExists(svelteHtmlDeclaration) - ? svelteHtmlDeclaration - : './svelte-jsx-v4.d.ts'; - const changedFilesForExportCache = new Set(); - - const svelteTsxFiles = ( - isSvelte3 - ? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts'] - : ['./svelte-shims-v4.d.ts', svelteHtmlFallbackIfNotExist, './svelte-native-jsx.d.ts'] - ).map((f) => tsSystem.resolvePath(resolve(svelteTsPath, f))); + const svelteTsxFiles = getSvelteShimFiles(); let languageServiceReducedMode = false; let projectVersion = 0; - let dirty = false; - let pendingProjectFileUpdate = false; + let dirty = projectConfig.fileNames.length > 0; const host: ts.LanguageServiceHost = { log: (message) => Logger.debug(`[ts] ${message}`), @@ -297,7 +374,10 @@ async function createLanguageService( readFile: svelteModuleLoader.readFile, resolveModuleNames: svelteModuleLoader.resolveModuleNames, readDirectory: svelteModuleLoader.readDirectory, + realpath: tsSystem.realpath, getDirectories: tsSystem.getDirectories, + getProjectReferences: () => projectConfig.projectReferences, + getParsedCommandLine, useCaseSensitiveFileNames: () => tsSystem.useCaseSensitiveFileNames, getScriptKind: (fileName: string) => getSnapshot(fileName).scriptKind, getProjectVersion: () => projectVersion.toString(), @@ -305,11 +385,17 @@ async function createLanguageService( resolveTypeReferenceDirectiveReferences: svelteModuleLoader.resolveTypeReferenceDirectiveReferences, hasInvalidatedResolutions: svelteModuleLoader.mightHaveInvalidatedResolutions, - getModuleResolutionCache: svelteModuleLoader.getModuleResolutionCache + getModuleResolutionCache: svelteModuleLoader.getModuleResolutionCache, + useSourceOfProjectReferenceRedirect() { + return !languageServiceReducedMode; + }, + setCompilerHost: (host) => (compilerHost = host), + getCompilerHost: () => compilerHost }; const documentRegistry = getOrCreateDocumentRegistry( - host.getCurrentDirectory(), + // this should mostly be a singleton while host.getCurrentDirectory() might be the directory where the tsconfig is + tsSystem.getCurrentDirectory(), tsSystem.useCaseSensitiveFileNames ); @@ -326,8 +412,7 @@ async function createLanguageService( docContext.globalSnapshotsManager.onChange(scheduleUpdate); reduceLanguageServiceCapabilityIfFileSizeTooBig(); - updateExtendedConfigDependents(); - watchConfigFile(); + watchConfigFiles(projectConfig.extendedConfigPaths, projectConfig); return { tsconfigPath, @@ -338,6 +423,7 @@ async function createLanguageService( deleteSnapshot, scheduleProjectFileUpdate, updateTsOrJsFile, + ensureProjectFileUpdates, hasFile, fileBelongsToProject, snapshotManager, @@ -345,10 +431,34 @@ async function createLanguageService( onAutoImportProviderSettingsChanged, onPackageJsonChange, getTsConfigSvelteOptions, + getResolvedProjectReferences, + openVirtualDocument, + isShimFiles, dispose }; - function watchWildCardDirectories() { + function createSnapshotManager( + parsedCommandLine: ts.ParsedCommandLine, + configFileName: string + ) { + const cached = configFileName ? parsedTsConfigInfo.get(configFileName) : undefined; + if (cached?.snapshotManager) { + return cached.snapshotManager; + } + // raw is the tsconfig merged with extending config + // see: https://github.com/microsoft/TypeScript/blob/08e4f369fbb2a5f0c30dee973618d65e6f7f09f8/src/compiler/commandLineParser.ts#L2537 + return new SnapshotManager( + docContext.globalSnapshotsManager, + parsedCommandLine.raw, + configFileName ? dirname(configFileName) : workspacePath, + tsSystem, + parsedCommandLine.fileNames.map(normalizePath), + parsedCommandLine.wildcardDirectories + ); + } + + function watchWildCardDirectories(parseCommandLine: ts.ParsedCommandLine) { + const { wildcardDirectories } = parseCommandLine; if (!wildcardDirectories || !docContext.watchDirectory) { return; } @@ -376,10 +486,8 @@ async function createLanguageService( } function getService(skipSynchronize?: boolean) { - if (pendingProjectFileUpdate) { - updateProjectFiles(); - pendingProjectFileUpdate = false; - } + ensureProjectFileUpdates(); + if (!skipSynchronize) { updateIfDirty(); } @@ -411,7 +519,13 @@ async function createLanguageService( function updateSnapshotFromDocument(document: Document): DocumentSnapshot { const filePath = document.getFilePath() || ''; const prevSnapshot = snapshotManager.get(filePath); - if (prevSnapshot?.version === document.version) { + + if ( + prevSnapshot?.version === document.version && + // In the test, there might be a new document instance with a different openedByClient + // In that case, Create a new snapshot otherwise the getClientFileNames won't include the new client file + prevSnapshot.isOpenedInClient() === document.openedByClient + ) { return prevSnapshot; } @@ -481,17 +595,38 @@ async function createLanguageService( } function scheduleProjectFileUpdate(watcherNewFiles: string[]): void { - if (snapshotManager.areIgnoredFromNewFileWatch(watcherNewFiles)) { - return; + if (!snapshotManager.areIgnoredFromNewFileWatch(watcherNewFiles)) { + scheduleUpdate(); + const info = parsedTsConfigInfo.get(tsconfigPath); + if (info) { + info.pendingProjectFileUpdate = true; + } } - scheduleUpdate(); - pendingProjectFileUpdate = true; + if (!projectConfig.projectReferences) { + return; + } + for (const ref of projectConfig.projectReferences) { + const config = parsedTsConfigInfo.get(ref.path); + if ( + config && + // handled by the respective service + !services.has(config.configFilePath) && + !config.snapshotManager.areIgnoredFromNewFileWatch(watcherNewFiles) + ) { + config.pendingProjectFileUpdate = true; + scheduleUpdate(); + } + } } - function updateProjectFiles(): void { + function ensureProjectFileUpdates(): void { + const info = parsedTsConfigInfo.get(tsconfigPath); + if (!info || !info.pendingProjectFileUpdate) { + return; + } const projectFileCountBefore = snapshotManager.getProjectFileNames().length; - snapshotManager.updateProjectFiles(); + ensureFilesForConfigUpdates(info); const projectFileCountAfter = snapshotManager.getProjectFileNames().length; if (projectFileCountAfter > projectFileCountBefore) { @@ -535,72 +670,26 @@ async function createLanguageService( } function getParsedConfig() { - const forcedCompilerOptions: ts.CompilerOptions = { - allowNonTsExtensions: true, - target: ts.ScriptTarget.Latest, - allowJs: true, - noEmit: true, - declaration: false, - skipLibCheck: true - }; - - // always let ts parse config to get default compilerOption - let configJson = - (tsconfigPath && ts.readConfigFile(tsconfigPath, tsSystem.readFile).config) || - getDefaultJsConfig(); - - // Only default exclude when no extends for now - if (!configJson.extends) { - configJson = Object.assign( - { - exclude: getDefaultExclude() - }, - configJson - ); - } - - const extendedConfigPaths = new Set(); - const { extendedConfigCache } = docContext; - const cacheMonitorProxy = { - ...docContext.extendedConfigCache, - get(key: string) { - extendedConfigPaths.add(key); - return extendedConfigCache.get(key); - }, - has(key: string) { - extendedConfigPaths.add(key); - return extendedConfigCache.has(key); - }, - set(key: string, value: ts.ExtendedConfigCacheEntry) { - extendedConfigPaths.add(key); - return extendedConfigCache.set(key, value); + let compilerOptions: ts.CompilerOptions; + let parsedConfig: ts.ParsedCommandLine; + let extendedConfigPaths: Set | undefined; + + if (tsconfigPath) { + const info = ensureTsConfigInfoUpToDate(tsconfigPath); + // tsconfig is either found from file-system or passed from svelte-check + // so this is already be validated to exist + if (!info) { + throw new Error('Failed to get tsconfig: ' + tsconfigPath); } - }; - - const parsedConfig = ts.parseJsonConfigFileContent( - configJson, - tsSystem, - workspacePath, - forcedCompilerOptions, - tsconfigPath, - undefined, - [ - { - extension: 'svelte', - isMixedContent: true, - // Deferred was added in a later TS version, fall back to tsx - // If Deferred exists, this means that all Svelte files are included - // in parsedConfig.fileNames - scriptKind: ts.ScriptKind.Deferred ?? ts.ScriptKind.TS - } - ], - cacheMonitorProxy - ); + compilerOptions = info.parsedCommandLine.options; + parsedConfig = info.parsedCommandLine; + extendedConfigPaths = info.extendedConfigPaths; + } else { + const config = parseDefaultCompilerOptions(); + compilerOptions = config.compilerOptions; + parsedConfig = config.parsedConfig; + } - const compilerOptions: ts.CompilerOptions = { - ...parsedConfig.options, - ...forcedCompilerOptions - }; if ( !compilerOptions.moduleResolution || compilerOptions.moduleResolution === ts.ModuleResolutionKind.Classic @@ -609,6 +698,7 @@ async function createLanguageService( // NodeJS: up to 4.9, Node10: since 5.0 (ts.ModuleResolutionKind as any).NodeJs ?? ts.ModuleResolutionKind.Node10; } + if ( !compilerOptions.module || [ @@ -623,6 +713,12 @@ async function createLanguageService( compilerOptions.module = ts.ModuleKind.ESNext; } + if (!compilerOptions.target) { + compilerOptions.target = ts.ScriptTarget.Latest; + } else if (ts.ScriptTarget.ES2015 > compilerOptions.target) { + compilerOptions.target = ts.ScriptTarget.ES2015; + } + // detect which JSX namespace to use (svelte | svelteNative) if not specified or not compatible if (!compilerOptions.jsxFactory || !compilerOptions.jsxFactory.startsWith('svelte')) { //override if we detect svelte-native @@ -640,6 +736,20 @@ async function createLanguageService( } } + const svelteConfigDiagnostics = checkSvelteInput(parsedConfig); + if (svelteConfigDiagnostics.length > 0) { + docContext.reportConfigError?.({ + uri: pathToUrl(tsconfigPath), + diagnostics: svelteConfigDiagnostics.map((d) => ({ + message: d.messageText as string, + range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }, + severity: ts.DiagnosticCategory.Error, + source: 'svelte' + })) + }); + parsedConfig.errors.push(...svelteConfigDiagnostics); + } + return { ...parsedConfig, fileNames: parsedConfig.fileNames.map(normalizePath), @@ -648,15 +758,43 @@ async function createLanguageService( }; } - /** - * This should only be used when there's no jsconfig/tsconfig at all - */ - function getDefaultJsConfig(): { - compilerOptions: ts.CompilerOptions; - include: string[]; - } { - return { + function checkSvelteInput(config: ts.ParsedCommandLine) { + if (!tsconfigPath || config.raw.references || config.raw.files) { + return []; + } + + const svelteFiles = config.fileNames.filter(isSvelteFilePath); + if (svelteFiles.length > 0) { + return []; + } + const { include, exclude } = config.raw; + const inputText = JSON.stringify(include); + const excludeText = JSON.stringify(exclude); + const svelteConfigDiagnostics: ts.Diagnostic[] = [ + { + category: ts.DiagnosticCategory.Error, + code: 0, + file: undefined, + start: undefined, + length: undefined, + messageText: + `No svelte input files were found in config file '${tsconfigPath}'. ` + + `Did you forget to add svelte files to the 'include' in your ${basename(tsconfigPath)}? ` + + `Specified 'include' paths were '${inputText}' and 'exclude' paths were '${excludeText}'`, + source: 'svelte' + } + ]; + + return svelteConfigDiagnostics; + } + + function parseDefaultCompilerOptions() { + let configJson = { compilerOptions: { + allowJs: true, + noEmit: true, + declaration: false, + skipLibCheck: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true }, @@ -664,10 +802,17 @@ async function createLanguageService( // with potentially completely unrelated .ts/.js files: include: [] }; - } - function getDefaultExclude() { - return ['node_modules', ...ignoredBuildDirectories]; + const parsedConfig = ts.parseJsonConfigFileContent(configJson, tsSystem, workspacePath); + + const compilerOptions: ts.CompilerOptions = { + ...parsedConfig.options, + target: ts.ScriptTarget.Latest, + allowNonTsExtensions: true, + moduleResolution: ts.ModuleResolutionKind.Node10 + }; + + return { compilerOptions, parsedConfig }; } /** @@ -694,6 +839,7 @@ async function createLanguageService( } function dispose() { + compilerHost = undefined; languageService.dispose(); snapshotManager.dispose(); configWatchers.get(tsconfigPath)?.close(); @@ -702,19 +848,23 @@ async function createLanguageService( docContext.globalSnapshotsManager.removeChangeListener(scheduleUpdate); } - function updateExtendedConfigDependents() { - extendedConfigPaths.forEach((extendedConfig) => { - let dependedTsConfig = extendedConfigToTsConfigPath.get(extendedConfig); + function watchConfigFiles( + extendedConfigPaths: Set | undefined, + parsedCommandLine: ts.ParsedCommandLine + ) { + const tsconfigDependencies = Array.from(extendedConfigPaths ?? []).concat( + parsedCommandLine.projectReferences?.map((r) => r.path) ?? [] + ); + tsconfigDependencies.forEach((configPath) => { + let dependedTsConfig = configPathToDependedProject.get(configPath); if (!dependedTsConfig) { dependedTsConfig = new FileSet(tsSystem.useCaseSensitiveFileNames); - extendedConfigToTsConfigPath.set(extendedConfig, dependedTsConfig); + configPathToDependedProject.set(configPath, dependedTsConfig); } dependedTsConfig.add(tsconfigPath); }); - } - function watchConfigFile() { if (!tsSystem.watchFile || !docContext.watchTsConfig) { return; } @@ -728,16 +878,16 @@ async function createLanguageService( ); } - for (const config of extendedConfigPaths) { - if (extendedConfigWatchers.has(config)) { + for (const config of tsconfigDependencies) { + if (dependedConfigWatchers.has(config)) { continue; } configFileModifiedTime.set(config, tsSystem.getModifiedTime?.(config)); - extendedConfigWatchers.set( + dependedConfigWatchers.set( config, // for some reason setting the polling interval is necessary, else some error in TS is thrown - tsSystem.watchFile(config, createWatchExtendedConfigCallback(docContext), 1000) + tsSystem.watchFile(config, createWatchDependedConfigCallback(docContext), 1000) ); } } @@ -763,7 +913,8 @@ async function createLanguageService( configFileForOpenFiles.clear(); } - docContext.onProjectReloaded?.(); + docContext.onProjectReloaded?.([fileName]); + docContext.reportConfigError?.({ uri: pathToUrl(fileName), diagnostics: [] }); } function updateIfDirty() { @@ -780,6 +931,7 @@ async function createLanguageService( } dirty = false; + compilerHost = undefined; // https://github.com/microsoft/TypeScript/blob/23faef92703556567ddbcb9afb893f4ba638fc20/src/server/project.ts#L1624 // host.getCachedExportInfoMap will create the cache if it doesn't exist @@ -791,6 +943,9 @@ async function createLanguageService( } exportMapCache.releaseSymbols(); + // https://github.com/microsoft/TypeScript/blob/941d1543c201e40d87e63c9db04818493afdd9e7/src/server/project.ts#L1731 + // if one file change results in clearing the cache + // don't continue to check other files, this will mark the cache as usable while it's empty for (const fileName of changedFilesForExportCache) { const oldFile = oldProgram.getSourceFile(fileName); const newFile = program?.getSourceFile(fileName); @@ -800,11 +955,15 @@ async function createLanguageService( continue; } - if (oldFile && newFile) { - exportMapCache.onFileChanged?.(oldFile, newFile, false); - } else { + if (!oldFile || !newFile) { // new file or deleted file exportMapCache.clear(); + break; + } + + const cleared = exportMapCache.onFileChanged?.(oldFile, newFile, false); + if (cleared) { + break; } } changedFilesForExportCache.clear(); @@ -875,6 +1034,141 @@ async function createLanguageService( namespace: transformationConfig.typingsNamespace }; } + + function ensureTsConfigInfoUpToDate(configFilePath: string) { + const cached = parsedTsConfigInfo.get(configFilePath); + if (cached !== undefined) { + ensureFilesForConfigUpdates(cached); + return cached; + } + + const content = tsSystem.fileExists(configFilePath) && tsSystem.readFile(configFilePath); + if (!content) { + parsedTsConfigInfo.set(configFilePath, null); + return null; + } + + const json = ts.parseJsonText(configFilePath, content); + + const extendedConfigPaths = new Set(); + const { extendedConfigCache } = docContext; + const cacheMonitorProxy = { + ...docContext.extendedConfigCache, + get(key: string) { + extendedConfigPaths.add(key); + return extendedConfigCache.get(key); + }, + has(key: string) { + extendedConfigPaths.add(key); + return extendedConfigCache.has(key); + }, + set(key: string, value: ts.ExtendedConfigCacheEntry) { + extendedConfigPaths.add(key); + return extendedConfigCache.set(key, value); + } + }; + + // TypeScript will throw if the parsedCommandLine doesn't include the sourceFile for the config file + // i.e. it must be directly parse from the json text instead of a javascript object like we do in getParsedConfig + const parsedCommandLine = ts.parseJsonSourceFileConfigFileContent( + json, + tsSystem, + dirname(configFilePath), + /*existingOptions*/ undefined, + configFilePath, + /*resolutionStack*/ undefined, + [ + { + extension: 'svelte', + isMixedContent: true, + // Deferred was added in a later TS version, fall back to tsx + // If Deferred exists, this means that all Svelte files are included + // in parsedConfig.fileNames + scriptKind: ts.ScriptKind.Deferred ?? ts.ScriptKind.TS + } + ], + cacheMonitorProxy + ); + + parsedCommandLine.options.allowNonTsExtensions = true; + + const snapshotManager = createSnapshotManager(parsedCommandLine, configFilePath); + + const tsconfigInfo: TsConfigInfo = { + parsedCommandLine, + snapshotManager, + pendingProjectFileUpdate: false, + configFilePath, + extendedConfigPaths + }; + parsedTsConfigInfo.set(configFilePath, tsconfigInfo); + + watchConfigFiles(extendedConfigPaths, parsedCommandLine); + + return tsconfigInfo; + } + + function getParsedCommandLine(configFilePath: string) { + return ensureTsConfigInfoUpToDate(configFilePath)?.parsedCommandLine; + } + + function ensureFilesForConfigUpdates(info: TsConfigInfo | null) { + if (info?.pendingProjectFileUpdate) { + info.pendingProjectFileUpdate = false; + info.snapshotManager.updateProjectFiles(); + info.parsedCommandLine.fileNames = info.snapshotManager.getProjectFileNames(); + } + } + + function getResolvedProjectReferences(): TsConfigInfo[] { + if (!tsconfigPath || !projectConfig.projectReferences) { + return []; + } + + return projectConfig.projectReferences + .map((ref) => ensureTsConfigInfoUpToDate(normalizePath(ref.path))) + .filter(isNotNullOrUndefined); + } + + function openVirtualDocument(document: Document) { + const filePath = document.getFilePath(); + if (!filePath) { + return; + } + configFileForOpenFiles.set(filePath, tsconfigPath || workspacePath); + updateSnapshot(document); + scheduleUpdate(filePath); + } + + function getSvelteShimFiles() { + const isSvelte3 = sveltePackageInfo.version.major === 3; + const svelteHtmlDeclaration = isSvelte3 + ? undefined + : join(sveltePackageInfo.path, 'svelte-html.d.ts'); + const svelteHtmlFallbackIfNotExist = + svelteHtmlDeclaration && tsSystem.fileExists(svelteHtmlDeclaration) + ? svelteHtmlDeclaration + : './svelte-jsx-v4.d.ts'; + + const svelteTsxFiles = ( + isSvelte3 + ? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts'] + : [ + './svelte-shims-v4.d.ts', + svelteHtmlFallbackIfNotExist, + './svelte-native-jsx.d.ts' + ] + ).map((f) => tsSystem.resolvePath(resolve(svelteTsPath, f))); + + const result = new FileSet(tsSystem.useCaseSensitiveFileNames); + + svelteTsxFiles.forEach((f) => result.add(normalizePath(f))); + return result; + } + + function isShimFiles(filePath: string) { + return svelteTsxFiles.has(normalizePath(filePath)); + } } /** @@ -934,7 +1228,7 @@ function exceedsTotalSizeLimitForNonTsFiles( * because it would reference the closure * So that GC won't drop it and cause memory leaks */ -function createWatchExtendedConfigCallback(docContext: LanguageServiceDocumentContext) { +function createWatchDependedConfigCallback(docContext: LanguageServiceDocumentContext) { return async ( fileName: string, kind: ts.FileWatcherEventKind, @@ -950,10 +1244,18 @@ function createWatchExtendedConfigCallback(docContext: LanguageServiceDocumentCo return; } + const getCanonicalFileName = createGetCanonicalFileName( + docContext.tsSystem.useCaseSensitiveFileNames + ); + + docContext.extendedConfigCache.delete(getCanonicalFileName(fileName)); + // rely on TypeScript internal behavior so delete both just in case docContext.extendedConfigCache.delete(fileName); - const promises = Array.from(extendedConfigToTsConfigPath.get(fileName) ?? []).map( + const reloadingConfigs: string[] = []; + const promises = Array.from(configPathToDependedProject.get(fileName) ?? []).map( async (config) => { + reloadingConfigs.push(config); const oldService = services.get(config); scheduleReload(config); (await oldService)?.dispose(); @@ -961,7 +1263,7 @@ function createWatchExtendedConfigCallback(docContext: LanguageServiceDocumentCo ); await Promise.all(promises); - docContext.onProjectReloaded?.(); + docContext.onProjectReloaded?.(reloadingConfigs); }; } diff --git a/packages/language-server/src/plugins/typescript/serviceCache.ts b/packages/language-server/src/plugins/typescript/serviceCache.ts index 379a0306f..d91b05b32 100644 --- a/packages/language-server/src/plugins/typescript/serviceCache.ts +++ b/packages/language-server/src/plugins/typescript/serviceCache.ts @@ -81,8 +81,10 @@ export function createProject( 'getSymlinkCache', 'getPackageJsonsVisibleToFile', 'getPackageJsonAutoImportProvider', - 'includePackageJsonAutoImports', - 'useSourceOfProjectReferenceRedirect' + 'includePackageJsonAutoImports' + // Volar doesn't have the "languageServiceReducedMode" support but we do + // so don't proxy this method and implement this directly in the ts.LanguageServiceHost + // 'useSourceOfProjectReferenceRedirect' ]; proxyMethods.forEach((key) => ((host as any)[key] = project[key].bind(project))); diff --git a/packages/language-server/src/plugins/typescript/svelte-sys.ts b/packages/language-server/src/plugins/typescript/svelte-sys.ts index 4fb3c205c..a639b2a58 100644 --- a/packages/language-server/src/plugins/typescript/svelte-sys.ts +++ b/packages/language-server/src/plugins/typescript/svelte-sys.ts @@ -20,12 +20,18 @@ export function createSvelteSys(tsSystem: ts.System) { } } + function getRealSveltePathIfExists(path: string) { + return svelteFileExists(path) ? toRealSvelteFilePath(path) : path; + } + const svelteSys: ts.System & { deleteFromCache: (path: string) => void; svelteFileExists: (path: string) => boolean; + getRealSveltePathIfExists: (path: string) => string; } = { ...tsSystem, svelteFileExists, + getRealSveltePathIfExists, fileExists(path: string) { // We need to check both .svelte and .svelte.ts/js because that's how Svelte 5 will likely mark files with runes in them const sveltePathExists = svelteFileExists(path); @@ -36,7 +42,7 @@ export function createSvelteSys(tsSystem: ts.System) { }, readFile(path: string) { // No getSnapshot here, because TS will very rarely call this and only for files that are not in the project - return tsSystem.readFile(svelteFileExists(path) ? toRealSvelteFilePath(path) : path); + return tsSystem.readFile(getRealSveltePathIfExists(path)); }, readDirectory(path, extensions, exclude, include, depth) { const extensionsWithSvelte = extensions ? [...extensions, '.svelte'] : undefined; diff --git a/packages/language-server/src/server.ts b/packages/language-server/src/server.ts index 3fffda855..d19e56dcc 100644 --- a/packages/language-server/src/server.ts +++ b/packages/language-server/src/server.ts @@ -199,7 +199,10 @@ export function startServer(options?: LSOptions) { onProjectReloaded: refreshCrossFilesSemanticFeatures, watch: true, nonRecursiveWatchPattern, - watchDirectory: (patterns) => watchDirectory(patterns) + watchDirectory: (patterns) => watchDirectory(patterns), + reportConfigError(diagnostic) { + connection?.sendDiagnostics(diagnostic); + } }), normalizedWorkspaceUris, docManager diff --git a/packages/language-server/src/svelte-check.ts b/packages/language-server/src/svelte-check.ts index b6100e60c..de8e54f56 100644 --- a/packages/language-server/src/svelte-check.ts +++ b/packages/language-server/src/svelte-check.ts @@ -18,6 +18,7 @@ import { JSOrTSDocumentSnapshot } from './plugins/typescript/DocumentSnapshot'; import { isInGeneratedCode } from './plugins/typescript/features/utils'; import { convertRange, getDiagnosticTag, mapSeverity } from './plugins/typescript/utils'; import { pathToUrl, urlToPath } from './utils'; +import { groupBy } from 'lodash'; export type SvelteCheckDiagnosticSource = 'js' | 'css' | 'svelte'; @@ -188,10 +189,36 @@ export class SvelteCheck { private async getDiagnosticsForTsconfig(tsconfigPath: string) { const lsContainer = await this.getLSContainer(tsconfigPath); + const map = (diagnostic: ts.Diagnostic, range?: Range): Diagnostic => { + const file = diagnostic.file; + range ??= file + ? convertRange( + { positionAt: file.getLineAndCharacterOfPosition.bind(file) }, + diagnostic + ) + : { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }; - const noInputsFoundError = lsContainer.configErrors?.find((e) => e.code === 18003); - if (noInputsFoundError) { - throw new Error(noInputsFoundError.messageText.toString()); + return { + range: range, + severity: mapSeverity(diagnostic.category), + source: diagnostic.source, + message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'), + code: diagnostic.code, + tags: getDiagnosticTag(diagnostic) + }; + }; + + if (lsContainer.configErrors) { + const grouped = groupBy( + lsContainer.configErrors, + (error) => error.file?.fileName ?? tsconfigPath + ); + + return Object.entries(grouped).map(([filePath, errors]) => ({ + filePath, + text: '', + diagnostics: errors.map((diagnostic) => map(diagnostic)) + })); } const lang = lsContainer.getService(); @@ -211,6 +238,7 @@ export class SvelteCheck { const skipDiagnosticsForFile = (options.skipLibCheck && file.isDeclarationFile) || (options.skipDefaultLibCheck && file.hasNoDefaultLib) || + lsContainer.isShimFiles(file.fileName) || // ignore JS files in node_modules /\/node_modules\/.+\.(c|m)?js$/.test(file.fileName); const snapshot = lsContainer.snapshotManager.get(file.fileName) as @@ -218,20 +246,6 @@ export class SvelteCheck { | undefined; const isKitFile = snapshot?.kitFile ?? false; const diagnostics: Diagnostic[] = []; - const map = (diagnostic: ts.Diagnostic, range?: Range) => ({ - range: - range ?? - convertRange( - { positionAt: file.getLineAndCharacterOfPosition.bind(file) }, - diagnostic - ), - severity: mapSeverity(diagnostic.category), - source: diagnostic.source, - message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'), - code: diagnostic.code, - tags: getDiagnosticTag(diagnostic) - }); - if (!skipDiagnosticsForFile) { const originalDiagnostics = [ ...lang.getSyntacticDiagnostics(file.fileName), diff --git a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts index 92601459f..dbccee6e5 100644 --- a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts @@ -98,7 +98,7 @@ describe('DiagnosticsProvider', function () { ); const newFilePath = normalizePath(path.join(testDir, 'empty-export.ts')) || ''; - await lsAndTsDocResolver.getSnapshot(newFilePath); + await lsAndTsDocResolver.getOrCreateSnapshot(newFilePath); const diagnostics1 = await plugin.getDiagnostics(document); assert.deepStrictEqual( diff --git a/packages/language-server/test/plugins/typescript/features/FindReferencesProvider.test.ts b/packages/language-server/test/plugins/typescript/features/FindReferencesProvider.test.ts index ecd5d4f5b..12509c1c6 100644 --- a/packages/language-server/test/plugins/typescript/features/FindReferencesProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/FindReferencesProvider.test.ts @@ -31,7 +31,11 @@ describe('FindReferencesProvider', function () { (textDocument) => new Document(textDocument.uri, textDocument.text) ); const lsConfigManager = new LSConfigManager(); - const lsAndTsDocResolver = new LSAndTSDocResolver(docManager, [testDir], lsConfigManager); + const lsAndTsDocResolver = new LSAndTSDocResolver( + docManager, + [pathToUrl(testDir)], + lsConfigManager + ); const provider = new FindReferencesProviderImpl( lsAndTsDocResolver, new FindComponentReferencesProviderImpl(lsAndTsDocResolver) diff --git a/packages/language-server/test/plugins/typescript/features/HoverProvider.test.ts b/packages/language-server/test/plugins/typescript/features/HoverProvider.test.ts index 17b6d8c35..49e146108 100644 --- a/packages/language-server/test/plugins/typescript/features/HoverProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/HoverProvider.test.ts @@ -25,7 +25,11 @@ describe('HoverProvider', function () { (textDocument) => new Document(textDocument.uri, textDocument.text) ); const lsConfigManager = new LSConfigManager(); - const lsAndTsDocResolver = new LSAndTSDocResolver(docManager, [testDir], lsConfigManager); + const lsAndTsDocResolver = new LSAndTSDocResolver( + docManager, + [pathToUrl(testDir)], + lsConfigManager + ); const provider = new HoverProviderImpl(lsAndTsDocResolver); const document = openDoc(filename); return { provider, document }; diff --git a/packages/language-server/test/plugins/typescript/features/ImplemenationProvider.test.ts b/packages/language-server/test/plugins/typescript/features/ImplemenationProvider.test.ts index 57ad86192..152542342 100644 --- a/packages/language-server/test/plugins/typescript/features/ImplemenationProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/ImplemenationProvider.test.ts @@ -29,7 +29,7 @@ describe('ImplementationProvider', function () { ); const lsAndTsDocResolver = new LSAndTSDocResolver( docManager, - [testDir], + [pathToUrl(testDir)], new LSConfigManager() ); const provider = new ImplementationProviderImpl(lsAndTsDocResolver); diff --git a/packages/language-server/test/plugins/typescript/features/TypeDefinitionProvider.test.ts b/packages/language-server/test/plugins/typescript/features/TypeDefinitionProvider.test.ts index f4e0baa44..e709bbd3c 100644 --- a/packages/language-server/test/plugins/typescript/features/TypeDefinitionProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/TypeDefinitionProvider.test.ts @@ -29,7 +29,7 @@ describe('TypeDefinitionProvider', function () { ); const lsAndTsDocResolver = new LSAndTSDocResolver( docManager, - [testDir], + [pathToUrl(testDir)], new LSConfigManager() ); const provider = new TypeDefinitionProviderImpl(lsAndTsDocResolver); diff --git a/packages/language-server/test/plugins/typescript/features/UpdateImportsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/UpdateImportsProvider.test.ts index 998896aa3..cdba08f4b 100644 --- a/packages/language-server/test/plugins/typescript/features/UpdateImportsProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/UpdateImportsProvider.test.ts @@ -33,7 +33,10 @@ describe('UpdateImportsProviderImpl', function () { new LSConfigManager(), { tsSystem: { ...ts.sys, useCaseSensitiveFileNames } } ); - const updateImportsProvider = new UpdateImportsProviderImpl(lsAndTsDocResolver); + const updateImportsProvider = new UpdateImportsProviderImpl( + lsAndTsDocResolver, + useCaseSensitiveFileNames + ); const filePath = join(updateImportTestDir, filename); const fileUri = pathToUrl(filePath); const document = docManager.openClientDocument({ diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/expectedv2.json new file mode 100644 index 000000000..ddef317a6 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/expectedv2.json @@ -0,0 +1,10 @@ +[ + { + "range": { "start": { "line": 6, "character": 8 }, "end": { "line": 6, "character": 9 } }, + "severity": 1, + "source": "ts", + "message": "Type 'string' is not assignable to type 'number'.", + "code": 2322, + "tags": [] + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/imported.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/imported.ts new file mode 100644 index 000000000..bc8481bbb --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/imported.ts @@ -0,0 +1,5 @@ +/** + * + * @param cb callback because if the module resolution failed there will be a noImplicitAny error + */ +export function hi(cb: (num: number) => string) {} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/input.svelte new file mode 100644 index 000000000..3b505cd2c --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/input.svelte @@ -0,0 +1,8 @@ + diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub2.json new file mode 100644 index 000000000..310ef4967 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub2.json @@ -0,0 +1,7 @@ +{ + "include": [], + "compilerOptions": { + "composite": true + }, + "references": [{ "path": "./tsconfig_sub3.json" }] +} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub3.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub3.json new file mode 100644 index 000000000..f96815577 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/nested/tsconfig_sub3.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "strict": true, + "paths": { + "hi2": ["./imported.ts"] + }, + "types": [] + } +} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/expectedv2.json new file mode 100644 index 000000000..3d707cc47 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/expectedv2.json @@ -0,0 +1,10 @@ +[ + { + "range": { "start": { "line": 6, "character": 4 }, "end": { "line": 6, "character": 5 } }, + "severity": 1, + "source": "ts", + "message": "Type 'string' is not assignable to type 'number'.", + "code": 2322, + "tags": [] + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/imported.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/imported.ts new file mode 100644 index 000000000..bc8481bbb --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/imported.ts @@ -0,0 +1,5 @@ +/** + * + * @param cb callback because if the module resolution failed there will be a noImplicitAny error + */ +export function hi(cb: (num: number) => string) {} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/input.svelte new file mode 100644 index 000000000..5e19507ef --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/input.svelte @@ -0,0 +1,8 @@ + diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/tsconfig_sub.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/tsconfig_sub.json new file mode 100644 index 000000000..384062fc0 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/paths/tsconfig_sub.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "composite": true, + "strict": true, + "paths": { + "hi": ["./imported.ts"] + }, + "types": [] + } +} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/tsconfig.json new file mode 100644 index 000000000..59b56bf8f --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/project-reference/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": [], + "references": [ + { + "path": "./paths/tsconfig_sub.json" + }, + { + "path": "./nested/tsconfig_sub2.json" + } + ] +} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/tsconfig.json index f8add4155..da66f1d47 100644 --- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/tsconfig.json +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/tsconfig.json @@ -1,11 +1,13 @@ { "compilerOptions": { "strict": true, + "allowJs": true, + "target": "ESNext", /** This is actually not needed, but makes the tests faster because TS does not look up other types. */ "types": ["svelte"] }, - "exclude": ["./svelte-native/**/*", "./node16/**/*"] + "exclude": ["./svelte-native/**/*", "./node16/**/*", "project-reference/**/*"] } diff --git a/packages/language-server/test/plugins/typescript/features/inlayHints/fixtures/tsconfig.json b/packages/language-server/test/plugins/typescript/features/inlayHints/fixtures/tsconfig.json new file mode 100644 index 000000000..f493a66ae --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/inlayHints/fixtures/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "allowJs": true, + "target": "ESNext", + "strict": true, + /** + This is actually not needed, but makes the tests faster + because TS does not look up other types. + */ + "types": ["svelte"] + } +} diff --git a/packages/language-server/test/plugins/typescript/module-loader.test.ts b/packages/language-server/test/plugins/typescript/module-loader.test.ts index d08e42682..559eeb447 100644 --- a/packages/language-server/test/plugins/typescript/module-loader.test.ts +++ b/packages/language-server/test/plugins/typescript/module-loader.test.ts @@ -21,8 +21,11 @@ describe('createSvelteModuleLoader', () => { const moduleCacheMock = { getPackageJsonInfoCache: () => ({}) }; + const moduleResolutionHost = { ...ts.sys }; - const svelteSys = 'svelteSys'; + const svelteSys = { + ...svS.createSvelteSys(ts.sys) + }; sinon.stub(svS, 'createSvelteSys').returns(svelteSys); const compilerOptions: ts.CompilerOptions = { strict: true, paths: { '/@/*': [] } }; @@ -34,7 +37,8 @@ describe('createSvelteModuleLoader', () => { ...ts, createModuleResolutionCache: () => moduleCacheMock, resolveModuleName: resolveStub - } + }, + () => moduleResolutionHost ); return { @@ -43,7 +47,8 @@ describe('createSvelteModuleLoader', () => { resolveStub, compilerOptions, moduleResolver, - svelteSys + svelteSys, + moduleResolutionHost }; } @@ -51,132 +56,18 @@ describe('createSvelteModuleLoader', () => { return stub.getCall(stub.getCalls().length - 1); } - it('uses tsSys for normal files', async () => { - const resolvedModule: ts.ResolvedModuleFull = { - extension: ts.Extension.Ts, - resolvedFileName: 'filename.ts' - }; - const { resolveStub, moduleResolver, compilerOptions, moduleCacheMock } = - setup(resolvedModule); - const result = moduleResolver.resolveModuleNames( - ['./normal.ts'], - 'C:/somerepo/somefile.svelte', - undefined, - undefined, - undefined as any - ); - - assert.deepStrictEqual(result, [resolvedModule]); - assert.deepStrictEqual(lastCall(resolveStub).args, [ - './normal.ts', - 'C:/somerepo/somefile.svelte', - compilerOptions, - ts.sys, - moduleCacheMock, - undefined, - undefined - ]); - }); - - it('uses tsSys for normal files part of TS aliases', async () => { - const resolvedModule: ts.ResolvedModuleFull = { - extension: ts.Extension.Ts, - resolvedFileName: 'filename.ts' - }; - const { resolveStub, moduleResolver, compilerOptions, moduleCacheMock } = - setup(resolvedModule); - const result = moduleResolver.resolveModuleNames( - ['/@/normal'], - 'C:/somerepo/somefile.svelte', - undefined, - undefined, - undefined as any - ); - - assert.deepStrictEqual(result, [resolvedModule]); - assert.deepStrictEqual(lastCall(resolveStub).args, [ - '/@/normal', - 'C:/somerepo/somefile.svelte', - compilerOptions, - ts.sys, - moduleCacheMock, - undefined, - undefined - ]); - }); - - it('uses tsSys for svelte.d.ts files', async () => { - const resolvedModule: ts.ResolvedModuleFull = { - extension: ts.Extension.Dts, - resolvedFileName: 'filename.d.ts' - }; - const { resolveStub, moduleResolver, compilerOptions, moduleCacheMock } = - setup(resolvedModule); - const result = moduleResolver.resolveModuleNames( - ['./normal.ts'], - 'C:/somerepo/somefile.svelte', - undefined, - undefined, - undefined as any - ); - - assert.deepStrictEqual(result, [resolvedModule]); - assert.deepStrictEqual(lastCall(resolveStub).args, [ - './normal.ts', - 'C:/somerepo/somefile.svelte', - compilerOptions, - ts.sys, - moduleCacheMock, - undefined, - undefined - ]); - }); - - it('uses svelte module loader for virtual svelte files', async () => { + it('uses svelte script kind if resolved module is svelte file', async () => { const resolvedModule: ts.ResolvedModuleFull = { extension: ts.Extension.Ts, resolvedFileName: 'filename.svelte.ts' }; - const { resolveStub, svelteSys, moduleResolver, compilerOptions, getSvelteSnapshotStub } = - setup(resolvedModule); - resolveStub.onFirstCall().returns({ resolvedModule: undefined }); - const result = moduleResolver.resolveModuleNames( - ['./svelte.svelte'], - 'C:/somerepo/somefile.svelte', - undefined, - undefined, - undefined as any - ); + const { getSvelteSnapshotStub, moduleResolver, svelteSys } = setup(resolvedModule); - assert.deepStrictEqual(result, [ - { - extension: ts.Extension.Jsx, - resolvedFileName: 'filename.svelte', - isExternalLibraryImport: undefined - } - ]); - assert.deepStrictEqual(lastCall(resolveStub).args, [ - './svelte.svelte', - 'C:/somerepo/somefile.svelte', - compilerOptions, - svelteSys, - undefined, - undefined, - undefined - ]); - assert.deepStrictEqual(lastCall(getSvelteSnapshotStub).args, ['filename.svelte']); - }); + svelteSys.getRealSveltePathIfExists = (filename: string) => + filename === 'filename.svelte.ts' ? 'filename.svelte' : filename; - it('uses svelte module loader for virtual svelte files with TS path aliases', async () => { - const resolvedModule: ts.ResolvedModuleFull = { - extension: ts.Extension.Ts, - resolvedFileName: 'filename.svelte.ts' - }; - const { resolveStub, svelteSys, moduleResolver, compilerOptions, getSvelteSnapshotStub } = - setup(resolvedModule); - resolveStub.onFirstCall().returns({ resolvedModule: undefined }); const result = moduleResolver.resolveModuleNames( - ['/@/svelte.svelte'], + ['./normal.ts'], 'C:/somerepo/somefile.svelte', undefined, undefined, @@ -190,15 +81,6 @@ describe('createSvelteModuleLoader', () => { isExternalLibraryImport: undefined } ]); - assert.deepStrictEqual(lastCall(resolveStub).args, [ - '/@/svelte.svelte', - 'C:/somerepo/somefile.svelte', - compilerOptions, - svelteSys, - undefined, - undefined, - undefined - ]); assert.deepStrictEqual(lastCall(getSvelteSnapshotStub).args, ['filename.svelte']); }); diff --git a/packages/language-server/test/plugins/typescript/service.test.ts b/packages/language-server/test/plugins/typescript/service.test.ts index 02cac2d21..50c793c6f 100644 --- a/packages/language-server/test/plugins/typescript/service.test.ts +++ b/packages/language-server/test/plugins/typescript/service.test.ts @@ -1,16 +1,17 @@ import assert from 'assert'; import path from 'path'; +import sinon from 'sinon'; import ts from 'typescript'; +import { RelativePattern } from 'vscode-languageserver-protocol'; import { Document } from '../../../src/lib/documents'; import { GlobalSnapshotsManager } from '../../../src/plugins/typescript/SnapshotManager'; import { + LanguageServiceContainer, LanguageServiceDocumentContext, getService } from '../../../src/plugins/typescript/service'; -import { pathToUrl } from '../../../src/utils'; +import { normalizePath, pathToUrl } from '../../../src/utils'; import { createVirtualTsSystem, getRandomVirtualDirPath } from './test-utils'; -import sinon from 'sinon'; -import { RelativePattern } from 'vscode-languageserver-protocol'; describe('service', () => { const testDir = path.join(__dirname, 'testfiles'); @@ -33,7 +34,8 @@ describe('service', () => { onProjectReloaded: undefined, projectService: undefined, nonRecursiveWatchPattern: undefined, - watchDirectory: undefined + watchDirectory: undefined, + reportConfigError: undefined }; return { virtualSystem, lsDocumentContext, rootUris }; @@ -53,6 +55,11 @@ describe('service', () => { }) ); + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + const ls = await getService( path.join(dirPath, 'random.svelte'), rootUris, @@ -63,11 +70,117 @@ describe('service', () => { delete ls.compilerOptions.configFilePath; assert.deepStrictEqual(ls.compilerOptions, { - allowJs: true, allowNonTsExtensions: true, checkJs: true, strict: true, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Node10, + target: ts.ScriptTarget.ESNext + }); + }); + + it('errors if tsconfig matches no svelte files', async () => { + const dirPath = getRandomVirtualDirPath(testDir); + const { virtualSystem, lsDocumentContext, rootUris } = setup(); + + virtualSystem.readDirectory = () => [path.join(dirPath, 'random.ts')]; + + virtualSystem.writeFile( + path.join(dirPath, 'tsconfig.json'), + JSON.stringify({ + include: ['**/*.ts'] + }) + ); + + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + + let called = false; + await getService(path.join(dirPath, 'random.svelte'), rootUris, { + ...lsDocumentContext, + reportConfigError: (message) => { + called = true; + assert.equal(message.uri, pathToUrl(path.join(dirPath, 'tsconfig.json'))); + } + }); + assert.ok(called); + }); + + it('do not errors if referenced tsconfig matches no svelte files', async () => { + const dirPath = getRandomVirtualDirPath(testDir); + const { virtualSystem, lsDocumentContext, rootUris } = setup(); + + const tsPattern = '**/*.ts'; + const sveltePattern = '**/*.svelte'; + virtualSystem.readDirectory = (_path, _extensions, _excludes, include) => { + return include?.[0] === tsPattern + ? [path.join(dirPath, 'random.ts')] + : include?.[0] === sveltePattern + ? [path.join(dirPath, 'random.svelte')] + : []; + }; + + virtualSystem.writeFile( + path.join(dirPath, 'tsconfig.json'), + JSON.stringify({ + include: [], + references: [{ path: './tsconfig_node.json' }, { path: './tsconfig_web.json' }] + }) + ); + + virtualSystem.writeFile( + path.join(dirPath, 'tsconfig_node.json'), + JSON.stringify({ + include: [tsPattern] + }) + ); + + virtualSystem.writeFile( + path.join(dirPath, 'tsconfig_web.json'), + JSON.stringify({ + include: [sveltePattern] + }) + ); + + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + + let called = false; + const lsContainer = await getService(path.join(dirPath, 'random.svelte'), rootUris, { + ...lsDocumentContext, + reportConfigError: () => { + called = true; + } + }); + + assert.equal( + normalizePath(path.join(dirPath, 'tsconfig_web.json')), + lsContainer.tsconfigPath + ); + assert.equal(called, false, 'expected not to call reportConfigError'); + }); + + it('can loads default tsconfig', async () => { + const dirPath = getRandomVirtualDirPath(testDir); + const { lsDocumentContext, rootUris } = setup(); + + const ls = await getService( + path.join(dirPath, 'random.svelte'), + rootUris, + lsDocumentContext + ); + + assert.deepStrictEqual(ls.compilerOptions, { + allowJs: true, + allowSyntheticDefaultImports: true, + allowNonTsExtensions: true, + configFilePath: undefined, declaration: false, + maxNodeModuleJsDepth: 2, module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Node10, noEmit: true, @@ -93,6 +206,11 @@ describe('service', () => { }) ); + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + const ls = await getService( path.join(dirPath, 'random.svelte'), rootUris, @@ -125,21 +243,24 @@ describe('service', () => { function createReloadTester( docContext: LanguageServiceDocumentContext, - testAfterReload: () => Promise + testAfterReload: (reloadingConfigs: string[]) => Promise ) { let _resolve: () => void; - const reloadPromise = new Promise((resolve) => { + let _reject: (e: unknown) => void; + const reloadPromise = new Promise((resolve, reject) => { _resolve = resolve; + _reject = reject; }); return { docContextWithReload: { ...docContext, - async onProjectReloaded() { + async onProjectReloaded(reloadingConfigs: string[]) { try { - await testAfterReload(); - } finally { + await testAfterReload(reloadingConfigs); _resolve(); + } catch (e) { + _reject(e); } } }, @@ -161,6 +282,11 @@ describe('service', () => { }) ); + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + const { reloadPromise, docContextWithReload } = createReloadTester( { ...lsDocumentContext, watchTsConfig: true }, testAfterReload @@ -189,6 +315,8 @@ describe('service', () => { true, 'expected to reload compilerOptions' ); + + return true; } }); @@ -197,7 +325,7 @@ describe('service', () => { const { virtualSystem, lsDocumentContext, rootUris } = setup(); const tsconfigPath = path.join(dirPath, 'tsconfig.json'); const extend = './.svelte-kit/tsconfig.json'; - const extendedConfigPathFull = path.resolve(tsconfigPath, extend); + const extendedConfigPathFull = path.resolve(path.dirname(tsconfigPath), extend); virtualSystem.writeFile( tsconfigPath, @@ -206,6 +334,11 @@ describe('service', () => { }) ); + virtualSystem.writeFile( + path.join(dirPath, 'random.svelte'), + '' + ); + const { reloadPromise, docContextWithReload } = createReloadTester( { ...lsDocumentContext, watchTsConfig: true }, testAfterReload @@ -234,23 +367,78 @@ describe('service', () => { true, 'expected to reload compilerOptions' ); + return true; } }); - it('can open client file that do not exist in fs', async () => { + it('can watch project reference tsconfig', async () => { const dirPath = getRandomVirtualDirPath(testDir); const { virtualSystem, lsDocumentContext, rootUris } = setup(); + const tsconfigPath = path.join(dirPath, 'tsconfig.json'); + const referenced = './tsconfig_node.json'; + const referencedConfigPathFull = path.resolve(path.dirname(tsconfigPath), referenced); virtualSystem.writeFile( - path.join(dirPath, 'tsconfig.json'), + tsconfigPath, + JSON.stringify({ + references: [{ path: referenced }], + include: [] + }) + ); + + virtualSystem.writeFile( + referencedConfigPathFull, JSON.stringify({ compilerOptions: { - checkJs: true, strict: true + }, + files: ['random.ts'] + }) + ); + + const { reloadPromise, docContextWithReload } = createReloadTester( + { ...lsDocumentContext, watchTsConfig: true }, + testAfterReload + ); + + const tsFilePath = path.join(dirPath, 'random.ts'); + virtualSystem.writeFile(tsFilePath, 'const a: number = null;'); + + const ls = await getService(tsFilePath, rootUris, docContextWithReload); + assert.deepStrictEqual(getSemanticDiagnosticsMessages(ls, tsFilePath), [ + "Type 'null' is not assignable to type 'number'." + ]); + + virtualSystem.writeFile( + referencedConfigPathFull, + JSON.stringify({ + compilerOptions: { + strict: false } }) ); + await reloadPromise; + + async function testAfterReload(reloadingConfigs: string[]) { + if (!reloadingConfigs.includes(referencedConfigPathFull)) { + return false; + } + const newLs = await getService(tsFilePath, rootUris, { + ...lsDocumentContext, + watchTsConfig: true + }); + + assert.deepStrictEqual(getSemanticDiagnosticsMessages(newLs, tsFilePath), []); + return true; + } + }); + + it('can open client file that do not exist in fs', async () => { + const dirPath = getRandomVirtualDirPath(testDir); + const { lsDocumentContext, rootUris } = setup(); + + // don't need tsconfig because files doesn't exist in fs goes to a service with default config const ls = await getService( path.join(dirPath, 'random.svelte'), rootUris, @@ -266,6 +454,60 @@ describe('service', () => { }); }); + it('resolve module with source project reference redirect', async () => { + const dirPath = getRandomVirtualDirPath(testDir); + const { virtualSystem, lsDocumentContext, rootUris } = setup(); + + const package1 = path.join(dirPath, 'package1'); + + virtualSystem.writeFile( + path.join(package1, 'tsconfig.json'), + JSON.stringify({ + references: [{ path: '../package2' }], + files: ['index.ts'] + }) + ); + + const package2 = path.join(dirPath, 'package2'); + virtualSystem.writeFile( + path.join(package2, 'tsconfig.json'), + JSON.stringify({ + compilerOptions: { + composite: true, + strict: true + }, + files: ['index.ts'] + }) + ); + + const importing = path.join(package1, 'index.ts'); + virtualSystem.writeFile(importing, 'import { hi } from "package2"; hi((a) => `${a}`);'); + + const imported = path.join(package2, 'index.ts'); + virtualSystem.writeFile(imported, 'export function hi(cb: (num: number) => string) {}'); + + const package2Link = normalizePath(path.join(package1, 'node_modules', 'package2')); + virtualSystem.realpath = (p) => { + if (normalizePath(p).startsWith(package2Link)) { + const sub = p.substring(package2Link.length); + return path.join(package2) + sub; + } + + return p; + }; + + const fileExists = virtualSystem.fileExists; + virtualSystem.fileExists = (p) => { + const realPath = virtualSystem.realpath!(p); + + return fileExists(realPath); + }; + + const ls = await getService(importing, rootUris, lsDocumentContext); + + assert.deepStrictEqual(getSemanticDiagnosticsMessages(ls, importing), []); + }); + it('skip directory watching if directory is root', async () => { const dirPath = getRandomVirtualDirPath(path.join(testDir, 'Test')); const { virtualSystem, lsDocumentContext } = setup(); @@ -322,4 +564,11 @@ describe('service', () => { sinon.assert.calledWith(watchDirectory.firstCall, []); }); + + function getSemanticDiagnosticsMessages(ls: LanguageServiceContainer, filePath: string) { + return ls + .getService() + .getSemanticDiagnostics(filePath) + .map((d) => d.messageText); + } }); diff --git a/packages/language-server/test/plugins/typescript/test-utils.ts b/packages/language-server/test/plugins/typescript/test-utils.ts index c77970256..f615f847f 100644 --- a/packages/language-server/test/plugins/typescript/test-utils.ts +++ b/packages/language-server/test/plugins/typescript/test-utils.ts @@ -6,8 +6,14 @@ import { DocumentManager, Document } from '../../../src/lib/documents'; import { FileMap } from '../../../src/lib/documents/fileCollection'; import { LSConfigManager } from '../../../src/ls-config'; import { LSAndTSDocResolver } from '../../../src/plugins'; -import { createGetCanonicalFileName, normalizePath, pathToUrl } from '../../../src/utils'; +import { + createGetCanonicalFileName, + normalizePath, + pathToUrl, + urlToPath +} from '../../../src/utils'; import { VERSION } from 'svelte/compiler'; +import { findTsConfigPath } from '../../../src/plugins/typescript/utils'; const isSvelte5Plus = Number(VERSION.split('.')[0]) >= 5; @@ -100,7 +106,7 @@ export function createVirtualTsSystem(currentDirectory: string): ts.System { ); } - const normalizedPath = normalizePath(toAbsolute(path)); + const normalizedPath = getCanonicalFileName(normalizePath(toAbsolute(path))); return Array.from(virtualFs.keys()).filter((fileName) => fileName.startsWith(normalizedPath) ); @@ -110,6 +116,11 @@ export function createVirtualTsSystem(currentDirectory: string): ts.System { return virtualSystem; function triggerWatch(normalizedPath: string, kind: ts.FileWatcherEventKind) { + // if watcher is not set yet. don't trigger it + if (!watchers.has(normalizedPath)) { + return; + } + let timeoutsOfPath = watchTimeout.get(normalizedPath); if (!timeoutsOfPath) { @@ -273,37 +284,56 @@ export async function createJsonSnapshotFormatter(dir: string) { }); } -export function serviceWarmup(suite: Mocha.Suite, testDir: string, rootUri = pathToUrl(testDir)) { +export function serviceWarmup( + suite: Mocha.Suite, + testDir: string, + rootUri = pathToUrl(testDir), + tsconfigPath: string | undefined = undefined +) { const defaultTimeout = suite.timeout(); // allow to set a higher timeout for slow machines from cli flag const warmupTimeout = Math.max(defaultTimeout, 5_000); suite.timeout(warmupTimeout); - before(async () => { + before(() => warmup(tsconfigPath)); + + suite.timeout(defaultTimeout); + + async function warmup(configFilePath: string | undefined = undefined) { const start = Date.now(); console.log('Warming up language service...'); const docManager = new DocumentManager( (textDocument) => new Document(textDocument.uri, textDocument.text) ); + const lsAndTsDocResolver = new LSAndTSDocResolver( docManager, [rootUri], new LSConfigManager() ); - const filePath = join(testDir, 'DoesNotMater.svelte'); - const document = docManager.openClientDocument({ - uri: pathToUrl(filePath), - text: ts.sys.readFile(filePath) || '' - }); + configFilePath ??= findTsConfigPath( + join(testDir, 'DoesNotMater.svelte'), + [rootUri], + ts.sys.fileExists, + createGetCanonicalFileName(ts.sys.useCaseSensitiveFileNames) + ); - await lsAndTsDocResolver.getLSAndTSDoc(document); + const ls = await lsAndTsDocResolver.getTSServiceByConfigPath( + configFilePath, + configFilePath ? dirname(configFilePath) : urlToPath(rootUri)! + ); + ls.getService(); - console.log(`Service warming up done in ${Date.now() - start}ms`); - }); + const projectReferences = ls.getResolvedProjectReferences(); - suite.timeout(defaultTimeout); + if (projectReferences.length) { + await Promise.all(projectReferences.map((ref) => warmup(ref.configFilePath))); + } + + console.log(`Service warming up done in ${Date.now() - start}ms`); + } } export function recursiveServiceWarmup( diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json index 89dd24236..8cd68407b 100644 --- a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "allowJs": true, + "target": "ESNext", "strict": true, /** This is actually not needed, but makes the tests faster diff --git a/packages/language-server/test/plugins/typescript/testfiles/tsconfig.json b/packages/language-server/test/plugins/typescript/testfiles/tsconfig.json index 63ec98505..03afba4d9 100644 --- a/packages/language-server/test/plugins/typescript/testfiles/tsconfig.json +++ b/packages/language-server/test/plugins/typescript/testfiles/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "allowJs": true, + "target": "ESNext", "strict": true, /** This is actually not needed, but makes the tests faster diff --git a/packages/svelte-check/src/writers.ts b/packages/svelte-check/src/writers.ts index 37c6823b8..af1fd89d8 100644 --- a/packages/svelte-check/src/writers.ts +++ b/packages/svelte-check/src/writers.ts @@ -57,14 +57,9 @@ export class HumanFriendlyWriter implements Writer { `${workspaceDir}${sep}${pc.green(filename)}:${line + 1}:${character + 1}\n` ); - // Show some context around diagnostic range - const codePrevLine = this.getLine(diagnostic.range.start.line - 1, text); - const codeLine = this.getCodeLine(diagnostic, text); - const codeNextLine = this.getLine(diagnostic.range.end.line + 1, text); - const code = codePrevLine + codeLine + codeNextLine; - let msg; if (this.isVerbose) { + const code = this.formatRelatedCode(diagnostic, text); msg = `${diagnostic.message} ${source}\n${pc.cyan(code)}`; } else { msg = `${diagnostic.message} ${source}`; @@ -80,6 +75,20 @@ export class HumanFriendlyWriter implements Writer { }); } + private formatRelatedCode(diagnostic: Diagnostic, text: string) { + if (!text) { + return ''; + } + + // Show some context around diagnostic range + const codePrevLine = this.getLine(diagnostic.range.start.line - 1, text); + const codeLine = this.getCodeLine(diagnostic, text); + const codeNextLine = this.getLine(diagnostic.range.end.line + 1, text); + const code = codePrevLine + codeLine + codeNextLine; + + return code; + } + private getCodeLine(diagnostic: Diagnostic, text: string) { const startOffset = offsetAt(diagnostic.range.start, text); const endOffset = offsetAt(diagnostic.range.end, text); diff --git a/packages/svelte-check/test/tsconfig.json b/packages/svelte-check/test/tsconfig.json index 15458e89c..86649d013 100644 --- a/packages/svelte-check/test/tsconfig.json +++ b/packages/svelte-check/test/tsconfig.json @@ -1,6 +1,6 @@ { - "extends": "@tsconfig/node12/tsconfig.json", "compilerOptions": { + "target": "ESNext", "moduleResolution": "node", "strict": true, "allowJs": true, From 89a15322c41c1a4ee939810740606138ae1fd55e Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 27 Aug 2024 12:49:14 +0200 Subject: [PATCH 03/12] fix: check error length --- packages/language-server/src/svelte-check.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-server/src/svelte-check.ts b/packages/language-server/src/svelte-check.ts index de8e54f56..652f2d7a7 100644 --- a/packages/language-server/src/svelte-check.ts +++ b/packages/language-server/src/svelte-check.ts @@ -208,7 +208,7 @@ export class SvelteCheck { }; }; - if (lsContainer.configErrors) { + if (lsContainer.configErrors.length > 0) { const grouped = groupBy( lsContainer.configErrors, (error) => error.file?.fileName ?? tsconfigPath From 53856cc7b031cf45020bb991b8726b714fbb5a8b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 27 Aug 2024 21:05:11 +0200 Subject: [PATCH 04/12] breaking: vs code / language-server major (#2475) - use Svelte 4 as built-in version now - add exports map - bump vs code ls/client version to 9 - require Node 18 / VS Code 1.82 minimum - drive-by fix of __sveltets_2_ensureComponent (the previous version didn't work after all) --- package.json | 2 +- packages/language-server/package.json | 25 +- packages/language-server/src/importPackage.ts | 8 - .../features/CallHierarchyProvider.ts | 2 +- .../features/CodeActionsProvider.ts | 2 +- packages/svelte-check/package.json | 4 +- packages/svelte-vscode/package.json | 8 +- packages/svelte-vscode/src/html/autoClose.ts | 2 +- packages/svelte2tsx/package.json | 8 +- .../src/htmlxtojsx_v2/utils/node-utils.ts | 2 +- packages/svelte2tsx/svelte-shims-v4.d.ts | 4 +- packages/typescript-plugin/package.json | 6 +- .../src/language-service/call-hierarchy.ts | 2 +- pnpm-lock.yaml | 336 ++++++++++++------ 14 files changed, 267 insertions(+), 144 deletions(-) diff --git a/package.json b/package.json index 793a32730..6e450086d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "cross-env": "^7.0.2", - "prettier": "~3.2.5", + "prettier": "~3.3.3", "ts-node": "^10.0.0" }, "packageManager": "pnpm@9.3.0" diff --git a/packages/language-server/package.json b/packages/language-server/package.json index f79ba9ede..bae08937a 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -1,9 +1,13 @@ { "name": "svelte-language-server", - "version": "0.16.0", + "version": "0.17.0", "description": "A language server for Svelte", "main": "dist/src/index.js", "typings": "dist/src/index", + "exports": { + "./package.json": "./package.json", + ".": "./dist/src/index.js" + }, "scripts": { "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\"", "build": "tsc", @@ -31,14 +35,13 @@ }, "homepage": "https://github.com/sveltejs/language-tools#readme", "engines": { - "node": ">= 12.0.0" + "node": ">= 18.0.0" }, "devDependencies": { "@types/estree": "^0.0.42", "@types/lodash": "^4.14.116", "@types/mocha": "^9.1.0", - "@types/node": "^16.0.0", - "@types/prettier": "^2.2.3", + "@types/node": "^18.0.0", "@types/sinon": "^7.5.2", "cross-env": "^7.0.2", "mocha": "^9.2.0", @@ -46,23 +49,23 @@ "ts-node": "^10.0.0" }, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.25", "@vscode/emmet-helper": "2.8.4", "chokidar": "^3.4.1", "estree-walker": "^2.0.1", "fdir": "^6.2.0", "lodash": "^4.17.21", - "prettier": "~3.2.5", - "prettier-plugin-svelte": "^3.2.2", - "svelte": "^3.57.0", + "prettier": "~3.3.3", + "prettier-plugin-svelte": "^3.2.6", + "svelte": "^4.2.19", "svelte2tsx": "workspace:~", "typescript": "^5.5.2", "typescript-auto-import-cache": "^0.3.3", "vscode-css-languageservice": "~6.3.0", "vscode-html-languageservice": "~5.3.0", - "vscode-languageserver": "8.0.2", - "vscode-languageserver-protocol": "3.17.2", - "vscode-languageserver-types": "3.17.2", + "vscode-languageserver": "9.0.1", + "vscode-languageserver-protocol": "3.17.5", + "vscode-languageserver-types": "3.17.5", "vscode-uri": "~3.0.0" } } diff --git a/packages/language-server/src/importPackage.ts b/packages/language-server/src/importPackage.ts index 41e040762..7386a6c95 100644 --- a/packages/language-server/src/importPackage.ts +++ b/packages/language-server/src/importPackage.ts @@ -63,14 +63,6 @@ export function importSvelte(fromPath: string): typeof svelte { Logger.debug('Using Svelte v' + pkg.version.full, 'from', main); if (pkg.version.major === 4) { return dynamicRequire(main + '.cjs'); - } else if (pkg.version.major === 5) { - // TODO remove once Svelte 5 is released - // (we switched from compiler.cjs to compiler/index.js at some point) - try { - return dynamicRequire(main); - } catch (e) { - return dynamicRequire(main + '.cjs'); - } } else { return dynamicRequire(main); } diff --git a/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts b/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts index 3d60b40ac..2151038b9 100644 --- a/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CallHierarchyProvider.ts @@ -213,7 +213,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider { .provideCallHierarchyOutgoingCalls(filePath, offset) .concat( isComponentModulePosition - ? this.getOutgoingCallsForComponent(program, filePath) ?? [] + ? (this.getOutgoingCallsForComponent(program, filePath) ?? []) : [] ); diff --git a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts index 59def877c..c114fc4b6 100644 --- a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts @@ -1471,7 +1471,7 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { const position = inModuleScript ? originalRange.start - : this.fixPropsCodeActionRange(originalRange.start, document) ?? originalRange.start; + : (this.fixPropsCodeActionRange(originalRange.start, document) ?? originalRange.start); // fix the length of trailing indent const linesOfNewText = edit.newText.split('\n'); diff --git a/packages/svelte-check/package.json b/packages/svelte-check/package.json index 361c2c33f..634a029f6 100644 --- a/packages/svelte-check/package.json +++ b/packages/svelte-check/package.json @@ -26,7 +26,7 @@ "node": ">= 18.0.0" }, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^3.4.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", @@ -52,7 +52,7 @@ "rollup": "3.7.5", "rollup-plugin-cleanup": "^3.2.0", "rollup-plugin-copy": "^3.4.0", - "svelte": "^3.57.0", + "svelte": "^4.2.19", "svelte-language-server": "workspace:*", "typescript": "^5.5.2", "vscode-languageserver": "8.0.2", diff --git a/packages/svelte-vscode/package.json b/packages/svelte-vscode/package.json index 27ed07e9e..a39165e17 100644 --- a/packages/svelte-vscode/package.json +++ b/packages/svelte-vscode/package.json @@ -37,7 +37,7 @@ "Formatters" ], "engines": { - "vscode": "^1.67.0" + "vscode": "^1.82.0" }, "activationEvents": [ "onLanguage:svelte", @@ -718,7 +718,7 @@ }, "devDependencies": { "@types/lodash": "^4.14.116", - "@types/node": "^16.0.0", + "@types/node": "^18.0.0", "@types/vscode": "^1.67", "js-yaml": "^3.14.0", "tslib": "^2.4.0", @@ -729,7 +729,7 @@ "lodash": "^4.17.21", "svelte-language-server": "workspace:*", "typescript-svelte-plugin": "workspace:*", - "vscode-languageclient": "^8.0.0", - "vscode-languageserver-protocol": "3.17.2" + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-protocol": "3.17.5" } } diff --git a/packages/svelte-vscode/src/html/autoClose.ts b/packages/svelte-vscode/src/html/autoClose.ts index 1dc71b2b3..281bb600f 100644 --- a/packages/svelte-vscode/src/html/autoClose.ts +++ b/packages/svelte-vscode/src/html/autoClose.ts @@ -26,7 +26,7 @@ export function activateTagClosing( updateEnabledState(); window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables); - let timeout: NodeJS.Timer | undefined = void 0; + let timeout: NodeJS.Timeout | undefined = void 0; function updateEnabledState() { isEnabled = false; diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index 34d5ef7be..97f42d5ea 100644 --- a/packages/svelte2tsx/package.json +++ b/packages/svelte2tsx/package.json @@ -18,15 +18,15 @@ "module": "index.mjs", "types": "index.d.ts", "devDependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.25", "@rollup/plugin-commonjs": "^24.0.0", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.0", "@rollup/plugin-typescript": "^10.0.0", "@types/estree": "^0.0.42", "@types/mocha": "^9.1.0", - "@types/node": "^16.0.0", + "@types/node": "^18.0.0", "@types/unist": "^2.0.3", "@types/vfile": "^3.0.2", "builtin-modules": "^3.3.0", @@ -37,7 +37,7 @@ "rollup": "3.7.5", "rollup-plugin-delete": "^2.0.0", "source-map-support": "^0.5.16", - "svelte": "~3.57.0", + "svelte": "~4.2.19", "tiny-glob": "^0.2.6", "tslib": "^2.4.0", "typescript": "^5.5.2" diff --git a/packages/svelte2tsx/src/htmlxtojsx_v2/utils/node-utils.ts b/packages/svelte2tsx/src/htmlxtojsx_v2/utils/node-utils.ts index c523a53f2..d3c95164a 100644 --- a/packages/svelte2tsx/src/htmlxtojsx_v2/utils/node-utils.ts +++ b/packages/svelte2tsx/src/htmlxtojsx_v2/utils/node-utils.ts @@ -233,7 +233,7 @@ export function rangeWithTrailingPropertyAccess( * Get the end of the node, excluding the type annotation */ export function getEnd(node: any) { - return isTypescriptNode(node) ? node.expression.end : node.typeAnnotation?.start ?? node.end; + return isTypescriptNode(node) ? node.expression.end : (node.typeAnnotation?.start ?? node.end); } export function isTypescriptNode(node: any) { diff --git a/packages/svelte2tsx/svelte-shims-v4.d.ts b/packages/svelte2tsx/svelte-shims-v4.d.ts index e8e276255..87e365798 100644 --- a/packages/svelte2tsx/svelte-shims-v4.d.ts +++ b/packages/svelte2tsx/svelte-shims-v4.d.ts @@ -223,9 +223,9 @@ declare type ATypedSvelteComponent = { declare type ConstructorOfATypedSvelteComponent = new (args: {target: any, props?: any}) => ATypedSvelteComponent declare function __sveltets_2_ensureComponent< // @ts-ignore svelte.Component doesn't exist in Svelte 4 - T extends ConstructorOfATypedSvelteComponent | (0 extends (1 & import('svelte').Component) ? never : import('svelte').Component) | null | undefined + T extends ConstructorOfATypedSvelteComponent | (typeof import('svelte') extends { mount: any } ? import('svelte').Component : never) | null | undefined // @ts-ignore svelte.Component doesn't exist in Svelte 4 ->(type: T): NonNullable ? typeof import('svelte').SvelteComponent : T>; +>(type: T): NonNullable ? typeof import('svelte').SvelteComponent : T : T>; declare function __sveltets_2_ensureArray | Iterable>(array: T): T extends ArrayLike ? U[] : T extends Iterable ? Iterable : any[]; type __sveltets_2_PropsWithChildren = Props & diff --git a/packages/typescript-plugin/package.json b/packages/typescript-plugin/package.json index 9b17e9196..6928186a4 100644 --- a/packages/typescript-plugin/package.json +++ b/packages/typescript-plugin/package.json @@ -18,12 +18,12 @@ "author": "The Svelte Community", "license": "MIT", "devDependencies": { - "@types/node": "^16.0.0", + "@types/node": "^18.0.0", "typescript": "^5.5.2", - "svelte": "^3.57.0" + "svelte": "^4.2.19" }, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", + "@jridgewell/sourcemap-codec": "^1.5.0", "svelte2tsx": "workspace:~" } } diff --git a/packages/typescript-plugin/src/language-service/call-hierarchy.ts b/packages/typescript-plugin/src/language-service/call-hierarchy.ts index bfd7248eb..b16aafb0a 100644 --- a/packages/typescript-plugin/src/language-service/call-hierarchy.ts +++ b/packages/typescript-plugin/src/language-service/call-hierarchy.ts @@ -89,7 +89,7 @@ export function decorateCallHierarchy( return provideCallHierarchyOutgoingCalls(fileName, offset) .concat( program && sourceFile && isComponentModulePosition(fileName, position) - ? getOutgoingCallsForComponent(program, sourceFile) ?? [] + ? (getOutgoingCallsForComponent(program, sourceFile) ?? []) : [] ) .map((item): ts.CallHierarchyOutgoingCall | null => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ebe4e95d..d60a88176 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,17 +16,17 @@ importers: specifier: ^7.0.2 version: 7.0.3 prettier: - specifier: ~3.2.5 - version: 3.2.5 + specifier: ~3.3.3 + version: 3.3.3 ts-node: specifier: ^10.0.0 - version: 10.9.1(@types/node@16.18.32)(typescript@5.5.2) + version: 10.9.1(@types/node@18.19.46)(typescript@5.5.2) packages/language-server: dependencies: '@jridgewell/trace-mapping': - specifier: ^0.3.17 - version: 0.3.18 + specifier: ^0.3.25 + version: 0.3.25 '@vscode/emmet-helper': specifier: 2.8.4 version: 2.8.4 @@ -43,14 +43,14 @@ importers: specifier: ^4.17.21 version: 4.17.21 prettier: - specifier: ~3.2.5 - version: 3.2.5 + specifier: ~3.3.3 + version: 3.3.3 prettier-plugin-svelte: - specifier: ^3.2.2 - version: 3.2.2(prettier@3.2.5)(svelte@3.57.0) + specifier: ^3.2.6 + version: 3.2.6(prettier@3.3.3)(svelte@4.2.19) svelte: - specifier: ^3.57.0 - version: 3.57.0 + specifier: ^4.2.19 + version: 4.2.19 svelte2tsx: specifier: workspace:~ version: link:../svelte2tsx @@ -67,14 +67,14 @@ importers: specifier: ~5.3.0 version: 5.3.0 vscode-languageserver: - specifier: 8.0.2 - version: 8.0.2 + specifier: 9.0.1 + version: 9.0.1 vscode-languageserver-protocol: - specifier: 3.17.2 - version: 3.17.2 + specifier: 3.17.5 + version: 3.17.5 vscode-languageserver-types: - specifier: 3.17.2 - version: 3.17.2 + specifier: 3.17.5 + version: 3.17.5 vscode-uri: specifier: ~3.0.0 version: 3.0.8 @@ -89,11 +89,8 @@ importers: specifier: ^9.1.0 version: 9.1.1 '@types/node': - specifier: ^16.0.0 - version: 16.18.32 - '@types/prettier': - specifier: ^2.2.3 - version: 2.7.2 + specifier: ^18.0.0 + version: 18.19.46 '@types/sinon': specifier: ^7.5.2 version: 7.5.2 @@ -108,13 +105,13 @@ importers: version: 11.1.2 ts-node: specifier: ^10.0.0 - version: 10.9.1(@types/node@16.18.32)(typescript@5.5.2) + version: 10.9.1(@types/node@18.19.46)(typescript@5.5.2) packages/svelte-check: dependencies: '@jridgewell/trace-mapping': - specifier: ^0.3.17 - version: 0.3.18 + specifier: ^0.3.25 + version: 0.3.25 chokidar: specifier: ^3.4.1 version: 3.5.3 @@ -159,8 +156,8 @@ importers: specifier: ^3.4.0 version: 3.4.0 svelte: - specifier: ^3.57.0 - version: 3.57.0 + specifier: ^4.2.19 + version: 4.2.19 svelte-language-server: specifier: workspace:* version: link:../language-server @@ -192,18 +189,18 @@ importers: specifier: workspace:* version: link:../typescript-plugin vscode-languageclient: - specifier: ^8.0.0 - version: 8.1.0 + specifier: ^9.0.1 + version: 9.0.1 vscode-languageserver-protocol: - specifier: 3.17.2 - version: 3.17.2 + specifier: 3.17.5 + version: 3.17.5 devDependencies: '@types/lodash': specifier: ^4.14.116 version: 4.14.194 '@types/node': - specifier: ^16.0.0 - version: 16.18.32 + specifier: ^18.0.0 + version: 18.19.46 '@types/vscode': specifier: ^1.67 version: 1.78.0 @@ -230,11 +227,11 @@ importers: version: 3.1.2 devDependencies: '@jridgewell/sourcemap-codec': - specifier: ^1.4.14 - version: 1.4.15 + specifier: ^1.5.0 + version: 1.5.0 '@jridgewell/trace-mapping': - specifier: ^0.3.17 - version: 0.3.18 + specifier: ^0.3.25 + version: 0.3.25 '@rollup/plugin-commonjs': specifier: ^24.0.0 version: 24.1.0(rollup@3.7.5) @@ -254,8 +251,8 @@ importers: specifier: ^9.1.0 version: 9.1.1 '@types/node': - specifier: ^16.0.0 - version: 16.18.32 + specifier: ^18.0.0 + version: 18.19.46 '@types/unist': specifier: ^2.0.3 version: 2.0.6 @@ -287,8 +284,8 @@ importers: specifier: ^0.5.16 version: 0.5.21 svelte: - specifier: ~3.57.0 - version: 3.57.0 + specifier: ~4.2.19 + version: 4.2.19 tiny-glob: specifier: ^0.2.6 version: 0.2.9 @@ -302,24 +299,28 @@ importers: packages/typescript-plugin: dependencies: '@jridgewell/sourcemap-codec': - specifier: ^1.4.14 - version: 1.4.15 + specifier: ^1.5.0 + version: 1.5.0 svelte2tsx: specifier: workspace:~ version: link:../svelte2tsx devDependencies: '@types/node': - specifier: ^16.0.0 - version: 16.18.32 + specifier: ^18.0.0 + version: 18.19.46 svelte: - specifier: ^3.57.0 - version: 3.57.0 + specifier: ^4.2.19 + version: 4.2.19 typescript: specifier: ^5.5.2 version: 5.5.2 packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -333,22 +334,26 @@ packages: '@emmetio/scanner@1.0.4': resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} - '@jridgewell/resolve-uri@3.1.0': - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.1': resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.14': - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/trace-mapping@0.3.18': - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -480,11 +485,8 @@ packages: '@types/mri@1.1.1': resolution: {integrity: sha512-nJOuiTlsvmClSr3+a/trTSx4DTuY/VURsWGKSf/eeavh0LRMqdsK60ti0TlwM5iHiGOK3/Ibkxsbr7i9rzGreA==} - '@types/node@16.18.32': - resolution: {integrity: sha512-zpnXe4dEz6PrWz9u7dqyRoq9VxwCvoXRPy/ewhmMa1CgEyVmtL1NJPQ2MX+4pf97vetquVKkpiMx0MwI8pjNOw==} - - '@types/prettier@2.7.2': - resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} + '@types/node@18.19.46': + resolution: {integrity: sha512-vnRgMS7W6cKa1/0G3/DTtQYpVrZ8c0Xm6UkLaVFrb9jtcVC3okokW09Ki1Qdrj9ISokszD69nY4WDLRlvHlhAA==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -521,6 +523,11 @@ packages: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} @@ -559,10 +566,17 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -613,6 +627,9 @@ packages: cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -650,6 +667,10 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + debug@4.3.3: resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} engines: {node: '>=6.0'} @@ -674,6 +695,10 @@ packages: resolution: {integrity: sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==} engines: {node: '>=8'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -719,6 +744,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -879,6 +907,9 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -910,6 +941,9 @@ packages: just-extend@4.2.1: resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -938,9 +972,15 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1037,6 +1077,9 @@ packages: periscopic@2.0.3: resolution: {integrity: sha512-FuCZe61mWxQOJAQFEfmt9FjzebRlcpFz8sFPbyaCKtdusPkMEbA9ey0eARnRav5zAhmXznhaQkKGFAPn7X9NUw==} + periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -1044,14 +1087,14 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - prettier-plugin-svelte@3.2.2: - resolution: {integrity: sha512-ZzzE/wMuf48/1+Lf2Ffko0uDa6pyCfgHV6+uAhtg2U0AAXGrhCSW88vEJNAkAxW5qyrFY1y1zZ4J8TgHrjW++Q==} + prettier-plugin-svelte@3.2.6: + resolution: {integrity: sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true @@ -1140,6 +1183,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -1182,9 +1229,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte@3.57.0: - resolution: {integrity: sha512-WMXEvF+RtAaclw0t3bPDTUe19pplMlfyKDsixbHQYgCWi9+O9VN0kXU1OppzrB9gPAvz4NALuoca2LfW2bOjTQ==} - engines: {node: '>= 8'} + svelte@4.2.19: + resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==} + engines: {node: '>=16'} tiny-glob@0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} @@ -1222,6 +1269,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + unist-util-stringify-position@3.0.3: resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} @@ -1245,19 +1295,19 @@ packages: resolution: {integrity: sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==} engines: {node: '>=14.0.0'} - vscode-jsonrpc@8.1.0: - resolution: {integrity: sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==} + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} engines: {node: '>=14.0.0'} - vscode-languageclient@8.1.0: - resolution: {integrity: sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==} - engines: {vscode: ^1.67.0} + vscode-languageclient@9.0.1: + resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==} + engines: {vscode: ^1.82.0} vscode-languageserver-protocol@3.17.2: resolution: {integrity: sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==} - vscode-languageserver-protocol@3.17.3: - resolution: {integrity: sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==} + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} vscode-languageserver-textdocument@1.0.11: resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} @@ -1265,9 +1315,6 @@ packages: vscode-languageserver-types@3.17.2: resolution: {integrity: sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==} - vscode-languageserver-types@3.17.3: - resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} - vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} @@ -1275,6 +1322,10 @@ packages: resolution: {integrity: sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==} hasBin: true + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + vscode-nls@5.2.0: resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} @@ -1338,6 +1389,11 @@ packages: snapshots: + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -1352,23 +1408,29 @@ snapshots: '@emmetio/scanner@1.0.4': {} - '@jridgewell/resolve-uri@3.1.0': {} + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.1': {} - '@jridgewell/sourcemap-codec@1.4.14': {} + '@jridgewell/set-array@1.2.1': {} '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/trace-mapping@0.3.18': + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -1476,12 +1538,12 @@ snapshots: '@types/fs-extra@8.1.2': dependencies: - '@types/node': 16.18.32 + '@types/node': 18.19.46 '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 16.18.32 + '@types/node': 18.19.46 '@types/lodash@4.14.194': {} @@ -1491,9 +1553,9 @@ snapshots: '@types/mri@1.1.1': {} - '@types/node@16.18.32': {} - - '@types/prettier@2.7.2': {} + '@types/node@18.19.46': + dependencies: + undici-types: 5.26.5 '@types/resolve@1.20.2': {} @@ -1511,7 +1573,7 @@ snapshots: '@types/vfile@3.0.2': dependencies: - '@types/node': 16.18.32 + '@types/node': 18.19.46 '@types/unist': 2.0.6 '@types/vfile-message': 2.0.0 @@ -1524,7 +1586,7 @@ snapshots: emmet: 2.4.4 jsonc-parser: 2.3.1 vscode-languageserver-textdocument: 1.0.11 - vscode-languageserver-types: 3.17.2 + vscode-languageserver-types: 3.17.5 vscode-nls: 5.2.0 vscode-uri: 2.1.2 @@ -1532,6 +1594,8 @@ snapshots: acorn-walk@8.2.0: {} + acorn@8.12.1: {} + acorn@8.8.2: {} aggregate-error@3.1.0: @@ -1564,8 +1628,14 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + array-union@2.1.0: {} + axobject-query@4.1.0: {} + balanced-match@1.0.2: {} binary-extensions@2.2.0: {} @@ -1622,6 +1692,14 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + code-red@1.0.4: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.1 + acorn: 8.12.1 + estree-walker: 3.0.3 + periscopic: 3.1.0 + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -1654,6 +1732,11 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + debug@4.3.3(supports-color@8.1.1): dependencies: ms: 2.1.2 @@ -1677,6 +1760,8 @@ snapshots: rimraf: 3.0.2 slash: 3.0.0 + dequal@2.0.3: {} + diff@4.0.2: {} diff@5.0.0: {} @@ -1706,6 +1791,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.1 + fast-glob@3.2.12: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1863,6 +1952,10 @@ snapshots: dependencies: '@types/estree': 0.0.42 + is-reference@3.0.2: + dependencies: + '@types/estree': 0.0.42 + is-unicode-supported@0.1.0: {} isarray@0.0.1: {} @@ -1892,6 +1985,8 @@ snapshots: just-extend@4.2.1: {} + locate-character@3.0.0: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -1919,10 +2014,16 @@ snapshots: magic-string@0.27.0: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 make-error@1.3.6: {} + mdn-data@2.0.30: {} + merge2@1.4.1: {} micromatch@4.0.5: @@ -2034,16 +2135,22 @@ snapshots: estree-walker: 2.0.2 is-reference: 1.2.1 + periscopic@3.1.0: + dependencies: + '@types/estree': 1.0.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + picocolors@1.0.0: {} picomatch@2.3.1: {} - prettier-plugin-svelte@3.2.2(prettier@3.2.5)(svelte@3.57.0): + prettier-plugin-svelte@3.2.6(prettier@3.3.3)(svelte@4.2.19): dependencies: - prettier: 3.2.5 - svelte: 3.57.0 + prettier: 3.3.3 + svelte: 4.2.19 - prettier@3.2.5: {} + prettier@3.3.3: {} queue-microtask@1.2.3: {} @@ -2132,6 +2239,8 @@ snapshots: slash@3.0.0: {} + source-map-js@1.2.0: {} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -2169,7 +2278,22 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte@3.57.0: {} + svelte@4.2.19: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + '@types/estree': 1.0.1 + acorn: 8.12.1 + aria-query: 5.3.0 + axobject-query: 4.1.0 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.11 + periscopic: 3.1.0 tiny-glob@0.2.9: dependencies: @@ -2180,14 +2304,14 @@ snapshots: dependencies: is-number: 7.0.0 - ts-node@10.9.1(@types/node@16.18.32)(typescript@5.5.2): + ts-node@10.9.1(@types/node@18.19.46)(typescript@5.5.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 16.18.32 + '@types/node': 18.19.46 acorn: 8.8.2 acorn-walk: 8.2.0 arg: 4.1.3 @@ -2208,6 +2332,8 @@ snapshots: typescript@5.5.2: {} + undici-types@5.26.5: {} + unist-util-stringify-position@3.0.3: dependencies: '@types/unist': 2.0.6 @@ -2237,36 +2363,38 @@ snapshots: vscode-jsonrpc@8.0.2: {} - vscode-jsonrpc@8.1.0: {} + vscode-jsonrpc@8.2.0: {} - vscode-languageclient@8.1.0: + vscode-languageclient@9.0.1: dependencies: minimatch: 5.1.6 semver: 7.5.1 - vscode-languageserver-protocol: 3.17.3 + vscode-languageserver-protocol: 3.17.5 vscode-languageserver-protocol@3.17.2: dependencies: vscode-jsonrpc: 8.0.2 vscode-languageserver-types: 3.17.2 - vscode-languageserver-protocol@3.17.3: + vscode-languageserver-protocol@3.17.5: dependencies: - vscode-jsonrpc: 8.1.0 - vscode-languageserver-types: 3.17.3 + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 vscode-languageserver-textdocument@1.0.11: {} vscode-languageserver-types@3.17.2: {} - vscode-languageserver-types@3.17.3: {} - vscode-languageserver-types@3.17.5: {} vscode-languageserver@8.0.2: dependencies: vscode-languageserver-protocol: 3.17.2 + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + vscode-nls@5.2.0: {} vscode-oniguruma@1.7.0: {} From 6a059635eaf8c6077c87b6eb0ca5b4393c72b7dc Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 27 Aug 2024 21:11:09 +0200 Subject: [PATCH 05/12] fix: add server bin to package exports --- packages/language-server/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/language-server/package.json b/packages/language-server/package.json index bae08937a..8ee68b6ed 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -6,7 +6,8 @@ "typings": "dist/src/index", "exports": { "./package.json": "./package.json", - ".": "./dist/src/index.js" + ".": "./dist/src/index.js", + "./bin/server.js": "./bin/server.js" }, "scripts": { "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\"", From ec5fef4b7d9ef44838522bb4e8661ed8998680c0 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 27 Aug 2024 21:18:26 +0200 Subject: [PATCH 06/12] chore: bump magic-string (#2476) requires adjustments of mapping tests and a fix in the InlayHintProvider, because more things are mappable now --- .../typescript/features/InlayHintProvider.ts | 26 +- packages/svelte2tsx/package.json | 2 +- .../samples/action-directive/mappings.jsx | 30 +- .../samples/await-block/mappings.jsx | 62 ++- .../samples/component-props/mappings.jsx | 31 +- .../samples/each-block/mappings.jsx | 30 +- .../samples/element-attributes/mappings.jsx | 36 +- .../sourcemaps/samples/if-block/mappings.jsx | 12 +- .../samples/import-equal/mappings.jsx | 8 +- .../samples/large-sample-1/mappings.jsx | 371 +++++++++++++----- .../test/sourcemaps/samples/let/mappings.jsx | 8 +- .../samples/reactive-statements/mappings.jsx | 8 +- .../samples/reserved-variables/mappings.jsx | 22 +- .../sourcemaps/samples/slot-let/mappings.jsx | 14 +- .../sourcemaps/samples/slots/mappings.jsx | 18 +- pnpm-lock.yaml | 4 +- 16 files changed, 503 insertions(+), 179 deletions(-) diff --git a/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts b/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts index a11fb0f43..7db6f9f82 100644 --- a/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/InlayHintProvider.ts @@ -1,4 +1,4 @@ -import ts from 'typescript'; +import ts, { ArrowFunction } from 'typescript'; import { CancellationToken } from 'vscode-languageserver'; import { Position, @@ -69,6 +69,7 @@ export class InlayHintProviderImpl implements InlayHintProvider { inlayHint.position !== renderFunctionReturnTypeLocation && !this.isSvelte2tsxFunctionHints(sourceFile, inlayHint) && !this.isGeneratedVariableTypeHint(sourceFile, inlayHint) && + !this.isGeneratedAsyncFunctionReturnType(sourceFile, inlayHint) && !this.isGeneratedFunctionReturnType(sourceFile, inlayHint) ) .map(async (inlayHint) => ({ @@ -254,6 +255,29 @@ export class InlayHintProviderImpl implements InlayHintProvider { ); } + /** `true` if is one of the `async () => {...}` functions svelte2tsx generates */ + private isGeneratedAsyncFunctionReturnType(sourceFile: ts.SourceFile, inlayHint: ts.InlayHint) { + if (inlayHint.kind !== ts.InlayHintKind.Type) { + return false; + } + + const expression = findContainingNode( + sourceFile, + { start: inlayHint.position, length: 0 }, + (node): node is ArrowFunction => ts.isArrowFunction(node) + ); + + if ( + !expression?.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) || + !expression.parent?.parent || + !ts.isBlock(expression.parent.parent) + ) { + return false; + } + + return this.getTypeAnnotationPosition(expression) === inlayHint.position; + } + private isGeneratedFunctionReturnType(sourceFile: ts.SourceFile, inlayHint: ts.InlayHint) { if (inlayHint.kind !== ts.InlayHintKind.Type) { return false; diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index 97f42d5ea..258f5397e 100644 --- a/packages/svelte2tsx/package.json +++ b/packages/svelte2tsx/package.json @@ -31,7 +31,7 @@ "@types/vfile": "^3.0.2", "builtin-modules": "^3.3.0", "estree-walker": "^2.0.1", - "magic-string": "^0.27.0", + "magic-string": "^0.30.11", "mocha": "^9.2.0", "periscopic": "^2.0.2", "rollup": "3.7.5", diff --git a/packages/svelte2tsx/test/sourcemaps/samples/action-directive/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/action-directive/mappings.jsx index abaf91f05..9864b0bcc 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/action-directive/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/action-directive/mappings.jsx @@ -7,10 +7,12 @@ async•()•=>•{•{const•$$action_0•=•__sveltets_2_ensureAction(action < action/ element su ↲ #================================================================== # Order-breaking mappings ↲ [original] line 1 +↲ [original] line 1 (rest generated at line 4) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 4 + ↲ +↲ [original] line 1 (rest generated at line 3) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {const $$action_0 = __sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element')));{ svelteHTML.createElement("element", __sveltets_2_union($$action_0), { });}}{/** •{const•$$action_0•=•__sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element')));{•svelteHTML.createElement("element",•__sveltets_2_union($$action_0),•{••});}}↲ [generated] line 5 @@ -30,27 +32,31 @@ async•()•=>•{•{const•$$action_0•=•__sveltets_2_ensureAction(action • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 5 +/>↲ [original] line 5 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 6 + ↲ +/>↲ [original] line 5 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {const $$action_0 = __sveltets_2_ensureAction(action.nested.method(svelteHTML.mapElementTag('element'),(foo)));{ svelteHTML.createElement("element", __sveltets_2_union($$action_0), { });}}{/** ••{const•$$action_0•=•__sveltets_2_ensureAction(action.nested.method(svelteHTML.mapElementTag('element'),(foo)));{•svelteHTML.createElement("element",•__sveltets_2_union($$action_0),•{••});}}↲ [generated] line 7 <> action.nested.method= foo} element s{u ↲ #============================================== #=============================== # Order-breaking mappings ↲ -↲ [original] line 7 +↲ [original] line 7 (rest generated at line 8) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 8 + ↲ +↲ [original] line 7 (rest generated at line 7) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {const $$action_0 = __sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),($foo)));{ svelteHTML.createElement("element", __sveltets_2_union($$action_0), { });}}{/** •{const•$$action_0•=•__sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),($foo)));{•svelteHTML.createElement("element",•__sveltets_2_union($$action_0),•{•••});}}↲ [generated] line 9 • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 11 +/>↲ [original] line 11 (rest generated at line 10) •{const•$$action_0•=•__sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),($foo)));{•svelteHTML.createElement("element",•__sveltets_2_union($$action_0),•{•••});}}↲ [generated] line 9 •{const•$$action_0•=•__sveltets_2_ensureAction( element",•__sveltets_2_union($$action_0),•{ });}} [generated] subset @@ -66,7 +72,9 @@ async•()•=>•{•{const•$$action_0•=•__sveltets_2_ensureAction(action ╚use:action={$foo}↲ [original] line 10 ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 10 + ↲ +/>↲ [original] line 11 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {const $$action_0 = __sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),({ foo })));{ svelteHTML.createElement("element", __sveltets_2_union($$action_0), { });}}{/** •{const•$$action_0•=•__sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),({•foo•})));{•svelteHTML.createElement("element",•__sveltets_2_union($$action_0),•{•••});}}↲ [generated] line 11 @@ -86,10 +94,12 @@ async•()•=>•{•{const•$$action_0•=•__sveltets_2_ensureAction(action • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 15 +/>↲ [original] line 15 (rest generated at line 12) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 12 + ↲ +/>↲ [original] line 15 (rest generated at line 11) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {const $$action_0 = __sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),({ {/** •{const•$$action_0•=•__sveltets_2_ensureAction(action(svelteHTML.mapElementTag('element'),({•↲ [generated] line 13 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/await-block/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/await-block/mappings.jsx index 35fff79b7..8e81cad42 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/await-block/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/await-block/mappings.jsx @@ -8,17 +8,23 @@ async•()•=>•{•••{•const•$$_value•=•await•(promise);{•con {t promise• value} ↲ #=========================== Order-breaking mappings { promise•t value}↲ -{#await•promise•then•value}↲ [original] line 1 +{#await•promise•then•value}↲ [original] line 1 (rest generated at line 4) async•()•=>•{•••{•const•$$_value•=•await•(promise);{•const•value•=•$$_value;•↲ [generated] line 3 •{•const•$$_value•=•await•( [generated] subset a a -{/await}↲ [original] line 3 (rest generated at line 5) +{/await}↲ [original] line 3 (rest generated at lines 5, 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { "foo":value.bar,});} {/** -===# Originless mappings ••••••{•svelteHTML.createElement("element",•{•"foo":value.bar,});}↲ [generated] line 4 +•••• [generated] subset +↲ + ↲ +{#await•promise•then•value}↲ [original] line 1 (rest generated at line 3) + +••••••{•svelteHTML.createElement("element",•{•"foo":value.bar,});}↲ [generated] line 4 + ••{•svelteHTML.createElement("element",•{•"foo":value.bar,});}↲ [generated] subset <> element {f oo= value.bar} ↲ #============================ # Order-breaking mappings ↲ @@ -28,31 +34,39 @@ async•()•=>•{•••{•const•$$_value•=•await•(promise);{•con }}↲ [generated] line 5 { ↲ { ↲ -{/await}↲ [original] line 3 (rest generated at line 3) +{/await}↲ [original] line 3 (rest generated at lines 3, 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 6 + ↲ +{/await}↲ [original] line 3 (rest generated at lines 3, 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { {/** •••{•↲ [generated] line 7 • ↲ [generated] subset { ↲ { ↲ -{#await•promise}↲ [original] line 5 (rest generated at line 9) +{#await•promise}↲ [original] line 5 (rest generated at lines 8, 9) •••{•↲ [generated] line 7 • [generated] subset : -{:then}↲ [original] line 7 (rest generated at line 9) +{:then}↲ [original] line 7 (rest generated at lines 9, 10) •••{•↲ [generated] line 7 •{• [generated] subset a -{/await}↲ [original] line 9 (rest generated at line 11) +{/await}↲ [original] line 9 (rest generated at lines 11, 12) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", {});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 8 + ╚ [generated] subset + ↲ + ↲ + {#await•promise}↲ [original] line 5 (rest generated at lines 7, 9) + + ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 8 + •{•svelteHTML.createElement("element",•{});}↲ [generated] subset < element / ↲ ↲ [original] line 6 @@ -62,17 +76,23 @@ await•(promise);↲ [generated] line 9 promise); [generated] subset promise} promise} -{#await•promise}↲ [original] line 5 (rest generated at line 7) +{#await•promise}↲ [original] line 5 (rest generated at lines 7, 8) await•(promise);↲ [generated] line 9 await•( ↲ [generated] subset { ↲ { ↲ -{:then}↲ [original] line 7 (rest generated at line 7) +{:then}↲ [original] line 7 (rest generated at lines 7, 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", {});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 10 + ╚ [generated] subset + ↲ + ↲ + {:then}↲ [original] line 7 (rest generated at lines 7, 9) + + ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 10 + •{•svelteHTML.createElement("element",•{});}↲ [generated] subset < element / ↲ ↲ [original] line 8 @@ -81,17 +101,19 @@ await•( ↲ [generated] subset }↲ [generated] line 11 {↲ { ↲ -{/await}↲ [original] line 9 (rest generated at line 7) +{/await}↲ [original] line 9 (rest generated at lines 7, 12) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 12 + ↲ +{/await}↲ [original] line 9 (rest generated at lines 7, 11) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { {/** ••{•↲ [generated] line 13 • ↲ [generated] subset { ↲ { ↲ -{#await•$promise}↲ [original] line 11 (rest generated at line 15) +{#await•$promise}↲ [original] line 11 (rest generated at lines 14, 15) ••{•↲ [generated] line 13 •{• [generated] subset @@ -100,8 +122,14 @@ await•( ↲ [generated] subset {/await} [original] line 13 (rest generated at line 15) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", {});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 14 + ╚ [generated] subset + ↲ + ↲ + {#await•$promise}↲ [original] line 11 (rest generated at lines 13, 15) + + ╚•{•svelteHTML.createElement("element",•{});}↲ [generated] line 14 + •{•svelteHTML.createElement("element",•{});}↲ [generated] subset < element / ↲ ↲ [original] line 12 @@ -111,7 +139,7 @@ await•($promise);}};↲ [generated] line 15 $promise);}};↲ [generated] subset $promise} $promise} -{#await•$promise}↲ [original] line 11 (rest generated at line 13) +{#await•$promise}↲ [original] line 11 (rest generated at lines 13, 14) await•($promise);}};↲ [generated] line 15 await•( [generated] subset diff --git a/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx index 86889cddc..e19b69c73 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx @@ -7,30 +7,37 @@ async•()•=>•{••{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureCom <> Component f oo• ↲ #====================================================== Order-breaking mappings ↲ -↲ [original] line 1 +↲ [original] line 1 (rest generated at line 4) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 4 + ↲ +↲ [original] line 1 (rest generated at line 3) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { "foo":`leet`,"bar":true,}});}{/** •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{•"foo":`leet`,"bar":true,}});}↲ [generated] line 5 < Component "f oo= leet" b ar/ ↲ # Order-breaking mappings ↲ [original] line 3 +↲ [original] line 3 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - + ╚↲ [generated] line 6 + ↲ + ↲ + ↲ [original] line 3 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { ...bar,}});} {/** •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{•...bar,}});}↲ [generated] line 7 < Component /...bar} ↲ # Order-breaking mappings ↲ [original] line 5 +↲ [original] line 5 (rest generated at line 8) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 8 + ↲ +↲ [original] line 5 (rest generated at line 7) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { ...bar,}});} {/** •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{••...bar,}});}↲ [generated] line 9 @@ -49,10 +56,12 @@ async•()•=>•{••{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureCom • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 9 +/>↲ [original] line 9 (rest generated at line 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 10 + ↲ +/>↲ [original] line 9 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { foo:bar,}});/*Ωignore_startΩ*/() => bar = __sveltets_2_any(null);/*Ωignore_endΩ*/}{/** •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{•••foo:bar,}});/*Ωignore_startΩ*/()•=>•bar•=•__sveltets_2_any(null);/*Ωignore_endΩ*/}↲ [generated] line 11 @@ -72,10 +81,12 @@ async•()•=>•{••{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureCom • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 13 +/>↲ [original] line 13 (rest generated at line 12) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 12 + ↲ +/>↲ [original] line 13 (rest generated at line 11) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); const $$_tnenopmoC0 = new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { }});bar = $$_tnenopmoC0;}};{/** •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•const•$$_tnenopmoC0•=•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{••}});bar•=•$$_tnenopmoC0;}};↲ [generated] line 13 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/each-block/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/each-block/mappings.jsx index 4ae205f2d..7e934b571 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/each-block/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/each-block/mappings.jsx @@ -19,10 +19,12 @@ async•()•=>•{••for(let•item•of•__sveltets_2_ensureArray(items)){ }↲ [generated] line 5 {↲ { ↲ -{/each}↲ [original] line 3 +{/each}↲ [original] line 3 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 6 + ↲ +{/each}↲ [original] line 3 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} for(let item of __sveltets_2_ensureArray(items)){let i = 1; {/** •••for(let•item•of•__sveltets_2_ensureArray(items)){let•i•=•1;↲ [generated] line 7 @@ -41,10 +43,12 @@ async•()•=>•{••for(let•item•of•__sveltets_2_ensureArray(items)){ }↲ [generated] line 9 {↲ { ↲ -{/each}↲ [original] line 7 +{/each}↲ [original] line 7 (rest generated at line 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 10 + ↲ +{/each}↲ [original] line 7 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} for(let { id, name, qty } of __sveltets_2_ensureArray(items)){let i = 1;id; {/** ••••for(let•{•id,•name,•qty•}•of•__sveltets_2_ensureArray(items)){let•i•=•1;id;↲ [generated] line 11 @@ -63,10 +67,12 @@ async•()•=>•{••for(let•item•of•__sveltets_2_ensureArray(items)){ }↲ [generated] line 13 {↲ { ↲ -{/each}↲ [original] line 11 +{/each}↲ [original] line 11 (rest generated at line 14) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 14 + ↲ +{/each}↲ [original] line 11 (rest generated at line 13) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} for(let { id, ...rest } of __sveltets_2_ensureArray(objects)){ {/** ••for(let•{•id,•...rest•}•of•__sveltets_2_ensureArray(objects)){↲ [generated] line 15 @@ -86,10 +92,12 @@ async•()•=>•{••for(let•item•of•__sveltets_2_ensureArray(items)){ }↲ [generated] line 17 {↲ { ↲ -{/each}↲ [original] line 15 +{/each}↲ [original] line 15 (rest generated at line 18) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 18 + ↲ +{/each}↲ [original] line 15 (rest generated at line 17) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} for(let [id, ...rest] of __sveltets_2_ensureArray(items)){ {/** ••for(let•[id,•...rest]•of•__sveltets_2_ensureArray(items)){↲ [generated] line 19 @@ -109,10 +117,12 @@ async•()•=>•{••for(let•item•of•__sveltets_2_ensureArray(items)){ }↲ [generated] line 21 {↲ { ↲ -{/each}↲ [original] line 19 +{/each}↲ [original] line 19 (rest generated at line 22) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 22 + ↲ +{/each}↲ [original] line 19 (rest generated at line 21) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} for(let todo of __sveltets_2_ensureArray(todos)){ {/** ••for(let•todo•of•__sveltets_2_ensureArray(todos)){↲ [generated] line 23 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/element-attributes/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/element-attributes/mappings.jsx index 7ef16bb3f..b5a478a40 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/element-attributes/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/element-attributes/mappings.jsx @@ -6,10 +6,12 @@ async () => { { svelteHTML.createElement("element", {"foo":true,});} async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});}↲ [generated] line 3 < element f oo/ ↲ ↲ [original] line 1 +↲ [original] line 1 (rest generated at line 4) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 4 + ↲ +↲ [original] line 1 (rest generated at line 3) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { "foo":true,});} {/** ••{•svelteHTML.createElement("element",•{•"foo":true,});}↲ [generated] line 5 @@ -28,10 +30,12 @@ async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});} •{•svelteHTML.createElement(" ↲ [generated] subset > ↲ >↲ -/>↲ [original] line 5 +/>↲ [original] line 5 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 6 + ↲ +/>↲ [original] line 5 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { "foo":`leet`,});} {/** •{•svelteHTML.createElement("element",•{•••"foo":`leet`,});}↲ [generated] line 7 @@ -50,10 +54,12 @@ async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});} •{•svelteHTML.createElement("element",•{•••"foo":`leet`,});}↲ [generated] line 7 ↲ [generated] subset ↲ -/>↲ [original] line 9 +/>↲ [original] line 9 (rest generated at line 8) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 8 + ↲ +/>↲ [original] line 9 (rest generated at line 7) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { foo,"bar":true,});} {/** ••{•svelteHTML.createElement("element",•{•foo,"bar":true,});}↲ [generated] line 9 @@ -72,10 +78,12 @@ async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});} •{•svelteHTML.createElement(" ↲ [generated] subset > ↲ >↲ -/>↲ [original] line 13 +/>↲ [original] line 13 (rest generated at line 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 10 + ↲ +/>↲ [original] line 13 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { foo,});} {/** •{•svelteHTML.createElement("element",•{••foo,});}↲ [generated] line 11 @@ -94,10 +102,12 @@ async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});} •{•svelteHTML.createElement("element",•{••foo,});}↲ [generated] line 11 ↲ [generated] subset ↲ -/>↲ [original] line 17 +/>↲ [original] line 17 (rest generated at line 12) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 12 + ↲ +/>↲ [original] line 17 (rest generated at line 11) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { "bind:foo":foo,});/*Ωignore_startΩ*/() => foo = __sveltets_2_any(null);/*Ωignore_endΩ*/} {/** •{•svelteHTML.createElement("element",•{••"bind:foo":foo,});/*Ωignore_startΩ*/()•=>•foo•=•__sveltets_2_any(null);/*Ωignore_endΩ*/}↲ [generated] line 13 @@ -116,10 +126,12 @@ async•()•=>•{•{•svelteHTML.createElement("element",•{"foo":true,});} • ↲ [generated] subset / ↲ / ↲ -/>↲ [original] line 21 +/>↲ [original] line 21 (rest generated at line 14) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 14 + ↲ +/>↲ [original] line 21 (rest generated at line 13) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("element", { "bind:foo":bar,});/*Ωignore_startΩ*/() => bar = __sveltets_2_any(null);/*Ωignore_endΩ*/}}; {/** •{•svelteHTML.createElement("element",•{••"bind:foo":bar,});/*Ωignore_startΩ*/()•=>•bar•=•__sveltets_2_any(null);/*Ωignore_endΩ*/}};↲ [generated] line 15 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/if-block/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/if-block/mappings.jsx index 080912ca8..f76cef7ba 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/if-block/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/if-block/mappings.jsx @@ -18,19 +18,23 @@ async•()•=>•{if(foo){↲ [generated] line 3 }↲ [generated] line 5 {↲ { ↲ -{/if}↲ [original] line 3 +{/if}↲ [original] line 3 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 6 + ↲ +{/if}↲ [original] line 3 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if($foo){ { svelteHTML.createElement("element", {});}} {/** if($foo){•{•svelteHTML.createElement("element",•{});}}↲ [generated] line 7 { $foo} < element / {↲ { $foo}{/if}↲ [original] line 5 +{#if•$foo}{/if}↲ [original] line 5 (rest generated at line 8) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 8 + ↲ +{#if•$foo}{/if}↲ [original] line 5 (rest generated at line 7) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if(foo){ {/** if(foo){↲ [generated] line 9 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/import-equal/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/import-equal/mappings.jsx index e122d928f..e1548fcb1 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/import-equal/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/import-equal/mappings.jsx @@ -19,9 +19,13 @@ ; {/** ;↲ [generated] line 6 < - [original] line 4 + [original] line 4 (rest generated at line 7) +------------------------------------------------------------------------------------------------------------------------------------------------------ */} +async () => {}; {/** +async•()•=>•{};↲ [generated] line 7 +< + [original] line 4 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} -async () => {}; return { props: /** @type {Record} */ ({}), slots: {}, events: {} }} export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) { diff --git a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx index dd6cc6ee5..44c3de9a8 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx @@ -23,7 +23,7 @@ ;;↲ [generated] line 15 ; [generated] subset < -↲ [original] line 14 (rest generated at line 119) +↲ [original] line 14 (rest generated at lines 119, 120) ;;↲ [generated] line 15 ;↲ [generated] subset @@ -251,69 +251,105 @@ s ; {/** ;↲ [generated] line 118 < -↲ [original] line 109 (rest generated at line 121) +↲ [original] line 109 (rest generated at lines 119, 121, 122) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} async () => { {/** -============# Originless mappings async•()•=>•{↲ [generated] line 119 - ↲ +async•()•=>•{ [generated] subset +< +↲ [original] line 109 (rest generated at lines 118, 121, 122) + +async•()•=>•{↲ [generated] line 119 + ↲ [generated] subset ↲ -↲ [original] line 14 (rest generated at line 15) +↲ [original] line 14 (rest generated at lines 15, 120) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 120 + ↲ +↲ [original] line 14 (rest generated at lines 15, 119) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** ↲ [generated] line 121 ↲ -↲ [original] line 109 (rest generated at line 118) +↲ [original] line 109 (rest generated at lines 118, 119, 122) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 122 + ↲ +↲ [original] line 109 (rest generated at lines 118, 119, 121) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** ↲ [generated] line 123 ↲ -↲ [original] line 259 +↲ [original] line 259 (rest generated at line 124) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 124 + ↲ +↲ [original] line 259 (rest generated at line 123) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("svelte:head", {}); {/** •{•svelteHTML.createElement("svelte:head",•{});↲ [generated] line 125 s ↲ s ↲ -↲ [original] line 261 +↲ [original] line 261 (rest generated at line 126) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("title", {});selected.section.title; selected.chapter.title; } {/** - # Originless mappings ╚•{•svelteHTML.createElement("title",•{});selected.section.title;••selected.chapter.title;••••}↲ [generated] line 126 + ╚ [generated] subset + ↲ + ↲ + ↲ [original] line 261 (rest generated at line 125) + + ╚•{•svelteHTML.createElement("title",•{});selected.section.title;••selected.chapter.title;••••}↲ [generated] line 126 + •{•svelteHTML.createElement("title",•{});selected.section.title;••selected.chapter.title;••••}↲ [generated] subset < title selected.section.title}• selected.chapter.title}• / ↲ {selected.section.title}•/•{selected.chapter.title}•••Svelte•Tutorial↲ [original] line 262 + ╚{selected.section.title}•/•{selected.chapter.title}•••Svelte•Tutorial↲ [original] line 262 (rest generated at lines 127, 128) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 127 + ↲ +╚{selected.section.title}•/•{selected.chapter.title}•••Svelte•Tutorial↲ [original] line 262 (rest generated at lines 126, 128) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("meta", { "name":`twitter:title`,"content":`Svelte tutorial`,});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("meta",•{•••"name":`twitter:title`,"content":`Svelte•tutorial`,});}↲ [generated] line 128 + ╚ [generated] subset + ↲ + ↲ + ╚{selected.section.title}•/•{selected.chapter.title}•••Svelte•Tutorial↲ [original] line 262 (rest generated at lines 126, 127) + + ╚•{•svelteHTML.createElement("meta",•{•••"name":`twitter:title`,"content":`Svelte•tutorial`,});}↲ [generated] line 128 + •{•svelteHTML.createElement("meta",•{•••"name":`twitter:title`,"content":`Svelte•tutorial`,});}↲ [generated] subset < meta "•"n ame= twitter:title" c ontent= Svelte•tutorial" ↲ # Order-breaking mappings ↲ [original] line 264 + ╚↲ [original] line 264 (rest generated at line 129) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("meta", { "name":`twitter:description`,"content":`${selected.section.title} / ${selected.chapter.title}`,});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("meta",•{•••"name":`twitter:description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] line 129 + ╚ [generated] subset + ↲ + ↲ + ╚↲ [original] line 264 (rest generated at line 128) + + ╚•{•svelteHTML.createElement("meta",•{•••"name":`twitter:description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] line 129 + •{•svelteHTML.createElement("meta",•{•••"name":`twitter:description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] subset < meta "•"n ame= twitter:description" c ontent= {selected.section.title}•/• {selected.chapter.title}" ↲ # Order-breaking mappings ↲ [original] line 265 + ╚↲ [original] line 265 (rest generated at line 130) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("meta", { "name":`Description`,"content":`${selected.section.title} / ${selected.chapter.title}`,});} {/** - # Originless mappings ╚•{•svelteHTML.createElement("meta",•{•••"name":`Description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] line 130 + ╚ [generated] subset + ↲ + ↲ + ╚↲ [original] line 265 (rest generated at line 129) + + ╚•{•svelteHTML.createElement("meta",•{•••"name":`Description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] line 130 + •{•svelteHTML.createElement("meta",•{•••"name":`Description`,"content":`${selected.section.title}•/•${selected.chapter.title}`,});}↲ [generated] subset < meta "•"n ame= Description" c ontent= {selected.section.title}•/• {selected.chapter.title}" ↲ # Order-breaking mappings ↲ [original] line 267 +↲ [original] line 267 (rest generated at line 132) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 132 + ↲ +↲ [original] line 267 (rest generated at line 131) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("svelte:window", { "bind:innerWidth":width,});/*Ωignore_startΩ*/() => width = __sveltets_2_any(null);/*Ωignore_endΩ*/} {/** ••{•svelteHTML.createElement("svelte:window",•{•"bind:innerWidth":width,});/*Ωignore_startΩ*/()•=>•width•=•__sveltets_2_any(null);/*Ωignore_endΩ*/}↲ [generated] line 133 <> ib width} ↲ #=============================================# Order-breaking mappings < bi width} >↲ -↲ [original] line 269 +↲ [original] line 269 (rest generated at line 134) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 134 + ↲ +↲ [original] line 269 (rest generated at line 133) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`tutorial-outer`,}); {/** •{•svelteHTML.createElement("div",•{•"class":`tutorial-outer`,});↲ [generated] line 135 < div "c lass= tutorial-outer" ↲ # Order-breaking mappings
↲ [original] line 271 +↲ [original] line 271 (rest generated at line 136) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`viewport offset-${offset}`,}); {/** - # Originless mappings ╚•{•svelteHTML.createElement("div",•{•"class":`viewport•offset-${offset}`,});↲ [generated] line 136 + ╚ [generated] subset + ↲ + ↲ + ↲ [original] line 271 (rest generated at line 135) + + ╚•{•svelteHTML.createElement("div",•{•"class":`viewport•offset-${offset}`,});↲ [generated] line 136 + •{•svelteHTML.createElement("div",•{•"class":`viewport•offset-${offset}`,});↲ [generated] subset < div "c lass= viewport•offset- {offset}" ↲ # Order-breaking mappings
↲ [original] line 272 + ╚↲ [original] line 272 (rest generated at line 137) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`tutorial-text`,}); {/** - =# Originless mappings ╚╚•{•svelteHTML.createElement("div",•{•"class":`tutorial-text`,});↲ [generated] line 137 + ╚╚ [generated] subset + ↲ + ↲ + ╚↲ [original] line 272 (rest generated at line 136) + + ╚╚•{•svelteHTML.createElement("div",•{•"class":`tutorial-text`,});↲ [generated] line 137 + •{•svelteHTML.createElement("div",•{•"class":`tutorial-text`,});↲ [generated] subset < div "c lass= tutorial-text" ↲ # Order-breaking mappings
↲ [original] line 273 + ╚╚↲ [original] line 273 (rest generated at line 138) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`table-of-contents`,}); {/** - ==# Originless mappings ╚╚╚•{•svelteHTML.createElement("div",•{•"class":`table-of-contents`,});↲ [generated] line 138 + ╚╚╚ [generated] subset + ↲ + ↲ + ╚╚↲ [original] line 273 (rest generated at line 137) + + ╚╚╚•{•svelteHTML.createElement("div",•{•"class":`table-of-contents`,});↲ [generated] line 138 + •{•svelteHTML.createElement("div",•{•"class":`table-of-contents`,});↲ [generated] subset < div "c lass= table-of-contents" ↲ # Order-breaking mappings
↲ [original] line 274 + ╚╚╚↲ [original] line 274 (rest generated at line 139) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_stnetnoCfOelbaT4C = __sveltets_2_ensureComponent(TableOfContents); new $$_stnetnoCfOelbaT4C({ target: __sveltets_2_any(), props: { sections,slug,selected,}});}{/** - ===# Originless mappings ╚╚╚╚••{•const•$$_stnetnoCfOelbaT4C•=•__sveltets_2_ensureComponent(TableOfContents);•new•$$_stnetnoCfOelbaT4C({•target:•__sveltets_2_any(),•props:•{••sections,slug,selected,}});}↲ [generated] line 139 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚↲ [original] line 274 (rest generated at line 138) + + ╚╚╚╚••{•const•$$_stnetnoCfOelbaT4C•=•__sveltets_2_ensureComponent(TableOfContents);•new•$$_stnetnoCfOelbaT4C({•target:•__sveltets_2_any(),•props:•{••sections,slug,selected,}});}↲ [generated] line 139 + ••{•const•$$_stnetnoCfOelbaT4C•=•__sveltets_2_ensureComponent(TableOfContents);•new•$$_stnetnoCfOelbaT4C({•target:•__sveltets_2_any(),•props:•{••sections,slug,selected,}});}↲ [generated] subset <> TableOfContents ••sections}slug}selected} ↲ #============================================================ # Order-breaking mappings ↲ - ╚╚╚╚↲ [original] line 275 + ╚╚╚╚↲ [original] line 275 (rest generated at line 140) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - ==# Originless mappings - ╚╚╚•}↲ [generated] line 140 + ╚╚╚•}↲ [generated] line 140 + ╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚↲ [original] line 275 (rest generated at line 139) + + ╚╚╚•}↲ [generated] line 140 + •}↲ [generated] subset / ↲ / ↲ - ╚╚╚
↲ [original] line 276 + ╚╚╚
↲ [original] line 276 (rest generated at lines 141, 142) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 141 + ↲ +╚╚╚
↲ [original] line 276 (rest generated at lines 140, 142) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_div3 = svelteHTML.createElement("div", { "class":`chapter-markup`,});scrollable = $$_div3; {/** - ==# Originless mappings ╚╚╚•{•const•$$_div3•=•svelteHTML.createElement("div",•{••"class":`chapter-markup`,});scrollable•=•$$_div3;↲ [generated] line 142 + ╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚
↲ [original] line 276 (rest generated at lines 140, 141) + + ╚╚╚•{•const•$$_div3•=•svelteHTML.createElement("div",•{••"class":`chapter-markup`,});scrollable•=•$$_div3;↲ [generated] line 142 + •{•const•$$_div3•=•svelteHTML.createElement("div",•{••"class":`chapter-markup`,});scrollable•=•$$_div3;↲ [generated] subset < div "•c lass= chapter-markup" scrollable} ↲ # Order-breaking mappings
↲ [original] line 278 + ╚╚╚↲ [original] line 278 (rest generated at line 143) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} chapter.html; {/** - ===# Originless mappings - ╚╚╚╚•chapter.html;↲ [generated] line 143 + ╚╚╚╚•chapter.html;↲ [generated] line 143 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚↲ [original] line 278 (rest generated at line 142) + + ╚╚╚╚•chapter.html;↲ [generated] line 143 + •chapter.html;↲ [generated] subset {chapter.html}↲ { chapter.html}↲ - ╚╚╚╚{@html•chapter.html}↲ [original] line 279 + ╚╚╚╚{@html•chapter.html}↲ [original] line 279 (rest generated at lines 144, 145) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 144 + ↲ +╚╚╚╚{@html•chapter.html}↲ [original] line 279 (rest generated at lines 143, 145) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`controls`,}); {/** - ===# Originless mappings ╚╚╚╚•{•svelteHTML.createElement("div",•{•"class":`controls`,});↲ [generated] line 145 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚{@html•chapter.html}↲ [original] line 279 (rest generated at lines 143, 144) + + ╚╚╚╚•{•svelteHTML.createElement("div",•{•"class":`controls`,});↲ [generated] line 145 + •{•svelteHTML.createElement("div",•{•"class":`controls`,});↲ [generated] subset < div "c lass= controls" ↲ # Order-breaking mappings
↲ [original] line 281 + ╚╚╚╚↲ [original] line 281 (rest generated at line 146) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if(chapter.app_b){ {/** - ====# Originless mappings - ╚╚╚╚╚if(chapter.app_b){↲ [generated] line 146 + ╚╚╚╚╚if(chapter.app_b){↲ [generated] line 146 + ╚╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚↲ [original] line 281 (rest generated at line 145) + + ╚╚╚╚╚if(chapter.app_b){↲ [generated] line 146 + if(chapter.app_b){↲ [generated] subset { chapter.app_b} ↲ { chapter.app_b}↲ - ╚╚╚╚╚{#if•chapter.app_b}↲ [original] line 282 + ╚╚╚╚╚{#if•chapter.app_b}↲ [original] line 282 ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** ╚╚╚╚╚╚↲ [generated] line 147 @@ -429,42 +527,68 @@ s ↲ ╚╚╚╚╚╚↲ [generated] line 147 ↲ [generated] subset ↲ - ╚╚╚╚╚╚╚matches•the•expected•end•result•-->↲ [original] line 284 + ╚╚╚╚╚╚╚matches•the•expected•end•result•-->↲ [original] line 284 (rest generated at line 148) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("button", { "class":`show`,"on:click":() => completed ? reset() : complete(),}); {/** - =====# Originless mappings ╚╚╚╚╚╚••{•svelteHTML.createElement("button",•{•••"class":`show`,"on:click":()•=>•completed•?•reset()•:•complete(),});↲ [generated] line 148 + ╚╚╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚╚╚╚matches•the•expected•end•result•-->↲ [original] line 284 (rest generated at line 147) + + ╚╚╚╚╚╚••{•svelteHTML.createElement("button",•{•••"class":`show`,"on:click":()•=>•completed•?•reset()•:•complete(),});↲ [generated] line 148 + ••{•svelteHTML.createElement("button",•{•••"class":`show`,"on:click":()•=>•completed•?•reset()•:•complete(),});↲ [generated] subset <> button "•"c lass= show" c lick =()•=>•completed•?•reset()•:•complete()} ↲ #============================ # Order-breaking mappings ↲ [original] line 287 + ╚╚╚╚╚╚↲ [original] line 287 ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** ╚╚╚╚╚}↲ [generated] line 151 ╚╚╚╚╚{↲ ╚╚╚╚╚{ ↲ - ╚╚╚╚╚{/if}↲ [original] line 288 + ╚╚╚╚╚{/if}↲ [original] line 288 (rest generated at lines 152, 153) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 152 + ↲ +╚╚╚╚╚{/if}↲ [original] line 288 (rest generated at lines 151, 153) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if(selected.next){ {/** - ====# Originless mappings ╚╚╚╚╚if(selected.next){↲ [generated] line 153 + ╚╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚╚{/if}↲ [original] line 288 (rest generated at lines 151, 152) + + ╚╚╚╚╚if(selected.next){↲ [generated] line 153 + if(selected.next){↲ [generated] subset { selected.next} ↲ { selected.next}↲ ╚╚╚╚╚{#if•selected.next}↲ [original] line 290 @@ -480,68 +604,119 @@ s ↲ ╚╚╚╚╚}↲ [generated] line 155 ╚╚╚╚╚{↲ ╚╚╚╚╚{ ↲ - ╚╚╚╚╚{/if}↲ [original] line 292 + ╚╚╚╚╚{/if}↲ [original] line 292 (rest generated at line 156) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - ===# Originless mappings ╚╚╚╚•}↲ [generated] line 156 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚╚{/if}↲ [original] line 292 (rest generated at line 155) + + ╚╚╚╚•}↲ [generated] line 156 + •}↲ [generated] subset / ↲ / ↲ - ╚╚╚╚
↲ [original] line 293 + ╚╚╚╚
↲ [original] line 293 (rest generated at lines 157, 158) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 157 + ↲ +╚╚╚╚↲ [original] line 293 (rest generated at lines 156, 158) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`improve-chapter`,}); {/** - ===# Originless mappings ╚╚╚╚•{•svelteHTML.createElement("div",•{•"class":`improve-chapter`,});↲ [generated] line 158 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚↲ [original] line 293 (rest generated at lines 156, 157) + + ╚╚╚╚•{•svelteHTML.createElement("div",•{•"class":`improve-chapter`,});↲ [generated] line 158 + •{•svelteHTML.createElement("div",•{•"class":`improve-chapter`,});↲ [generated] subset < div "c lass= improve-chapter" ↲ # Order-breaking mappings
↲ [original] line 295 + ╚╚╚╚↲ [original] line 295 (rest generated at line 159) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("a", { "class":`no-underline`,"href":improve_link,}); } {/** - ====# Originless mappings ╚╚╚╚╚•{•svelteHTML.createElement("a",•{•••"class":`no-underline`,"href":improve_link,});•••}↲ [generated] line 159 + ╚╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚↲ [original] line 295 (rest generated at line 158) + + ╚╚╚╚╚•{•svelteHTML.createElement("a",•{•••"class":`no-underline`,"href":improve_link,});•••}↲ [generated] line 159 + •{•svelteHTML.createElement("a",•{•••"class":`no-underline`,"href":improve_link,});•••}↲ [generated] subset < a "•{c lass= no-underline" h ref= improve_link} E / ↲ # Order-breaking mappings Edit•this•chapter↲ [original] line 296 + ╚╚╚╚╚Edit•this•chapter↲ [original] line 296 (rest generated at line 160) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - ===# Originless mappings - ╚╚╚╚•}↲ [generated] line 160 + ╚╚╚╚•}↲ [generated] line 160 + ╚╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚╚Edit•this•chapter↲ [original] line 296 (rest generated at line 159) + + ╚╚╚╚•}↲ [generated] line 160 + •}↲ [generated] subset / ↲ / ↲ - ╚╚╚╚
↲ [original] line 297 + ╚╚╚╚↲ [original] line 297 (rest generated at line 161) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - ==# Originless mappings - ╚╚╚•}↲ [generated] line 161 + ╚╚╚•}↲ [generated] line 161 + ╚╚╚ [generated] subset + ↲ + ↲ + ╚╚╚╚↲ [original] line 297 (rest generated at line 160) + + ╚╚╚•}↲ [generated] line 161 + •}↲ [generated] subset / ↲ / ↲ - ╚╚╚↲ [original] line 298 + ╚╚╚↲ [original] line 298 (rest generated at line 162) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - =# Originless mappings - ╚╚•}↲ [generated] line 162 + ╚╚•}↲ [generated] line 162 + ╚╚ [generated] subset + ↲ + ↲ + ╚╚╚↲ [original] line 298 (rest generated at line 161) + + ╚╚•}↲ [generated] line 162 + •}↲ [generated] subset / ↲ / ↲ - ╚╚↲ [original] line 299 + ╚╚↲ [original] line 299 (rest generated at lines 163, 164) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 163 + ↲ +╚╚↲ [original] line 299 (rest generated at lines 162, 164) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { svelteHTML.createElement("div", { "class":`tutorial-repl`,}); {/** - =# Originless mappings ╚╚•{•svelteHTML.createElement("div",•{•"class":`tutorial-repl`,});↲ [generated] line 164 + ╚╚ [generated] subset + ↲ + ↲ + ╚╚↲ [original] line 299 (rest generated at lines 162, 163) + + ╚╚•{•svelteHTML.createElement("div",•{•"class":`tutorial-repl`,});↲ [generated] line 164 + •{•svelteHTML.createElement("div",•{•"class":`tutorial-repl`,});↲ [generated] subset < div "c lass= tutorial-repl" ↲ # Order-breaking mappings
↲ [original] line 301 + ╚╚↲ [original] line 301 (rest generated at line 165) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_lpeR3C = __sveltets_2_ensureComponent(Repl); const $$_lpeR3 = new $$_lpeR3C({ target: __sveltets_2_any(), props: { "workersUrl":`workers`,svelteUrl,rollupUrl,"orientation":mobile ? 'columns' : 'rows',"fixed":mobile,"injectedJS":mapbox_setup,"relaxed":true,}});repl = $$_lpeR3;$$_lpeR3.$on("change", handle_change);}{/** - ==# Originless mappings + ╚╚╚•{•const•$$_lpeR3C•=•__sveltets_2_ensureComponent(Repl);•const•$$_lpeR3•=•new•$$_lpeR3C({•target:•__sveltets_2_any(),•props:•{•••••••••••••••"workersUrl":`workers`,svelteUrl,rollupUrl,"orientation":mobile•?•'columns'•:•'rows',"fixed":mobile,"injectedJS":mapbox_setup,"relaxed":true,}});repl•=•$$_lpeR3;$$_lpeR3.$on("change",•handle_change);}↲ [generated] line 165 + ╚╚╚ [generated] subset + ↲ + ↲ + ╚╚↲ [original] line 301 (rest generated at line 164) + ╚╚╚•{•const•$$_lpeR3C•=•__sveltets_2_ensureComponent(Repl);•const•$$_lpeR3•=•new•$$_lpeR3C({•target:•__sveltets_2_any(),•props:•{•••••••••••••••"workersUrl":`workers`,svelteUrl,rollupUrl,"orientation":mobile•?•'columns'•:•'rows',"fixed":mobile,"injectedJS":mapbox_setup,"relaxed":true,}});repl•=•$$_lpeR3;$$_lpeR3.$on("change",•handle_change);}↲ [generated] line 165 •{•const•$$_lpeR3C•=•__sveltets_2_ensureComponent(Repl);•const•$$_lpeR3•=•new•$$_lpeR3C({•target:•__sveltets_2_any(),•props:•{ [generated] subset < Repl @@ -614,28 +789,48 @@ s ↲ • ↲ [generated] subset ╚ ↲ ╚ ↲ - ╚╚╚/>↲ [original] line 312 + ╚╚╚/>↲ [original] line 312 (rest generated at line 166) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - =# Originless mappings ╚╚•}↲ [generated] line 166 + ╚╚ [generated] subset + ↲ + ↲ + ╚╚╚/>↲ [original] line 312 (rest generated at line 165) + + ╚╚•}↲ [generated] line 166 + •}↲ [generated] subset / ↲ / ↲ - ╚╚
↲ [original] line 313 + ╚╚↲ [original] line 313 (rest generated at line 167) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} } {/** - # Originless mappings - ╚•}↲ [generated] line 167 + ╚•}↲ [generated] line 167 + ╚ [generated] subset + ↲ + ↲ + ╚╚↲ [original] line 313 (rest generated at line 166) + + ╚•}↲ [generated] line 167 + •}↲ [generated] subset / ↲ / ↲ - ╚↲ [original] line 314 + ╚↲ [original] line 314 (rest generated at lines 168, 169) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 168 + ↲ +╚↲ [original] line 314 (rest generated at lines 167, 169) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if(mobile){ {/** - # Originless mappings ╚if(mobile){↲ [generated] line 169 + ╚ [generated] subset + ↲ + ↲ + ╚↲ [original] line 314 (rest generated at lines 167, 168) + + ╚if(mobile){↲ [generated] line 169 + if(mobile){↲ [generated] subset { mobile} ↲ { mobile}↲ ╚{#if•mobile}↲ [original] line 316 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/let/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/let/mappings.jsx index 265203757..36af25336 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/let/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/let/mappings.jsx @@ -19,9 +19,13 @@ ; {/** ;↲ [generated] line 5 < - [original] line 3 + [original] line 3 (rest generated at line 6) +------------------------------------------------------------------------------------------------------------------------------------------------------ */} +async () => {}; {/** +async•()•=>•{};↲ [generated] line 6 +< + [original] line 3 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} -async () => {}; return { props: /** @type {Record} */ ({}), slots: {}, events: {} }} export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) { diff --git a/packages/svelte2tsx/test/sourcemaps/samples/reactive-statements/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/reactive-statements/mappings.jsx index 8ef14e465..348d67a39 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/reactive-statements/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/reactive-statements/mappings.jsx @@ -41,9 +41,13 @@ ; {/** ;↲ [generated] line 9 < - [original] line 7 + [original] line 7 (rest generated at line 10) +------------------------------------------------------------------------------------------------------------------------------------------------------ */} +async () => {}; {/** +async•()•=>•{};↲ [generated] line 10 +< + [original] line 7 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} -async () => {}; return { props: {prop: prop}, slots: {}, events: {} }} export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) { diff --git a/packages/svelte2tsx/test/sourcemaps/samples/reserved-variables/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/reserved-variables/mappings.jsx index 9ac4a75c6..9ff7d1c21 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/reserved-variables/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/reserved-variables/mappings.jsx @@ -23,17 +23,18 @@ ; {/** ;↲ [generated] line 8 < -↲ [original] line 6 (rest generated at line 9) +↲ [original] line 6 (rest generated at lines 9, 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} async () => { {/** -============# Originless mappings async•()•=>•{↲ [generated] line 9 - ↲ - ↲ -↲ [original] line 6 (rest generated at line 8) +< ↲ +< ↲ +↲ [original] line 6 (rest generated at lines 8, 10) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 10 + ↲ +↲ [original] line 6 (rest generated at lines 8, 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} if($$slots.foo){ {/** if($$slots.foo){↲ [generated] line 11 @@ -45,10 +46,9 @@ if($$slots.foo){↲ [generated] line 11 ╚$$restProps.bar;↲ [generated] line 12 ╚$$restProps.bar}↲ ╚ $$restProps.bar}↲ - ╚{$$restProps.bar}↲ [original] line 9 + ╚{$$restProps.bar}↲ [original] line 9 (rest generated at line 13) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { ...$$props,}});} {/** - # Originless mappings ╚•{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{••...$$props,}});}↲ [generated] line 13 • ...$$props,}});} [generated] subset ╚ ...$$props} @@ -61,6 +61,12 @@ if($$slots.foo){↲ [generated] line 11 ╚ ↲ ╚/>↲ [original] line 12 + ╚•{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{••...$$props,}});}↲ [generated] line 13 + ╚ [generated] subset + ↲ + ↲ + ╚{$$restProps.bar}↲ [original] line 9 (rest generated at line 12) + ╚•{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{••...$$props,}});}↲ [generated] line 13 •{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureComponent(Component);•new•$$_tnenopmoC0C({•target:•__sveltets_2_any(),•props:•{ [generated] subset < Component diff --git a/packages/svelte2tsx/test/sourcemaps/samples/slot-let/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/slot-let/mappings.jsx index 2612247aa..a07171311 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/slot-let/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/slot-let/mappings.jsx @@ -7,14 +7,20 @@ async•()•=>•{•{•const•$$_tnenopmoC0C•=•__sveltets_2_ensureCompon < Component el{•l hi•hi2=hi_2}hi3 ↲ # Order-breaking mappings ↲ [original] line 1 +↲ [original] line 1 (rest generated at line 4) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} hi;hi_2;hi3; {/** -===# Originless mappings -••••hi;hi_2;hi3;↲ [generated] line 4 +••••hi;hi_2;hi3;↲ [generated] line 4 +•••• [generated] subset +↲ + ↲ +↲ [original] line 1 (rest generated at line 3) + +••••hi;hi_2;hi3;↲ [generated] line 4 + hi;hi_2;hi3;↲ [generated] subset hi}hi_2}hi3}↲ hi} hi_2} hi3}↲ -••••{hi}{hi_2}{hi3}↲ [original] line 2 +••••{hi}{hi_2}{hi3}↲ [original] line 2 ------------------------------------------------------------------------------------------------------------------------------------------------------ */} }Component}}; {/** •}Component}};↲ [generated] line 5 diff --git a/packages/svelte2tsx/test/sourcemaps/samples/slots/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/slots/mappings.jsx index 86e0654ab..8353a45dd 100644 --- a/packages/svelte2tsx/test/sourcemaps/samples/slots/mappings.jsx +++ b/packages/svelte2tsx/test/sourcemaps/samples/slots/mappings.jsx @@ -7,30 +7,36 @@ async () => { { __sveltets_createSlot("default", {});} async•()•=>•{•{•__sveltets_createSlot("default",•{});}↲ [generated] line 4 < / ↲ < / ↲ -↲ [original] line 1 +↲ [original] line 1 (rest generated at line 5) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 5 + ↲ +↲ [original] line 1 (rest generated at line 4) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { __sveltets_createSlot("foo", { }); } {/** •{•__sveltets_createSlot("foo",•{•});••}↲ [generated] line 6 < f oo " an f/ ↲ #==# Order-breaking mappings < na foo" f / ↲ -fallback↲ [original] line 3 +fallback↲ [original] line 3 (rest generated at line 7) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 7 + ↲ +fallback↲ [original] line 3 (rest generated at line 6) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { __sveltets_createSlot("bar", { "foo":foo,baz,"leet":true,}); } {/** •{•__sveltets_createSlot("bar",•{••••"foo":foo,baz,"leet":true,});••}↲ [generated] line 8 < b ar " {•••f oo= foo}baz}l eet f/ ↲ #== # Order-breaking mappings < foo={foo}• bar"• baz}•leet f / ↲ -fallback↲ [original] line 5 +fallback↲ [original] line 5 (rest generated at line 9) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} {/** - +↲ [generated] line 9 + ↲ +fallback↲ [original] line 5 (rest generated at line 8) ------------------------------------------------------------------------------------------------------------------------------------------------------ */} { __sveltets_createSlot("bar", { "foo":foo,baz,"leet":true,}); {/** •{•__sveltets_createSlot("bar",•{•••••"foo":foo,baz,"leet":true,});↲ [generated] line 10 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d60a88176..f1bc6d8f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -266,8 +266,8 @@ importers: specifier: ^2.0.1 version: 2.0.2 magic-string: - specifier: ^0.27.0 - version: 0.27.0 + specifier: ^0.30.11 + version: 0.30.11 mocha: specifier: ^9.2.0 version: 9.2.2 From 8c080cf36286984439b468e089e6e4eaacd4230e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:21:32 +0200 Subject: [PATCH 07/12] feat: better type checking for bindings in Svelte 5 (#2477) #1392 This adds enhanced type checking for bindings in Svelte 5: We're not only passing the variable in, we're also assigning the value of the component property back to the variable. That way, we can catch type errors like the child binding having a wider type as the input we give it. It's done for Svelte 5 only because it's a) easier there b) doesn't break as much code (people who upgrade can then also upgrade the types, or use type assertions in the template, which is only possible in Svelte 5). There's one limitation: Because of how the transformation works, we cannot infer generics. In other words, we will not catch type errors for bindings that rely on a generic type. The combination of generics + bindings is probably rare enough that this is okay, and we can revisit this later to try to find a way to make it work, if it comes up. Also note that this does not affect DOM element bindings like , this is only about component bindings. --- .../typescript/features/RenameProvider.ts | 10 +++++- .../bindings-two-way-check/Legacy.svelte | 7 ++++ .../bindings-two-way-check/Runes.svelte | 6 ++++ .../RunesGeneric.svelte | 6 ++++ .../expected_svelte_5.json | 36 +++++++++++++++++++ .../bindings-two-way-check/input.svelte | 19 ++++++++++ packages/svelte2tsx/repl/index.svelte | 12 +++---- .../src/htmlxtojsx_v2/nodes/Binding.ts | 6 ++++ .../htmlxtojsx_v2/nodes/InlineComponent.ts | 1 + packages/svelte2tsx/svelte-shims-v4.d.ts | 13 +++++++ .../samples/binding-bare/expected-svelte5.js | 4 +-- .../samples/binding/expected-svelte5.js | 8 ++--- .../editing-binding/expected-svelte5.js | 2 +- 13 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Legacy.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Runes.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/RunesGeneric.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expected_svelte_5.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/input.svelte diff --git a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts index 3b9caf86e..8340478c3 100644 --- a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts @@ -463,8 +463,16 @@ export class RenameProviderImpl implements RenameProvider { const mappedLocations = await Promise.all( renameLocations.map(async (loc) => { const snapshot = await snapshots.retrieve(loc.fileName); + const text = snapshot.getFullText(); + const end = loc.textSpan.start + loc.textSpan.length; - if (!isTextSpanInGeneratedCode(snapshot.getFullText(), loc.textSpan)) { + if ( + !(snapshot instanceof SvelteDocumentSnapshot) || + (!isTextSpanInGeneratedCode(text, loc.textSpan) && + // prevent generated code for bindings from being renamed + // (it's not inside a generate comment because diagnostics should show up) + text.slice(end + 3, end + 27) !== '__sveltets_binding_value') + ) { return { ...loc, range: this.mapRangeToOriginal(snapshot, loc.textSpan), diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Legacy.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Legacy.svelte new file mode 100644 index 000000000..18db75ea2 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Legacy.svelte @@ -0,0 +1,7 @@ + + +{legacy1} +{legacy2} \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Runes.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Runes.svelte new file mode 100644 index 000000000..299be4d66 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/Runes.svelte @@ -0,0 +1,6 @@ + + +{runes1} +{runes2} \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/RunesGeneric.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/RunesGeneric.svelte new file mode 100644 index 000000000..6accfc4b3 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/RunesGeneric.svelte @@ -0,0 +1,6 @@ + + +{foo} +{bar} \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expected_svelte_5.json new file mode 100644 index 000000000..6b1d57c83 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expected_svelte_5.json @@ -0,0 +1,36 @@ +[ + { + "code": 2322, + "message": "Type 'string | number' is not assignable to type 'string'.\n Type 'number' is not assignable to type 'string'.", + "range": { + "end": { + "character": 28, + "line": 17 + }, + "start": { + "character": 28, + "line": 17 + } + }, + "severity": 1, + "source": "ts", + "tags": [] + }, + { + "code": 2322, + "message": "Type 'string | number' is not assignable to type 'string'.\n Type 'number' is not assignable to type 'string'.", + "range": { + "end": { + "character": 45, + "line": 18 + }, + "start": { + "character": 45, + "line": 18 + } + }, + "severity": 1, + "source": "ts", + "tags": [] + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/input.svelte new file mode 100644 index 000000000..49020f784 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/input.svelte @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/packages/svelte2tsx/repl/index.svelte b/packages/svelte2tsx/repl/index.svelte index 49517cc72..bf8061fb0 100644 --- a/packages/svelte2tsx/repl/index.svelte +++ b/packages/svelte2tsx/repl/index.svelte @@ -1,7 +1,7 @@ - - -{#if value} - -{/if} + + \ No newline at end of file diff --git a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts index 5bb17cca7..555269b61 100644 --- a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts +++ b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts @@ -131,6 +131,12 @@ export function handleBinding( if (isSvelte5Plus && element instanceof InlineComponent) { // To check if property is actually bindable element.appendToStartEnd([`${element.name}.$$bindings = '${attr.name}';`]); + // To check if the binding is also assigned to the variable (only works when there's no assertion, we can't transform that) + if (!isTypescriptNode(attr.expression)) { + element.appendToStartEnd([ + `${expressionStr} = __sveltets_binding_value(${element.originalName}, '${attr.name}');` + ]); + } } if (element instanceof Element) { diff --git a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/InlineComponent.ts b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/InlineComponent.ts index b1bab058d..7459f227e 100644 --- a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/InlineComponent.ts +++ b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/InlineComponent.ts @@ -39,6 +39,7 @@ export class InlineComponent { private startTagEnd: number; private isSelfclosing: boolean; public child?: any; + public originalName = this.node.name; // Add const $$xxx = ... only if the variable name is actually used // in order to prevent "$$xxx is defined but never used" TS hints diff --git a/packages/svelte2tsx/svelte-shims-v4.d.ts b/packages/svelte2tsx/svelte-shims-v4.d.ts index 87e365798..5227cb041 100644 --- a/packages/svelte2tsx/svelte-shims-v4.d.ts +++ b/packages/svelte2tsx/svelte-shims-v4.d.ts @@ -254,3 +254,16 @@ declare function __sveltets_2_isomorphic_component< declare function __sveltets_2_isomorphic_component_slots< Props extends Record, Events extends Record, Slots extends Record, Exports extends Record, Bindings extends string >(klass: {props: Props, events: Events, slots: Slots, exports?: Exports, bindings?: Bindings }): __sveltets_2_IsomorphicComponent<__sveltets_2_PropsWithChildren, Events, Slots, Exports, Bindings>; + +type __sveltets_NonUndefined = T extends undefined ? never : T; + +declare function __sveltets_binding_value< + // @ts-ignore this is only used for Svelte 5, which knows about the Component type + Comp extends typeof import('svelte').Component, + Key extends string +>(comp: Comp, key: Key): Key extends keyof import('svelte').ComponentProps ? + // bail on unknown because it hints at a generic type which we can't properly resolve here + // remove undefined because optional properties have it, and would result in false positives + unknown extends import('svelte').ComponentProps[Key] ? any : __sveltets_NonUndefined[Key]> : any; +// Overload to ensure typings that only use old SvelteComponent class or something invalid are gracefully handled +declare function __sveltets_binding_value(comp: any, key: string): any diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js index bb934f658..43291dbae 100644 --- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js @@ -1,5 +1,5 @@ { svelteHTML.createElement("input", { "type":`text`,"bind:value":value,});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/} { svelteHTML.createElement("input", { "type":`checkbox`,"bind:checked":checked,});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value,}});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`checkbox`,checked,}});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'checked';} \ No newline at end of file + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value,}});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';value = __sveltets_binding_value(Input, 'value');} + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`checkbox`,checked,}});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'checked';checked = __sveltets_binding_value(Input, 'checked');} \ No newline at end of file diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js index 6c881fec9..355227003 100644 --- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js @@ -2,7 +2,7 @@ { svelteHTML.createElement("input", { "type":`text`,"bind:value":test,});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/} { svelteHTML.createElement("input", { "type":`text`,"bind:value":test,});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value'; Input} \ No newline at end of file + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';test = __sveltets_binding_value(Input, 'value');} + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';test = __sveltets_binding_value(Input, 'value');} + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';test = __sveltets_binding_value(Input, 'value');} + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';test = __sveltets_binding_value(Input, 'value'); Input} \ No newline at end of file diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js index 7bb207fea..31e8b2747 100644 --- a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js @@ -1,3 +1,3 @@ { svelteHTML.createElement("input", { });obj = __sveltets_2_any(null);} { svelteHTML.createElement("input", { "bind:value":obj.,});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/} - { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { value:obj.,}});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';} \ No newline at end of file + { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: { value:obj.,}});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';obj = __sveltets_binding_value(Input, 'value');} \ No newline at end of file From a3a7a91d0f5a2413333d0284530eee81994486fb Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:05:54 +0200 Subject: [PATCH 08/12] fix: Svelte module resolution tweaks (#2481) Svelte files are now resolved by intercepting checks for .d.svelte.ts instead of .svelte.ts. As a consequence, we can handle sibling foo.svelte / foo.svelte.ts files and importing both. When doing import Foo from './foo.svelte this now resolves to the Svelte file (prio to #2463 it would resovle to the .svelte.ts file), and doing import Foo from './foo.svelte.js will resolve to the TS file. --- packages/language-server/.gitignore | 1 + packages/language-server/package.json | 2 +- .../src/plugins/typescript/module-loader.ts | 2 +- .../src/plugins/typescript/svelte-sys.ts | 20 +++++-- .../src/plugins/typescript/utils.ts | 10 ++-- .../bindings-two-way-check/expectedv2.json | 18 ++++++ .../exports-map-svelte/expectedv2.json | 36 ++++++++++++ .../fixtures/exports-map-svelte/input.svelte | 9 +++ .../node_modules/package/foo.svelte | 3 + .../node_modules/package/package.json | 16 ++++++ .../node_modules/package/x-types.d.ts | 2 + .../node_modules/package/x.svelte | 3 + .../node_modules/package/y.svelte | 3 + .../fixtures/exports-map-svelte/tsconfig.json | 7 +++ .../fixtures/import-precedence/a.svelte | 3 + .../fixtures/import-precedence/a.svelte.d.ts | 1 + .../fixtures/import-precedence/b.svelte | 3 + .../fixtures/import-precedence/b.svelte.ts | 1 + .../fixtures/import-precedence/c.d.svelte.ts | 1 + .../fixtures/import-precedence/c.svelte | 3 + .../fixtures/import-precedence/d.svelte | 3 + .../fixtures/import-precedence/d.svelte.js | 1 + .../import-precedence/expectedv2.json | 1 + .../fixtures/import-precedence/input.svelte | 15 +++++ .../fixtures/import-precedence/tsconfig.json | 13 +++++ .../plugins/typescript/module-loader.test.ts | 4 +- .../plugins/typescript/svelte-sys.test.ts | 14 +++-- packages/typescript-plugin/package.json | 2 +- .../typescript-plugin/src/module-loader.ts | 56 +++++++++---------- packages/typescript-plugin/src/svelte-sys.ts | 2 +- packages/typescript-plugin/src/utils.ts | 10 +++- 31 files changed, 215 insertions(+), 50 deletions(-) create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expectedv2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/expectedv2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/input.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/foo.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/package.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x-types.d.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/y.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/tsconfig.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte.d.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.d.svelte.ts create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte.js create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/expectedv2.json create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/input.svelte create mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/tsconfig.json diff --git a/packages/language-server/.gitignore b/packages/language-server/.gitignore index 5dbbc7fbb..02c400bc6 100644 --- a/packages/language-server/.gitignore +++ b/packages/language-server/.gitignore @@ -1,3 +1,4 @@ dist/ .vscode/ node_modules/ +!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/ \ No newline at end of file diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 8ee68b6ed..f3702c47b 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -10,7 +10,7 @@ "./bin/server.js": "./bin/server.js" }, "scripts": { - "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\"", + "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.test.ts\"", "build": "tsc", "prepublishOnly": "npm run build", "watch": "tsc -w" diff --git a/packages/language-server/src/plugins/typescript/module-loader.ts b/packages/language-server/src/plugins/typescript/module-loader.ts index 915e50b26..4cbde00b8 100644 --- a/packages/language-server/src/plugins/typescript/module-loader.ts +++ b/packages/language-server/src/plugins/typescript/module-loader.ts @@ -104,7 +104,7 @@ class ImpliedNodeFormatResolver { return undefined; } - let mode = undefined; + let mode: ReturnType = undefined; if (sourceFile) { this.cacheImpliedNodeFormat(sourceFile, compilerOptions); mode = ts.getModeForResolutionAtIndex(sourceFile, importIdxInFile, compilerOptions); diff --git a/packages/language-server/src/plugins/typescript/svelte-sys.ts b/packages/language-server/src/plugins/typescript/svelte-sys.ts index a639b2a58..553bd17a3 100644 --- a/packages/language-server/src/plugins/typescript/svelte-sys.ts +++ b/packages/language-server/src/plugins/typescript/svelte-sys.ts @@ -11,6 +11,17 @@ export function createSvelteSys(tsSystem: ts.System) { function svelteFileExists(path: string) { if (isVirtualSvelteFilePath(path)) { const sveltePath = toRealSvelteFilePath(path); + + // First check if there's a `.svelte.d.ts` or `.d.svelte.ts` file, which should take precedence + const dtsPath = sveltePath.slice(0, -7) + '.svelte.d.ts'; + const dtsPathExists = fileExistsCache.get(dtsPath) ?? tsSystem.fileExists(dtsPath); + fileExistsCache.set(dtsPath, dtsPathExists); + if (dtsPathExists) return false; + + const svelteDtsPathExists = fileExistsCache.get(path) ?? tsSystem.fileExists(path); + fileExistsCache.set(path, svelteDtsPathExists); + if (svelteDtsPathExists) return false; + const sveltePathExists = fileExistsCache.get(sveltePath) ?? tsSystem.fileExists(sveltePath); fileExistsCache.set(sveltePath, sveltePathExists); @@ -33,10 +44,11 @@ export function createSvelteSys(tsSystem: ts.System) { svelteFileExists, getRealSveltePathIfExists, fileExists(path: string) { - // We need to check both .svelte and .svelte.ts/js because that's how Svelte 5 will likely mark files with runes in them + // We need to check if this is a virtual svelte file const sveltePathExists = svelteFileExists(path); - const exists = - sveltePathExists || (fileExistsCache.get(path) ?? tsSystem.fileExists(path)); + if (sveltePathExists) return true; + + const exists = fileExistsCache.get(path) ?? tsSystem.fileExists(path); fileExistsCache.set(path, exists); return exists; }, @@ -66,7 +78,7 @@ export function createSvelteSys(tsSystem: ts.System) { const realpath = tsSystem.realpath; svelteSys.realpath = function (path) { if (svelteFileExists(path)) { - return realpath(toRealSvelteFilePath(path)) + '.ts'; + return realpath(toRealSvelteFilePath(path)); } return realpath(path); }; diff --git a/packages/language-server/src/plugins/typescript/utils.ts b/packages/language-server/src/plugins/typescript/utils.ts index 62fa6359d..a8a6ce1f6 100644 --- a/packages/language-server/src/plugins/typescript/utils.ts +++ b/packages/language-server/src/plugins/typescript/utils.ts @@ -70,15 +70,17 @@ export function isSvelteFilePath(filePath: string) { } export function isVirtualSvelteFilePath(filePath: string) { - return filePath.endsWith('.svelte.ts'); + return filePath.endsWith('.d.svelte.ts'); } export function toRealSvelteFilePath(filePath: string) { - return filePath.slice(0, -'.ts'.length); + return filePath.slice(0, -11 /* 'd.svelte.ts'.length */) + 'svelte'; } -export function toVirtualSvelteFilePath(filePath: string) { - return filePath.endsWith('.ts') ? filePath : filePath + '.ts'; +export function toVirtualSvelteFilePath(svelteFilePath: string) { + return isVirtualSvelteFilePath(svelteFilePath) + ? svelteFilePath + : svelteFilePath.slice(0, -6 /* 'svelte'.length */) + 'd.svelte.ts'; } export function ensureRealSvelteFilePath(filePath: string) { diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expectedv2.json new file mode 100644 index 000000000..f8c8cb449 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings-two-way-check/expectedv2.json @@ -0,0 +1,18 @@ +[ + { + "code": -1, + "message": "Unexpected token", + "range": { + "end": { + "character": 47, + "line": 12 + }, + "start": { + "character": 47, + "line": 12 + } + }, + "severity": 1, + "source": "ts" + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/expectedv2.json new file mode 100644 index 000000000..58680d099 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/expectedv2.json @@ -0,0 +1,36 @@ +[ + { + "code": 2307, + "message": "Cannot find module 'package' or its corresponding type declarations.", + "range": { + "end": { + "character": 45, + "line": 1 + }, + "start": { + "character": 36, + "line": 1 + } + }, + "severity": 1, + "source": "ts", + "tags": [] + }, + { + "code": 2307, + "message": "Cannot find module 'package/y' or its corresponding type declarations.", + "range": { + "start": { + "character": 38, + "line": 3 + }, + "end": { + "character": 49, + "line": 3 + } + }, + "severity": 1, + "source": "ts", + "tags": [] + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/input.svelte new file mode 100644 index 000000000..2775a7582 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/input.svelte @@ -0,0 +1,9 @@ + + + + + diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/foo.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/foo.svelte new file mode 100644 index 000000000..e29d29c54 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/foo.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/package.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/package.json new file mode 100644 index 000000000..422ee721e --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/package.json @@ -0,0 +1,16 @@ +{ + "name": "package", + "version": "1.0.0", + "exports": { + ".": { + "svelte": "./foo.svelte" + }, + "./x": { + "types": "./x-types.d.ts", + "svelte": "./x.svelte" + }, + "./y": { + "svelte": "./y.svelte" + } + } +} \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x-types.d.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x-types.d.ts new file mode 100644 index 000000000..4d27ef5b0 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x-types.d.ts @@ -0,0 +1,2 @@ +declare const X: any; +export default X; \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x.svelte new file mode 100644 index 000000000..fb5dbfefa --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/x.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/y.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/y.svelte new file mode 100644 index 000000000..78f01008c --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package/y.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/tsconfig.json new file mode 100644 index 000000000..d5e498207 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "moduleResolution": "Bundler" + } +} diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte new file mode 100644 index 000000000..1f6a1cbe4 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte.d.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte.d.ts new file mode 100644 index 000000000..227cc1bb1 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/a.svelte.d.ts @@ -0,0 +1 @@ +export declare const a: boolean; diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte new file mode 100644 index 000000000..1f6a1cbe4 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte.ts new file mode 100644 index 000000000..1d9db10ef --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/b.svelte.ts @@ -0,0 +1 @@ +export const b = true; diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.d.svelte.ts b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.d.svelte.ts new file mode 100644 index 000000000..a12f971dd --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.d.svelte.ts @@ -0,0 +1 @@ +export declare const c: boolean; diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.svelte new file mode 100644 index 000000000..1f6a1cbe4 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/c.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte new file mode 100644 index 000000000..1f6a1cbe4 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte.js b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte.js new file mode 100644 index 000000000..ecee2a83c --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/d.svelte.js @@ -0,0 +1 @@ +export const d = true; diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/expectedv2.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/expectedv2.json @@ -0,0 +1 @@ +[] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/input.svelte new file mode 100644 index 000000000..9966c707e --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/input.svelte @@ -0,0 +1,15 @@ + + + + diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/tsconfig.json new file mode 100644 index 000000000..eef515315 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/import-precedence/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "allowJs": true, + "target": "ESNext", + /** + This is actually not needed, but makes the tests faster + because TS does not look up other types. + */ + "types": ["svelte"], + "allowArbitraryExtensions": true // else .d.svelte.ts will be an error + } +} diff --git a/packages/language-server/test/plugins/typescript/module-loader.test.ts b/packages/language-server/test/plugins/typescript/module-loader.test.ts index 559eeb447..1c3561cc2 100644 --- a/packages/language-server/test/plugins/typescript/module-loader.test.ts +++ b/packages/language-server/test/plugins/typescript/module-loader.test.ts @@ -59,12 +59,12 @@ describe('createSvelteModuleLoader', () => { it('uses svelte script kind if resolved module is svelte file', async () => { const resolvedModule: ts.ResolvedModuleFull = { extension: ts.Extension.Ts, - resolvedFileName: 'filename.svelte.ts' + resolvedFileName: 'filename.d.svelte.ts' }; const { getSvelteSnapshotStub, moduleResolver, svelteSys } = setup(resolvedModule); svelteSys.getRealSveltePathIfExists = (filename: string) => - filename === 'filename.svelte.ts' ? 'filename.svelte' : filename; + filename === 'filename.d.svelte.ts' ? 'filename.svelte' : filename; const result = moduleResolver.resolveModuleNames( ['./normal.ts'], diff --git a/packages/language-server/test/plugins/typescript/svelte-sys.test.ts b/packages/language-server/test/plugins/typescript/svelte-sys.test.ts index 2da35db20..ba9a21d5a 100644 --- a/packages/language-server/test/plugins/typescript/svelte-sys.test.ts +++ b/packages/language-server/test/plugins/typescript/svelte-sys.test.ts @@ -29,18 +29,24 @@ describe('Svelte Sys', () => { } describe('#fileExists', () => { - it('should leave files with no .svelte.ts-ending as is', async () => { + it('should leave files with no .d.svelte.ts-ending as is', async () => { const { loader, fileExistsStub } = setupLoader(); loader.fileExists('../file.ts'); assert.strictEqual(fileExistsStub.getCall(0).args[0], '../file.ts'); }); - it('should convert .svelte.ts-endings', async () => { + it('should convert .d.svelte.ts-endings', async () => { const { loader, fileExistsStub } = setupLoader(); - loader.fileExists('../file.svelte.ts'); + fileExistsStub.onCall(0).returns(false); + fileExistsStub.onCall(1).returns(false); + fileExistsStub.onCall(2).returns(true); - assert.strictEqual(fileExistsStub.getCall(0).args[0], '../file.svelte'); + loader.fileExists('../file.d.svelte.ts'); + + assert.strictEqual(fileExistsStub.getCall(0).args[0], '../file.svelte.d.ts'); + assert.strictEqual(fileExistsStub.getCall(1).args[0], '../file.d.svelte.ts'); + assert.strictEqual(fileExistsStub.getCall(2).args[0], '../file.svelte'); }); }); }); diff --git a/packages/typescript-plugin/package.json b/packages/typescript-plugin/package.json index 6928186a4..766ac42c5 100644 --- a/packages/typescript-plugin/package.json +++ b/packages/typescript-plugin/package.json @@ -1,6 +1,6 @@ { "name": "typescript-svelte-plugin", - "version": "0.2.0", + "version": "0.3.0", "description": "A TypeScript Plugin providing Svelte intellisense", "main": "dist/src/index.js", "scripts": { diff --git a/packages/typescript-plugin/src/module-loader.ts b/packages/typescript-plugin/src/module-loader.ts index cc2003cba..569f073b3 100644 --- a/packages/typescript-plugin/src/module-loader.ts +++ b/packages/typescript-plugin/src/module-loader.ts @@ -3,30 +3,7 @@ import { ConfigManager } from './config-manager'; import { Logger } from './logger'; import { SvelteSnapshotManager } from './svelte-snapshots'; import { createSvelteSys } from './svelte-sys'; -import { ensureRealSvelteFilePath, isVirtualSvelteFilePath } from './utils'; - -// TODO remove when we update to typescript 5.0 -declare module 'typescript/lib/tsserverlibrary' { - interface LanguageServiceHost { - /** @deprecated supply resolveModuleNameLiterals instead for resolution that can handle newer resolution modes like nodenext */ - resolveModuleNames?( - moduleNames: string[], - containingFile: string, - reusedNames: string[] | undefined, - redirectedReference: ts.ResolvedProjectReference | undefined, - options: ts.CompilerOptions, - containingSourceFile?: ts.SourceFile - ): (ts.ResolvedModule | undefined)[]; - resolveModuleNameLiterals?( - moduleLiterals: readonly ts.StringLiteralLike[], - containingFile: string, - redirectedReference: ts.ResolvedProjectReference | undefined, - options: ts.CompilerOptions, - containingSourceFile: ts.SourceFile, - reusedNames: readonly ts.StringLiteralLike[] | undefined - ): readonly ts.ResolvedModuleWithFailedLookupLocations[]; - } -} +import { ensureRealSvelteFilePath, isSvelteFilePath, isVirtualSvelteFilePath } from './utils'; /** * Caches resolved modules. @@ -110,6 +87,8 @@ export function patchModuleLoader( if (lsHost.resolveModuleNameLiterals) { lsHost.resolveModuleNameLiterals = resolveModuleNameLiterals; } else { + // TODO do we need to keep this around? We're requiring 5.0 now, so TS doesn't need it, + // but would this break when other TS plugins are used and we no longer provide it? lsHost.resolveModuleNames = resolveModuleNames; } @@ -161,12 +140,22 @@ export function patchModuleLoader( return resolved.map((tsResolvedModule, idx) => { const moduleName = moduleNames[idx]; - if (tsResolvedModule || !ensureRealSvelteFilePath(moduleName).endsWith('.svelte')) { + if ( + !isSvelteFilePath(moduleName) || + // corresponding .d.ts files take precedence over .svelte files + tsResolvedModule?.resolvedFileName.endsWith('.d.ts') || + tsResolvedModule?.resolvedFileName.endsWith('.d.svelte.ts') + ) { return tsResolvedModule; } - return resolveSvelteModuleNameFromCache(moduleName, containingFile, compilerOptions) - .resolvedModule; + const result = resolveSvelteModuleNameFromCache( + moduleName, + containingFile, + compilerOptions + ).resolvedModule; + // .svelte takes precedence over .svelte.ts etc + return result ?? tsResolvedModule; }); } @@ -238,14 +227,20 @@ export function patchModuleLoader( return resolved.map((tsResolvedModule, idx) => { const moduleName = moduleLiterals[idx].text; + const resolvedModule = tsResolvedModule.resolvedModule; + if ( - tsResolvedModule.resolvedModule || - !ensureRealSvelteFilePath(moduleName).endsWith('.svelte') + !isSvelteFilePath(moduleName) || + // corresponding .d.ts files take precedence over .svelte files + resolvedModule?.resolvedFileName.endsWith('.d.ts') || + resolvedModule?.resolvedFileName.endsWith('.d.svelte.ts') ) { return tsResolvedModule; } - return resolveSvelteModuleNameFromCache(moduleName, containingFile, options); + const result = resolveSvelteModuleNameFromCache(moduleName, containingFile, options); + // .svelte takes precedence over .svelte.ts etc + return result.resolvedModule ? result : tsResolvedModule; }); } @@ -262,6 +257,7 @@ export function patchModuleLoader( } const resolvedModule = resolveSvelteModuleName(moduleName, containingFile, options); + moduleCache.set(moduleName, containingFile, resolvedModule); return { resolvedModule: resolvedModule diff --git a/packages/typescript-plugin/src/svelte-sys.ts b/packages/typescript-plugin/src/svelte-sys.ts index d530e0b47..75b0a6f80 100644 --- a/packages/typescript-plugin/src/svelte-sys.ts +++ b/packages/typescript-plugin/src/svelte-sys.ts @@ -30,7 +30,7 @@ export function createSvelteSys(ts: _ts, logger: Logger) { const realpath = ts.sys.realpath; svelteSys.realpath = function (path) { if (isVirtualSvelteFilePath(path)) { - return realpath(toRealSvelteFilePath(path)) + '.ts'; + return realpath(toRealSvelteFilePath(path)); } return realpath(path); }; diff --git a/packages/typescript-plugin/src/utils.ts b/packages/typescript-plugin/src/utils.ts index d189f7ad1..1b14a6dbe 100644 --- a/packages/typescript-plugin/src/utils.ts +++ b/packages/typescript-plugin/src/utils.ts @@ -8,11 +8,17 @@ export function isSvelteFilePath(filePath: string) { } export function isVirtualSvelteFilePath(filePath: string) { - return filePath.endsWith('.svelte.ts'); + return filePath.endsWith('.d.svelte.ts'); } export function toRealSvelteFilePath(filePath: string) { - return filePath.slice(0, -'.ts'.length); + return filePath.slice(0, -11 /* 'd.svelte.ts'.length */) + 'svelte'; +} + +export function toVirtualSvelteFilePath(svelteFilePath: string) { + return isVirtualSvelteFilePath(svelteFilePath) + ? svelteFilePath + : svelteFilePath.slice(0, -6 /* 'svelte'.length */) + 'd.svelte.ts'; } export function ensureRealSvelteFilePath(filePath: string) { From d99497176dcca89de69471934b05c433b64558e3 Mon Sep 17 00:00:00 2001 From: "Lyu, Wei-Da" <36730922+jasonlyu123@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:33:28 +0800 Subject: [PATCH 09/12] feat: allow autoImportFileExcludePatterns to ignore files outside the root (#2480) part of #2479 --- packages/language-server/src/ls-config.ts | 36 +++++++---- .../typescript/features/preferences.test.ts | 64 ++++++++++++------- .../code-action-outside-root.svelte | 4 ++ 3 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte diff --git a/packages/language-server/src/ls-config.ts b/packages/language-server/src/ls-config.ts index 39e098ab3..63389e7bb 100644 --- a/packages/language-server/src/ls-config.ts +++ b/packages/language-server/src/ls-config.ts @@ -478,16 +478,25 @@ export class LSConfigManager { }; } - getTsUserPreferences(lang: TsUserConfigLang, workspacePath: string | null): ts.UserPreferences { + getTsUserPreferences( + lang: TsUserConfigLang, + normalizedWorkspacePath: string | null + ): ts.UserPreferences { const userPreferences = this.tsUserPreferences[lang]; - if (!workspacePath || !userPreferences.autoImportFileExcludePatterns) { + if (!normalizedWorkspacePath || !userPreferences.autoImportFileExcludePatterns) { return userPreferences; } - let autoImportFileExcludePatterns = this.resolvedAutoImportExcludeCache.get(workspacePath); + let autoImportFileExcludePatterns = + this.resolvedAutoImportExcludeCache.get(normalizedWorkspacePath); if (!autoImportFileExcludePatterns) { + const version = ts.version.split('.'); + const major = parseInt(version[0]); + const minor = parseInt(version[1]); + + const gte5_4 = major > 5 || (major === 5 && minor >= 4); autoImportFileExcludePatterns = userPreferences.autoImportFileExcludePatterns.map( (p) => { // Normalization rules: https://github.com/microsoft/TypeScript/pull/49578 @@ -497,17 +506,20 @@ export class LSConfigManager { return p; } - return path.join( - workspacePath, - p.startsWith('*') - ? '/' + slashNormalized - : isRelative - ? p - : '/**/' + slashNormalized - ); + // https://github.com/microsoft/vscode/pull/202762 + // ts 5.4+ supports leading wildcards + const wildcardPrefix = gte5_4 ? '' : path.parse(normalizedWorkspacePath).root; + return p.startsWith('*') + ? wildcardPrefix + slashNormalized + : isRelative + ? path.join(normalizedWorkspacePath, p) + : wildcardPrefix + '**/' + slashNormalized; } ); - this.resolvedAutoImportExcludeCache.set(workspacePath, autoImportFileExcludePatterns); + this.resolvedAutoImportExcludeCache.set( + normalizedWorkspacePath, + autoImportFileExcludePatterns + ); } return { diff --git a/packages/language-server/test/plugins/typescript/features/preferences.test.ts b/packages/language-server/test/plugins/typescript/features/preferences.test.ts index c794dc55b..b4c35f187 100644 --- a/packages/language-server/test/plugins/typescript/features/preferences.test.ts +++ b/packages/language-server/test/plugins/typescript/features/preferences.test.ts @@ -64,7 +64,14 @@ describe('ts user preferences', function () { typescript: { ...getPreferences(), ...preferences }, javascript: { ...getPreferences(), ...preferences } }); - return new LSAndTSDocResolver(docManager, [pathToUrl(testFilesDir)], configManager); + return { + lsAndTsDocResolver: new LSAndTSDocResolver( + docManager, + [pathToUrl(testFilesDir)], + configManager + ), + configManager + }; } function getDefaultPreferences(): TsUserPreferencesConfig { @@ -79,11 +86,8 @@ describe('ts user preferences', function () { it('provides auto import completion according to preferences', async () => { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -102,15 +106,13 @@ describe('ts user preferences', function () { context: CodeActionContext ) { const { docManager, document } = setup(filename); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); + const codeActionProvider = new CodeActionsProviderImpl( lsAndTsDocResolver, completionProvider, - new LSConfigManager() + configManager ); const codeAction = await codeActionProvider.getCodeActions(document, range, context); @@ -137,7 +139,7 @@ describe('ts user preferences', function () { it('provides auto import suggestions according to preferences', async () => { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { suggest: { autoImports: false, includeAutomaticOptionalChainCompletions: undefined, @@ -147,10 +149,7 @@ describe('ts user preferences', function () { includeCompletionsWithSnippetText: undefined } }); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -165,14 +164,14 @@ describe('ts user preferences', function () { function setupImportModuleSpecifierEndingJs() { const { docManager, document } = setup('module-specifier-js.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { preferences: { ...getDefaultPreferences(), importModuleSpecifierEnding: 'js' } }); - return { document, lsAndTsDocResolver }; + return { document, lsAndTsDocResolver, configManager }; } it('provides auto import for svelte component when importModuleSpecifierEnding is js', async () => { @@ -248,16 +247,13 @@ describe('ts user preferences', function () { async function testExcludeDefinitionDir(pattern: string) { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { preferences: { ...getDefaultPreferences(), autoImportFileExcludePatterns: [pattern] } }); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -280,4 +276,24 @@ describe('ts user preferences', function () { it('exclude auto import (**/ pattern)', async () => { await testExcludeDefinitionDir('**/definition'); }); + + it('exclude auto import outside of the root', async () => { + const { docManager, document } = setup('code-action-outside-root.svelte'); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { + preferences: { + ...getDefaultPreferences(), + autoImportFileExcludePatterns: ['definitions.ts'] + } + }); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); + + const completions = await completionProvider.getCompletions( + document, + Position.create(4, 7) + ); + + const item = completions?.items.find((item) => item.label === 'blubb'); + + assert.equal(item, undefined); + }); }); diff --git a/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte b/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte new file mode 100644 index 000000000..cb38cfe93 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte @@ -0,0 +1,4 @@ + \ No newline at end of file From 399ad6a08d03b14f46a40f1a1483a051d08a8ff2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:38:11 +0200 Subject: [PATCH 10/12] fix: detect `