diff --git a/.vscode/launch.json b/.vscode/launch.json index dfeb32f09c..a8b3ff3a10 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,23 +19,6 @@ "script": "watch" } }, - { - "type": "extensionHost", - "request": "launch", - "name": "Launch TypeScript Plugin", - "runtimeExecutable": "${execPath}", - "args": [ - "--disable-extensions", - "--extensionDevelopmentPath=${workspaceRoot}/extensions/vscode-typescript-plugin" - ], - "outFiles": [ - "${workspaceRoot}/*/*/out/**/*.js" - ], - "preLaunchTask": { - "type": "npm", - "script": "watch" - } - }, { "name": "Launch Web Client", "type": "pwa-extensionHost", diff --git a/extensions/vscode-typescript-plugin/.vscodeignore b/extensions/vscode-typescript-plugin/.vscodeignore deleted file mode 100644 index eebfc0444c..0000000000 --- a/extensions/vscode-typescript-plugin/.vscodeignore +++ /dev/null @@ -1,2 +0,0 @@ -scripts -typescript-vue-plugin-forward diff --git a/extensions/vscode-typescript-plugin/CHANGELOG.md b/extensions/vscode-typescript-plugin/CHANGELOG.md deleted file mode 100644 index ea5123962c..0000000000 --- a/extensions/vscode-typescript-plugin/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -For a full change history, refer to the [CHANGELOG](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md) on the Volar GitHub site. \ No newline at end of file diff --git a/extensions/vscode-typescript-plugin/LICENSE b/extensions/vscode-typescript-plugin/LICENSE deleted file mode 100644 index b55e47a7e8..0000000000 --- a/extensions/vscode-typescript-plugin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-present Johnson Chu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/extensions/vscode-typescript-plugin/README.md b/extensions/vscode-typescript-plugin/README.md deleted file mode 100644 index ce888a0f4f..0000000000 --- a/extensions/vscode-typescript-plugin/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# TypeScript Vue Plugin - -> A [TS server plugin](https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin) to make TS server know *.vue files. - -[Plugin's page on Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) - -⚠️ It's recommended to [use take over mode instead of VSCode built-in TS plugin](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode). - -This plugin proxies TS server requests to provide some extra functionality: - -- When finding references in *.ts files, you also get results from *.vue files. -- When renaming in *.ts files, references on *.vue files also get adjusted. -- When typing import statements, *.vue files will also appear for autocompletion. -- (And some extra details most people don't need to know...) - -## Sponsors - -

- - - -

diff --git a/extensions/vscode-typescript-plugin/images/icon.png b/extensions/vscode-typescript-plugin/images/icon.png deleted file mode 100644 index 5d72d8b80c..0000000000 Binary files a/extensions/vscode-typescript-plugin/images/icon.png and /dev/null differ diff --git a/extensions/vscode-typescript-plugin/package.json b/extensions/vscode-typescript-plugin/package.json deleted file mode 100644 index fecf6cbebc..0000000000 --- a/extensions/vscode-typescript-plugin/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "private": true, - "name": "vscode-typescript-vue-plugin", - "version": "1.8.26", - "repository": { - "type": "git", - "url": "https://github.com/vuejs/language-tools.git", - "directory": "extensions/vscode-typescript-plugin" - }, - "sponsor": { - "url": "https://github.com/sponsors/johnsoncodehk" - }, - "icon": "images/icon.png", - "displayName": "TypeScript Vue Plugin (Volar)", - "description": "Vue Plugin for TypeScript server", - "author": "johnsoncodehk", - "publisher": "Vue", - "engines": { - "vscode": "*" - }, - "contributes": { - "typescriptServerPlugins": [ - { - "name": "typescript-vue-plugin-bundle", - "enableForWorkspaceTypeScriptVersions": true - } - ] - }, - "scripts": { - "vscode:prepublish": "npm run build", - "prebuild": "cd ../.. && npm run build", - "build": "node scripts/build", - "watch": "node scripts/build --watch", - "pack": "vsce package", - "release": "vsce publish", - "release:next": "vsce publish --pre-release" - }, - "devDependencies": { - "esbuild": "latest", - "typescript-vue-plugin": "1.8.26", - "vsce": "latest" - } -} diff --git a/extensions/vscode-typescript-plugin/scripts/build.js b/extensions/vscode-typescript-plugin/scripts/build.js deleted file mode 100644 index 649c9856cb..0000000000 --- a/extensions/vscode-typescript-plugin/scripts/build.js +++ /dev/null @@ -1,35 +0,0 @@ -// @ts-check - -require('esbuild').context({ - entryPoints: ['./node_modules/typescript-vue-plugin/out/index.js'], - bundle: true, - outfile: './node_modules/typescript-vue-plugin-bundle/index.js', - external: [ - 'vscode', - 'typescript', // vue-component-meta - ], - format: 'cjs', - platform: 'node', - tsconfig: './tsconfig.json', - minify: process.argv.includes('--minify'), - plugins: [{ - name: 'umd2esm', - setup(build) { - build.onResolve({ filter: /^(vscode-.*-languageservice|jsonc-parser)/ }, args => { - const pathUmdMay = require.resolve(args.path, { paths: [args.resolveDir] }) - const pathEsm = pathUmdMay.replace('/umd/', '/esm/') - return { path: pathEsm } - }) - }, - }], -}).then(async ctx => { - console.log('building...'); - if (process.argv.includes('--watch')) { - await ctx.watch(); - console.log('watching...'); - } else { - await ctx.rebuild(); - await ctx.dispose(); - console.log('finished.'); - } -}) diff --git a/extensions/vscode-typescript-plugin/tsconfig.json b/extensions/vscode-typescript-plugin/tsconfig.json deleted file mode 100644 index 173527e3aa..0000000000 --- a/extensions/vscode-typescript-plugin/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "out", - "rootDir": "src", - }, - "include": [ "src" ], - "references": [ - { "path": "../../packages/typescript-plugin/tsconfig.json" }, - ] -} \ No newline at end of file diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 5dcdc9eac1..49bddf9a2c 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -24,11 +24,7 @@ "activationEvents": [ "onLanguage:vue", "onLanguage:markdown", - "onLanguage:html", - "onLanguage:javascript", - "onLanguage:typescript", - "onLanguage:javascriptreact", - "onLanguage:typescriptreact" + "onLanguage:html" ], "main": "./client.js", "browser": "./web.js", @@ -49,46 +45,6 @@ }, "contributes": { "jsonValidation": [ - { - "fileMatch": "tsconfig.json", - "url": "https://json.schemastore.org/tsconfig" - }, - { - "fileMatch": "tsconfig.*.json", - "url": "https://json.schemastore.org/tsconfig" - }, - { - "fileMatch": "tsconfig-*.json", - "url": "https://json.schemastore.org/tsconfig" - }, - { - "fileMatch": "typings.json", - "url": "https://json.schemastore.org/typings" - }, - { - "fileMatch": ".bowerrc", - "url": "https://json.schemastore.org/bowerrc" - }, - { - "fileMatch": ".babelrc", - "url": "https://json.schemastore.org/babelrc" - }, - { - "fileMatch": ".babelrc.json", - "url": "https://json.schemastore.org/babelrc" - }, - { - "fileMatch": "babel.config.json", - "url": "https://json.schemastore.org/babelrc" - }, - { - "fileMatch": "jsconfig.json", - "url": "https://json.schemastore.org/jsconfig" - }, - { - "fileMatch": "jsconfig.*.json", - "url": "https://json.schemastore.org/jsconfig" - }, { "fileMatch": "tsconfig.json", "url": "./dist/schemas/vue-tsconfig.schema.json" @@ -201,6 +157,16 @@ "id": "vue-sfc-style-variable-injection" } ], + "typescriptServerPlugins": [ + { + "name": "typescript-vue-plugin-bundle", + "enableForWorkspaceTypeScriptVersions": true, + "configNamespace": "typescript", + "languages": [ + "vue" + ] + } + ], "grammars": [ { "language": "vue", @@ -317,9 +283,6 @@ "type": "object", "title": "Volar", "properties": { - "typescript.tsdk": { - "type": "string" - }, "volar.format.initialIndent": { "type": "object", "description": "Whether to have initial indent.", @@ -381,23 +344,7 @@ } } }, - "volar.takeOverMode.extension": { - "type": "string", - "default": "Vue.volar", - "description": "The extension that take over language support for *.ts." - }, - "vue-semantic-server.trace.server": { - "scope": "window", - "type": "string", - "enum": [ - "off", - "messages", - "verbose" - ], - "default": "off", - "description": "Traces the communication between VS Code and the language server." - }, - "vue-syntactic-server.trace.server": { + "vue.trace.server": { "scope": "window", "type": "string", "enum": [ @@ -425,11 +372,6 @@ "default": "node", "description": "Vue Language Server runtime." }, - "vue.server.configFilePath": { - "type": "string", - "default": "./volar.config.js", - "description": "Path to volar.config.js." - }, "vue.server.maxFileSize": { "type": "number", "default": 20971520, @@ -474,7 +416,7 @@ "items": { "type": "string" }, - "default": [], + "default": [ ], "description": "List any additional file extensions that should be processed as Vue files (requires restart)." }, "vue.server.fullCompletionList": { @@ -599,16 +541,6 @@ } }, "commands": [ - { - "command": "volar.action.restartServer", - "title": "Restart Vue server", - "category": "Volar" - }, - { - "command": "volar.action.reloadProject", - "title": "Reload Project", - "category": "Volar" - }, { "command": "volar.action.doctor", "title": "Doctor", @@ -629,27 +561,37 @@ "title": "Split '); const metaSnapshots: Record = {}; - const host = new Proxy>({ - getScriptFileNames: () => { - const names = _host.getScriptFileNames(); - return [ - ...names, - ...names.map(getMetaFileName), - globalComponentName, - getMetaFileName(globalComponentName), - ]; - }, - getScriptSnapshot: fileName => { - if (isMetaFileName(fileName)) { - if (!metaSnapshots[fileName]) { - metaSnapshots[fileName] = ts.ScriptSnapshot.fromString(getMetaScriptContent(fileName)); - } - return metaSnapshots[fileName]; - } - else if (fileName === globalComponentName) { - return globalComponentSnapshot; - } - else { - return _host.getScriptSnapshot(fileName); - } - }, - }, { - get(target, prop) { - if (prop in target) { - return target[prop as keyof typeof target]; + const getScriptFileNames = host.getScriptFileNames; + const getScriptSnapshot = host.getScriptSnapshot; + host.getScriptFileNames = () => { + const names = getScriptFileNames(); + return [ + ...names, + ...names.map(getMetaFileName), + globalComponentName, + getMetaFileName(globalComponentName), + ]; + }; + host.getScriptSnapshot = (fileName) => { + if (isMetaFileName(fileName)) { + if (!metaSnapshots[fileName]) { + metaSnapshots[fileName] = ts.ScriptSnapshot.fromString(getMetaScriptContent(fileName)); } - return _host[prop as keyof typeof _host]; - }, - }) as vue.TypeScriptLanguageHost; - const vueLanguages = vue.createLanguages( + return metaSnapshots[fileName]; + } + else if (fileName === globalComponentName) { + return globalComponentSnapshot; + } + else { + return getScriptSnapshot(fileName); + } + }; + + const vueLanguagePlugins = vue.createLanguages( ts, host.getCompilationSettings(), vueCompilerOptions, ); - const core = vue.createLanguageContext(host, vueLanguages); - const tsLsHost = createLanguageServiceHost(core, ts, ts.sys); - const tsLs = ts.createLanguageService(tsLsHost); - - decorateLanguageService(core.virtualFiles, tsLs, false); + const language = createLanguage( + ts, + ts.sys, + vueLanguagePlugins, + configFileName, + host, + ); + const { languageServiceHost } = language.typescript!; + const tsLs = ts.createLanguageService(languageServiceHost); if (checkerOptions.forceUseTs) { - const getScriptKind = tsLsHost.getScriptKind; - tsLsHost.getScriptKind = (fileName) => { + const getScriptKind = languageServiceHost.getScriptKind?.bind(languageServiceHost); + languageServiceHost.getScriptKind = (fileName) => { if (fileName.endsWith('.vue.js')) { return ts.ScriptKind.TS; } @@ -281,7 +283,7 @@ ${vueCompilerOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} .map((prop) => { const { resolveNestedProperties, - } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, core); + } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, language); return resolveNestedProperties(prop); }) @@ -300,7 +302,7 @@ ${vueCompilerOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} const printer = ts.createPrinter(checkerOptions.printer); const snapshot = host.getScriptSnapshot(componentPath)!; - const vueSourceFile = core.virtualFiles.getSource(componentPath)?.root; + const vueSourceFile = language.files.getSourceFile(componentPath)?.virtualFile?.[0]; const vueDefaults = vueSourceFile && exportName === 'default' ? (vueSourceFile instanceof vue.VueFile ? readVueComponentDefaultProps(vueSourceFile, printer, ts, vueCompilerOptions) : {}) : {}; @@ -345,7 +347,7 @@ ${vueCompilerOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} const { resolveEventSignature, - } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, core); + } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, language); return resolveEventSignature(call); }).filter(event => event.name); @@ -365,7 +367,7 @@ ${vueCompilerOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} return properties.map((prop) => { const { resolveSlotProperties, - } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, core); + } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, language); return resolveSlotProperties(prop); }); @@ -388,7 +390,7 @@ ${vueCompilerOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} return properties.map((prop) => { const { resolveExposedProperties, - } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, core); + } = createSchemaResolvers(typeChecker, symbolNode!, checkerOptions, ts, language); return resolveExposedProperties(prop); }); @@ -447,7 +449,7 @@ function createSchemaResolvers( symbolNode: ts.Expression, { rawType, schema: options, noDeclarations }: MetaCheckerOptions, ts: typeof import('typescript/lib/tsserverlibrary'), - core: vue.LanguageContext, + core: vue.Language, ) { const visited = new Set(); @@ -638,12 +640,12 @@ function createSchemaResolvers( } function getDeclaration(declaration: ts.Declaration): Declaration | undefined { const fileName = declaration.getSourceFile().fileName; - const [virtualFile] = core.virtualFiles.getVirtualFile(fileName); + const [virtualFile] = core.files.getVirtualFile(fileName); if (virtualFile) { - const maps = core.virtualFiles.getMaps(virtualFile); + const maps = core.files.getMaps(virtualFile); for (const [source, [_, map]] of maps) { - const start = map.toSourceOffset(declaration.getStart()); - const end = map.toSourceOffset(declaration.getEnd()); + const start = map.getSourceOffset(declaration.getStart()); + const end = map.getSourceOffset(declaration.getEnd()); if (start && end) { return { file: source, diff --git a/packages/component-meta/src/index.ts b/packages/component-meta/src/index.ts index 77922556bd..c3820ebeb5 100644 --- a/packages/component-meta/src/index.ts +++ b/packages/component-meta/src/index.ts @@ -10,7 +10,7 @@ export function createComponentMetaCheckerByJsonConfig( checkerOptions: MetaCheckerOptions = {}, ) { return createCheckerByJsonConfigBase( - ts as any, + ts, rootPath, json, checkerOptions, @@ -22,7 +22,7 @@ export function createComponentMetaChecker( checkerOptions: MetaCheckerOptions = {}, ) { return createCheckerBase( - ts as any, + ts, tsconfig, checkerOptions, ); diff --git a/packages/language-core/package.json b/packages/language-core/package.json index 70ac7e1cb9..e371fc8971 100644 --- a/packages/language-core/package.json +++ b/packages/language-core/package.json @@ -13,13 +13,11 @@ "directory": "packages/language-core" }, "dependencies": { - "@volar/language-core": "~1.11.1", - "@volar/source-map": "~1.11.1", + "@volar/language-core": "2.0.0-alpha.12", "@vue/compiler-dom": "^3.3.0", "@vue/shared": "^3.3.0", "computeds": "^0.0.1", "minimatch": "^9.0.3", - "muggle-string": "^0.3.1", "path-browserify": "^1.0.1", "vue-template-compiler": "^2.7.14" }, diff --git a/packages/language-core/schemas/vue-tsconfig.deprecated.schema.json b/packages/language-core/schemas/vue-tsconfig.deprecated.schema.json index 2a4b89da79..94224badac 100644 --- a/packages/language-core/schemas/vue-tsconfig.deprecated.schema.json +++ b/packages/language-core/schemas/vue-tsconfig.deprecated.schema.json @@ -57,6 +57,12 @@ "experimentalRfc436": { "deprecated": true }, + "hooks": { + "deprecated": true, + "type": "array", + "description": "Deprecated since v2.0.0.", + "markdownDescription": "https://github.com/vuejs/language-tools/pull/2217" + }, "narrowingTypesInInlineHandlers": { "deprecated": true }, diff --git a/packages/language-core/schemas/vue-tsconfig.schema.json b/packages/language-core/schemas/vue-tsconfig.schema.json index 2f40e2fcf3..07944fa142 100644 --- a/packages/language-core/schemas/vue-tsconfig.schema.json +++ b/packages/language-core/schemas/vue-tsconfig.schema.json @@ -61,10 +61,6 @@ "default": [ ], "markdownDescription": "Plugins to be used in the SFC compiler." }, - "hooks": { - "type": "array", - "markdownDescription": "https://github.com/vuejs/language-tools/pull/2217" - }, "optionsWrapper": { "type": "array", "default": [ diff --git a/packages/language-core/src/generators/script.ts b/packages/language-core/src/generators/script.ts index 72776cec2d..d5f1a21b21 100644 --- a/packages/language-core/src/generators/script.ts +++ b/packages/language-core/src/generators/script.ts @@ -1,18 +1,22 @@ -import { FileRangeCapabilities, MirrorBehaviorCapabilities } from '@volar/language-core'; -import * as SourceMaps from '@volar/source-map'; -import { Segment, getLength } from '@volar/source-map'; -import * as muggle from 'muggle-string'; +import { Mapping } from '@volar/language-core'; import * as path from 'path-browserify'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import type * as templateGen from '../generators/template'; import type { ScriptRanges } from '../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import type { TextRange, VueCompilerOptions } from '../types'; +import type { Code, CodeAndStack, SfcBlock, VueCompilerOptions } from '../types'; import { Sfc } from '../types'; import { getSlotsPropertyName, hyphenateTag } from '../utils/shared'; -import { walkInterpolationFragment } from '../utils/transform'; +import { eachInterpolationSegment } from '../utils/transform'; +import { disableAllFeatures, enableAllFeatures, withStack } from './utils'; + +interface HelperType { + name: string; + usage?: boolean; + generated?: boolean; + code: string; +} -export function generate( +export function* generate( ts: typeof import('typescript/lib/tsserverlibrary'), fileName: string, script: Sfc['script'], @@ -21,14 +25,20 @@ export function generate( lang: string, scriptRanges: ScriptRanges | undefined, scriptSetupRanges: ScriptSetupRanges | undefined, - htmlGen: ReturnType | undefined, + templateCodegen: { + tsCodes: Code[]; + tsCodegenStacks: string[]; + tagNames: Set; + accessedGlobalVariables: Set; + hasSlot: boolean; + } | undefined, compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, + globalTypesHolder: string | undefined, + getGeneratedLength: () => number, + linkedCodeMappings: Mapping[] = [], codegenStack: boolean, -) { - - const [codes, codeStacks] = codegenStack ? muggle.track([] as Segment[]) : [[], []]; - const mirrorBehaviorMappings: SourceMaps.Mapping<[MirrorBehaviorCapabilities, MirrorBehaviorCapabilities]>[] = []; +): Generator { //#region monkey fix: https://github.com/vuejs/language-tools/pull/2113 if (!script && !scriptSetup) { @@ -43,7 +53,7 @@ export function generate( generic: undefined, genericOffset: 0, attrs: {}, - ast: ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS), + ast: ts.createSourceFile('', '', 99 satisfies ts.ScriptTarget.Latest, false, ts.ScriptKind.TS), }; scriptSetupRanges = { bindings: [], @@ -63,106 +73,281 @@ export function generate( ...scriptSetupRanges?.bindings.map(range => scriptSetup!.content.substring(range.start, range.end)) ?? [], ]); const bypassDefineComponent = lang === 'js' || lang === 'jsx'; - const usedHelperTypes = { - DefinePropsToOptions: false, - MergePropDefaults: false, - WithTemplateSlots: false, - PropsChildren: false, + const _ = codegenStack ? withStack : (code: Code): CodeAndStack => [code, '']; + const helperTypes = { + OmitKeepDiscriminatedUnion: { + get name() { + this.usage = true; + return `__VLS_OmitKeepDiscriminatedUnion`; + }, + get code() { + return `type __VLS_OmitKeepDiscriminatedUnion = T extends any + ? Pick> + : never;`; + }, + } satisfies HelperType as HelperType, + WithDefaults: { + get name() { + this.usage = true; + return `__VLS_WithDefaults`; + }, + get code(): string { + return `type __VLS_WithDefaults = { + [K in keyof Pick]: K extends keyof D + ? ${helperTypes.Prettify.name} + : P[K] + };`; + }, + } satisfies HelperType as HelperType, + Prettify: { + get name() { + this.usage = true; + return `__VLS_Prettify`; + }, + get code() { + return `type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};`; + }, + } satisfies HelperType as HelperType, + WithTemplateSlots: { + get name() { + this.usage = true; + return `__VLS_WithTemplateSlots`; + }, + get code(): string { + return `type __VLS_WithTemplateSlots = T & { + new(): { + ${getSlotsPropertyName(vueCompilerOptions.target)}: S; + ${vueCompilerOptions.jsxSlots ? `$props: ${helperTypes.PropsChildren.name};` : ''} + } + };`; + }, + } satisfies HelperType as HelperType, + PropsChildren: { + get name() { + this.usage = true; + return `__VLS_PropsChildren`; + }, + get code() { + return `type __VLS_PropsChildren = { + [K in keyof ( + boolean extends ( + // @ts-ignore + JSX.ElementChildrenAttribute extends never + ? true + : false + ) + ? never + // @ts-ignore + : JSX.ElementChildrenAttribute + )]?: S; + };`; + }, + } satisfies HelperType as HelperType, + TypePropsToOption: { + get name() { + this.usage = true; + return `__VLS_TypePropsToOption`; + }, + get code() { + return compilerOptions.exactOptionalPropertyTypes ? + `type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick + ? { type: import('${vueCompilerOptions.lib}').PropType } + : { type: import('${vueCompilerOptions.lib}').PropType, required: true } + };` : + `type __VLS_NonUndefinedable = T extends undefined ? never : T; + type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick + ? { type: import('${vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable> } + : { type: import('${vueCompilerOptions.lib}').PropType, required: true } + };`; + }, + } satisfies HelperType as HelperType, }; - codes.push(`/* __placeholder__ */\n`); - let generatedTemplate = false; - - generateSrc(); - generateScriptSetupImports(); - generateScriptContentBeforeExportDefault(); - generateScriptSetupAndTemplate(); - generateHelperTypes(); - generateScriptContentAfterExportDefault(); + let scriptSetupGeneratedOffset: number | undefined; + + yield _(`/* __placeholder__ */\n`); + yield* generateSrc(); + yield* generateScriptSetupImports(); + yield* generateScriptContentBeforeExportDefault(); + yield* generateScriptSetupAndTemplate(); + yield* generateScriptContentAfterExportDefault(); + if (globalTypesHolder === fileName) { + yield* generateGlobalHelperTypes(); + } + yield* generateLocalHelperTypes(); + yield _(`\ntype __VLS_IntrinsicElementsCompletion = __VLS_IntrinsicElements;\n`); if (!generatedTemplate) { - generateTemplate(false); + yield* generateTemplate(false); } if (scriptSetup) { - // for code action edits - codes.push([ - '', - 'scriptSetup', - scriptSetup.content.length, - {}, - ]); + yield _(['', 'scriptSetup', scriptSetup.content.length, disableAllFeatures({ verification: true })]); } - return { - codes, - codeStacks, - mirrorBehaviorMappings, - }; - - function generateHelperTypes() { - if (usedHelperTypes.DefinePropsToOptions) { - if (compilerOptions.exactOptionalPropertyTypes) { - codes.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueCompilerOptions.lib}').PropType } : { type: import('${vueCompilerOptions.lib}').PropType, required: true } };\n`); + function* generateGlobalHelperTypes(): Generator { + const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${vueCompilerOptions.strictTemplates ? '' : ' & Record'}`; + yield _(` +; declare global { +// @ts-ignore +type __VLS_IntrinsicElements = __VLS_PickNotAny>>; +// @ts-ignore +type __VLS_Element = __VLS_PickNotAny; +// @ts-ignore +type __VLS_GlobalComponents = ${[ + `__VLS_PickNotAny`, + `__VLS_PickNotAny`, + `__VLS_PickNotAny`, + `Pick` + ].join(' & ')}; +type __VLS_IsAny = 0 extends 1 & T ? true : false; +type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; + +const __VLS_intrinsicElements: __VLS_IntrinsicElements; + +// v-for +function __VLS_getVForSourceType(source: number): [number, number, number][]; +function __VLS_getVForSourceType(source: string): [string, number, number][]; +function __VLS_getVForSourceType(source: T): [ + T[number], // item + number, // key + number, // index +][]; +function __VLS_getVForSourceType }>(source: T): [ + T extends { [Symbol.iterator](): Iterator } ? T1 : never, // item + number, // key + undefined, // index +][]; +function __VLS_getVForSourceType(source: T): [ + T[keyof T], // item + keyof T, // key + number, // index +][]; + +// @ts-ignore +function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>; +// @ts-ignore +function __VLS_getSlotParam(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>[0]; +function __VLS_directiveFunction(dir: T): + T extends import('${vueCompilerOptions.lib}').ObjectDirective | import('${vueCompilerOptions.lib}').FunctionDirective ? (value: V) => void + : T; +function __VLS_withScope(ctx: T, scope: K): ctx is T & K; +function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; + +type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; +type __VLS_WithComponent = + N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : + N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : + N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : + N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } : + N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } : + N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } : + ${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: unknown }'} + +type __VLS_FillingEventArg_ParametersLength any> = __VLS_IsAny> extends true ? -1 : Parameters['length']; +type __VLS_FillingEventArg = E extends (...args: any) => any ? __VLS_FillingEventArg_ParametersLength extends 0 ? ($event?: undefined) => ReturnType : E : E; +function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): + T extends new (...args: any) => any + ? (props: ${fnPropsType}, ctx?: any) => JSX.Element & { __ctx?: { + attrs?: any, + slots?: K extends { ${getSlotsPropertyName(vueCompilerOptions.target)}: infer Slots } ? Slots : any, + emit?: K extends { $emit: infer Emit } ? Emit : any + } & { props?: ${fnPropsType}; expose?(exposed: K): void; } } + : T extends () => any ? (props: {}, ctx?: any) => ReturnType + : T extends (...args: any) => any ? T + : (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; +function __VLS_elementAsFunctionalComponent(t: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; +function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; +function __VLS_pickEvent(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg< + __VLS_PickNotAny< + __VLS_AsFunctionOrAny, + __VLS_AsFunctionOrAny + > +> | undefined; +function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): __VLS_PickNotAny< + '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any + , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any +>; +type __VLS_FunctionalComponentProps = + '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never + : T extends (props: infer P, ...args: any) => any ? P : + {}; +type __VLS_AsFunctionOrAny = unknown extends F ? any : ((...args: any) => any) extends F ? F : any; + +function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; + +/** + * emit + */ +// fix https://github.com/vuejs/language-tools/issues/926 +type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; +type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R + ? U extends T + ? never + : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) + : never; +type __VLS_OverloadUnion = Exclude< + __VLS_OverloadUnionInner<(() => never) & T>, + T extends () => never ? never : () => never +>; +type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F + ? F extends (event: infer E, ...args: infer A) => any + ? { [K in E & string]: (...args: A) => void; } + : never + : never; +type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< + __VLS_UnionToIntersection< + __VLS_ConstructorOverloads & { + [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never + } + > +>; +type __VLS_PrettifyGlobal = { [K in keyof T]: T[K]; } & {}; +} +`); + } + function* generateLocalHelperTypes(): Generator { + let shouldCheck = true; + while (shouldCheck) { + shouldCheck = false; + for (const helperType of Object.values(helperTypes)) { + if (helperType.usage && !helperType.generated) { + shouldCheck = true; + helperType.generated = true; + yield _('\n' + helperType.code + '\n'); + } } - else { - codes.push(`type __VLS_NonUndefinedable = T extends undefined ? never : T;\n`); - codes.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable> } : { type: import('${vueCompilerOptions.lib}').PropType, required: true } };\n`); - } - } - if (usedHelperTypes.MergePropDefaults) { - codes.push(`type __VLS_WithDefaults = { - // use 'keyof Pick' instead of 'keyof P' to keep props jsdoc - [K in keyof Pick]: K extends keyof D ? __VLS_Prettify : P[K] - };\n`); - codes.push(`type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};\n`); - } - if (usedHelperTypes.WithTemplateSlots) { - codes.push( - `type __VLS_WithTemplateSlots = T & { new(): {\n`, - `${getSlotsPropertyName(vueCompilerOptions.target)}: S;\n`, - ); - if (vueCompilerOptions.jsxSlots) { - usedHelperTypes.PropsChildren = true; - codes.push(`$props: __VLS_PropsChildren;\n`); - } - codes.push( - `} };\n`, - ); - } - if (usedHelperTypes.PropsChildren) { - codes.push(`type __VLS_PropsChildren = { [K in keyof (boolean extends (JSX.ElementChildrenAttribute extends never ? true : false) ? never : JSX.ElementChildrenAttribute)]?: S; };\n`); } } - function generateSrc() { + function* generateSrc(): Generator { if (!script?.src) return; let src = script.src; - if (src.endsWith('.d.ts')) src = src.substring(0, src.length - '.d.ts'.length); - else if (src.endsWith('.ts')) src = src.substring(0, src.length - '.ts'.length); - else if (src.endsWith('.tsx')) src = src.substring(0, src.length - '.tsx'.length) + '.jsx'; + if (src.endsWith('.d.ts')) + src = src.substring(0, src.length - '.d.ts'.length); + else if (src.endsWith('.ts')) + src = src.substring(0, src.length - '.ts'.length); + else if (src.endsWith('.tsx')) + src = src.substring(0, src.length - '.tsx'.length) + '.jsx'; - if (!src.endsWith('.js') && !src.endsWith('.jsx')) src = src + '.js'; + if (!src.endsWith('.js') && !src.endsWith('.jsx')) + src = src + '.js'; - codes.push(`export * from `); - codes.push([ + yield _(`export * from `); + yield _([ `'${src}'`, 'script', - [script.srcOffset - 1, script.srcOffset + script.src.length + 1], - { - ...FileRangeCapabilities.full, - rename: src === script.src ? true : { - normalize: undefined, - apply(newName) { - if ( - newName.endsWith('.jsx') - || newName.endsWith('.js') - ) { + script.srcOffset - 1, + enableAllFeatures({ + navigation: src === script.src ? true : { + shouldRename: () => false, + resolveRenameEditText(newName) { + if (newName.endsWith('.jsx') || newName.endsWith('.js')) { newName = newName.split('.').slice(0, -1).join('.'); } if (script?.src?.endsWith('.d.ts')) { @@ -177,214 +362,196 @@ export function generate( return newName; }, }, - }, + }), ]); - codes.push(`;\n`); - codes.push(`export { default } from '${src}';\n`); + yield _(`;\n`); + yield _(`export { default } from '${src}';\n`); } - function generateScriptContentBeforeExportDefault() { + function* generateScriptContentBeforeExportDefault(): Generator { if (!script) return; - if (!!scriptSetup && scriptRanges?.exportDefault) { - addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); - } - else { - let isExportRawObject = false; - if (scriptRanges?.exportDefault) { - isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); - } - if (isExportRawObject && vueCompilerOptions.optionsWrapper.length === 2 && scriptRanges?.exportDefault) { - addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); - codes.push(vueCompilerOptions.optionsWrapper[0]); - { - codes.push(['', 'script', scriptRanges.exportDefault.expression.start, { - __hint: { - setting: 'vue.inlayHints.optionsWrapper', - label: vueCompilerOptions.optionsWrapper[0], - tooltip: [ - 'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.', - 'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.', - ].join('\n\n'), - } - } as any]); - addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end); - codes.push(['', 'script', scriptRanges.exportDefault.expression.end, { - __hint: { - setting: 'vue.inlayHints.optionsWrapper', - label: vueCompilerOptions.optionsWrapper[1], - tooltip: '', - } - } as any]); - } - codes.push(vueCompilerOptions.optionsWrapper[1]); - addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length); + if (!!scriptSetup && scriptRanges?.exportDefault) + return yield _(generateSourceCode(script, 0, scriptRanges.exportDefault.expression.start)); + + let isExportRawObject = false; + if (scriptRanges?.exportDefault) + isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); + + if (!isExportRawObject || !vueCompilerOptions.optionsWrapper.length || !scriptRanges?.exportDefault) + return yield _(generateSourceCode(script, 0, script.content.length)); + + yield _(generateSourceCode(script, 0, scriptRanges.exportDefault.expression.start)); + yield _(vueCompilerOptions.optionsWrapper[0]); + yield _(['', 'script', scriptRanges.exportDefault.expression.start, disableAllFeatures({ + __hint: { + setting: 'vue.inlayHints.optionsWrapper', + label: vueCompilerOptions.optionsWrapper[0], + tooltip: [ + 'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.', + 'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.', + ].join('\n\n'), } - else { - addVirtualCode('script', 0, script.content.length); + })]); + yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end)); + yield _(['', 'script', scriptRanges.exportDefault.expression.end, disableAllFeatures({ + __hint: { + setting: 'vue.inlayHints.optionsWrapper', + label: vueCompilerOptions.optionsWrapper[1], + tooltip: '', } - } + })]); + yield _(vueCompilerOptions.optionsWrapper[1]); + yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.end, script.content.length)); } - function generateScriptContentAfterExportDefault() { + function* generateScriptContentAfterExportDefault(): Generator { if (!script) return; - if (!!scriptSetup && scriptRanges?.exportDefault) { - addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length); - } + if (!!scriptSetup && scriptRanges?.exportDefault) + yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.end, script.content.length)); } - function generateScriptSetupImports() { - - if (!scriptSetup) + function* generateScriptSetupImports(): Generator { + if (!scriptSetup || !scriptSetupRanges) return; - if (!scriptSetupRanges) - return; - - codes.push([ + yield _([ scriptSetup.content.substring(0, Math.max(scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.leadingCommentEndOffset)) + '\n', 'scriptSetup', 0, - FileRangeCapabilities.full, + enableAllFeatures({}), ]); } - function generateScriptSetupAndTemplate() { - - if (!scriptSetup || !scriptSetupRanges) { + function* generateScriptSetupAndTemplate(): Generator { + if (!scriptSetup || !scriptSetupRanges) return; - } - const definePropMirrors: Record = {}; - let scriptSetupGeneratedOffset: number | undefined; + const definePropMirrors = new Map(); if (scriptSetup.generic) { if (!scriptRanges?.exportDefault) { - codes.push('export default '); + yield _(`export default `); } - codes.push(`(<`); - codes.push([ + yield _(`(<`); + yield _([ scriptSetup.generic, scriptSetup.name, scriptSetup.genericOffset, - FileRangeCapabilities.full, + enableAllFeatures({}), ]); - if (!scriptSetup.generic.endsWith(',')) { - codes.push(`,`); + if (!scriptSetup.generic.endsWith(`,`)) { + yield _(`,`); } - codes.push(`>`); - codes.push('(\n'); - codes.push(`__VLS_props: Awaited['props'],\n`); - codes.push(`__VLS_ctx?: __VLS_Prettify, 'attrs' | 'emit' | 'slots'>>,\n`); // use __VLS_Prettify for less dts code - codes.push(`__VLS_expose?: NonNullable>['expose'],\n`); - codes.push('__VLS_setup = (async () => {\n'); - scriptSetupGeneratedOffset = generateSetupFunction(true, 'none', definePropMirrors); + yield _(`>`); + yield _(`(\n` + + ` __VLS_props: Awaited['props'],\n` + + ` __VLS_ctx?: ${helperTypes.Prettify.name}, 'attrs' | 'emit' | 'slots'>>,\n` // use __VLS_Prettify for less dts code + + ` __VLS_expose?: NonNullable>['expose'],\n` + + ` __VLS_setup = (async () => {\n`); + + yield* generateSetupFunction(true, 'none', definePropMirrors); //#region props - codes.push(`const __VLS_fnComponent = `); - codes.push(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`); + yield _(`const __VLS_fnComponent = ` + + `(await import('${vueCompilerOptions.lib}')).defineComponent({\n`); if (scriptSetupRanges.props.define?.arg) { - codes.push(`props: `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end); - codes.push(`,\n`); - + yield _(` props: `); + yield _(generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end)); + yield _(`,\n`); } if (scriptSetupRanges.emits.define) { - codes.push( - `emits: ({} as __VLS_NormalizeEmits),\n`, - ); + yield _(` emits: ({} as __VLS_NormalizeEmits),\n`); } - codes.push(`});\n`); + yield _(`});\n`); + if (scriptSetupRanges.defineProp.length) { - codes.push(`const __VLS_defaults = {\n`); + yield _(`const __VLS_defaults = {\n`); for (const defineProp of scriptSetupRanges.defineProp) { if (defineProp.defaultValue) { if (defineProp.name) { - codes.push(scriptSetup.content.substring(defineProp.name.start, defineProp.name.end)); + yield _(scriptSetup.content.substring(defineProp.name.start, defineProp.name.end)); } else { - codes.push('modelValue'); + yield _('modelValue'); } - codes.push(`: `); - codes.push(scriptSetup.content.substring(defineProp.defaultValue.start, defineProp.defaultValue.end)); - codes.push(`,\n`); + yield _(`: `); + yield _(scriptSetup.content.substring(defineProp.defaultValue.start, defineProp.defaultValue.end)); + yield _(`,\n`); } } - codes.push(`};\n`); + yield _(`};\n`); } - codes.push(`let __VLS_fnPropsTypeOnly!: {}`); // TODO: reuse __VLS_fnPropsTypeOnly even without generic, and remove __VLS_propsOption_defineProp + + yield _(`let __VLS_fnPropsTypeOnly!: {}`); // TODO: reuse __VLS_fnPropsTypeOnly even without generic, and remove __VLS_propsOption_defineProp if (scriptSetupRanges.props.define?.typeArg) { - codes.push(` & `); - addVirtualCode('scriptSetup', scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end); + yield _(` & `); + yield _(generateSourceCode(scriptSetup, scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end)); } if (scriptSetupRanges.defineProp.length) { - codes.push(` & {\n`); + yield _(` & {\n`); for (const defineProp of scriptSetupRanges.defineProp) { let propName = 'modelValue'; if (defineProp.name) { propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end); - const propMirrorStart = muggle.getLength(codes); - definePropMirrors[propName] = [propMirrorStart, propMirrorStart + propName.length]; + definePropMirrors.set(propName, getGeneratedLength()); } - codes.push(`${propName}${defineProp.required ? '' : '?'}: `); + yield _(`${propName}${defineProp.required ? '' : '?'}: `); if (defineProp.type) { - codes.push(scriptSetup.content.substring(defineProp.type.start, defineProp.type.end)); + yield _(scriptSetup.content.substring(defineProp.type.start, defineProp.type.end)); } else if (defineProp.defaultValue) { - codes.push(`typeof __VLS_defaults['`); - codes.push(propName); - codes.push(`']`); + yield _(`typeof __VLS_defaults['`); + yield _(propName); + yield _(`']`); } else { - codes.push(`any`); + yield _(`any`); } - codes.push(',\n'); + yield _(',\n'); } - codes.push(`}`); + yield _(`}`); } - codes.push(`;\n`); - codes.push(`let __VLS_fnPropsDefineComponent!: InstanceType['$props']`); - codes.push(`;\n`); - codes.push(`let __VLS_fnPropsSlots!: `); + yield _(`;\n`); + + yield _(`let __VLS_fnPropsDefineComponent!: InstanceType['$props'];\n`); + yield _(`let __VLS_fnPropsSlots!: `); if (scriptSetupRanges.slots.define && vueCompilerOptions.jsxSlots) { - usedHelperTypes.PropsChildren = true; - codes.push(`__VLS_PropsChildren`); + yield _(`${helperTypes.PropsChildren.name}`); } else { - codes.push(`{}`); - } - codes.push(`;\n`); - codes.push( - `let __VLS_defaultProps!: `, - `import('${vueCompilerOptions.lib}').VNodeProps`, - `& import('${vueCompilerOptions.lib}').AllowedComponentProps`, - `& import('${vueCompilerOptions.lib}').ComponentCustomProps`, - `;\n`, - ); + yield _(`{}`); + } + yield _(`;\n`); + + yield _(`let __VLS_defaultProps!:\n` + + ` import('${vueCompilerOptions.lib}').VNodeProps\n` + + ` & import('${vueCompilerOptions.lib}').AllowedComponentProps\n` + + ` & import('${vueCompilerOptions.lib}').ComponentCustomProps;\n`); //#endregion - codes.push('return {} as {\n'); - codes.push(`props: __VLS_Prettify<__VLS_OmitKeepDiscriminatedUnion> & typeof __VLS_fnPropsSlots & typeof __VLS_defaultProps,\n`); - codes.push(`expose(exposed: import('${vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,\n`); - codes.push('attrs: any,\n'); - codes.push('slots: ReturnType,\n'); - codes.push(`emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`); - codes.push('};\n'); - codes.push('})(),\n'); - codes.push(`) => ({} as import('${vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`); + yield _(` return {} as {\n` + + ` props: ${helperTypes.Prettify.name}<${helperTypes.OmitKeepDiscriminatedUnion.name}> & typeof __VLS_fnPropsSlots & typeof __VLS_defaultProps,\n` + + ` expose(exposed: import('${vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,\n` + + ` attrs: any,\n` + + ` slots: ReturnType,\n` + + ` emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n` + + ` };\n`); + yield _(` })(),\n`); // __VLS_setup = (async () => { + yield _(`) => ({} as import('${vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`); } else if (!script) { // no script block, generate script setup code at root - scriptSetupGeneratedOffset = generateSetupFunction(false, 'export', definePropMirrors); + yield* generateSetupFunction(false, 'export', definePropMirrors); } else { if (!scriptRanges?.exportDefault) { - codes.push('export default '); + yield _(`export default `); } - codes.push('await (async () => {\n'); - scriptSetupGeneratedOffset = generateSetupFunction(false, 'return', definePropMirrors); - codes.push(`})()`); + yield _(`await (async () => {\n`); + yield* generateSetupFunction(false, 'return', definePropMirrors); + yield _(`})()`); } if (scriptSetupGeneratedOffset !== undefined) { @@ -393,142 +560,150 @@ export function generate( continue; } const propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end); - const propMirror = definePropMirrors[propName]; - if (propMirror) { - mirrorBehaviorMappings.push({ - sourceRange: [defineProp.name.start + scriptSetupGeneratedOffset, defineProp.name.end + scriptSetupGeneratedOffset], - generatedRange: propMirror, - data: [ - MirrorBehaviorCapabilities.full, - MirrorBehaviorCapabilities.full, - ], + const propMirror = definePropMirrors.get(propName); + if (propMirror !== undefined) { + linkedCodeMappings.push({ + sourceOffsets: [defineProp.name.start + scriptSetupGeneratedOffset], + generatedOffsets: [propMirror], + lengths: [defineProp.name.end - defineProp.name.start], + data: undefined, }); } } } } - function generateSetupFunction(functional: boolean, mode: 'return' | 'export' | 'none', definePropMirrors: Record) { - - if (!scriptSetupRanges || !scriptSetup) { + function* generateSetupFunction(functional: boolean, mode: 'return' | 'export' | 'none', definePropMirrors: Map): Generator { + if (!scriptSetupRanges || !scriptSetup) return; - } const definePropProposalA = scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition'; const definePropProposalB = scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition'; if (vueCompilerOptions.target >= 3.3) { - codes.push('const { '); + yield _(`const { `); for (const macro of Object.keys(vueCompilerOptions.macros)) { if (!bindingNames.has(macro)) { - codes.push(macro, ', '); + yield _(macro + `, `); } } - codes.push(`} = await import('${vueCompilerOptions.lib}');\n`); + yield _(`} = await import('${vueCompilerOptions.lib}');\n`); } if (definePropProposalA) { - codes.push(` -declare function defineProp(name: string, options: { required: true } & Record): import('${vueCompilerOptions.lib}').ComputedRef; -declare function defineProp(name: string, options: { default: any } & Record): import('${vueCompilerOptions.lib}').ComputedRef; -declare function defineProp(name?: string, options?: any): import('${vueCompilerOptions.lib}').ComputedRef; -`.trim() + '\n'); + yield _(`declare function defineProp(name: string, options: { required: true } & Record): import('${vueCompilerOptions.lib}').ComputedRef;\n`); + yield _(`declare function defineProp(name: string, options: { default: any } & Record): import('${vueCompilerOptions.lib}').ComputedRef;\n`); + yield _(`declare function defineProp(name?: string, options?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`); } if (definePropProposalB) { - codes.push(` -declare function defineProp(value: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef; -declare function defineProp(value: T | (() => T) | undefined, required: true, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef; -declare function defineProp(value?: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef; -`.trim() + '\n'); + yield _(`declare function defineProp(value: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`); + yield _(`declare function defineProp(value: T | (() => T) | undefined, required: true, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`); + yield _(`declare function defineProp(value?: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`); } - const scriptSetupGeneratedOffset = muggle.getLength(codes) - scriptSetupRanges.importSectionEndOffset; + scriptSetupGeneratedOffset = getGeneratedLength() - scriptSetupRanges.importSectionEndOffset; - let setupCodeModifies: [() => void, number, number][] = []; + let setupCodeModifies: [Code[], number, number][] = []; if (scriptSetupRanges.props.define && !scriptSetupRanges.props.name) { const range = scriptSetupRanges.props.withDefaults ?? scriptSetupRanges.props.define; const statement = scriptSetupRanges.props.define.statement; if (statement.start === range.start && statement.end === range.end) { - setupCodeModifies.push([() => codes.push(`const __VLS_props = `), range.start, range.start]); + setupCodeModifies.push([[`const __VLS_props = `], range.start, range.start]); } else { - setupCodeModifies.push([() => { - codes.push(`const __VLS_props = `); - addVirtualCode('scriptSetup', range.start, range.end); - codes.push(`;\n`); - addVirtualCode('scriptSetup', statement.start, range.start); - codes.push(`__VLS_props`); - }, statement.start, range.end]); + setupCodeModifies.push([[ + `const __VLS_props = `, + generateSourceCode(scriptSetup, range.start, range.end), + `;\n`, + generateSourceCode(scriptSetup, statement.start, range.start), + `__VLS_props`, + ], statement.start, range.end]); } } if (scriptSetupRanges.slots.define && !scriptSetupRanges.slots.name) { - setupCodeModifies.push([() => codes.push(`const __VLS_slots = `), scriptSetupRanges.slots.define.start, scriptSetupRanges.slots.define.start]); + setupCodeModifies.push([[`const __VLS_slots = `], scriptSetupRanges.slots.define.start, scriptSetupRanges.slots.define.start]); } if (scriptSetupRanges.emits.define && !scriptSetupRanges.emits.name) { - setupCodeModifies.push([() => codes.push(`const __VLS_emit = `), scriptSetupRanges.emits.define.start, scriptSetupRanges.emits.define.start]); + setupCodeModifies.push([[`const __VLS_emit = `], scriptSetupRanges.emits.define.start, scriptSetupRanges.emits.define.start]); } if (scriptSetupRanges.expose.define) { - setupCodeModifies.push([() => { - if (scriptSetupRanges?.expose.define?.typeArg) { - codes.push(`let __VLS_exposed!: `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.expose.define.typeArg.start, scriptSetupRanges.expose.define.typeArg.end); - codes.push(`;\n`); - } - else if (scriptSetupRanges?.expose.define?.arg) { - codes.push(`const __VLS_exposed = `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.expose.define.arg.start, scriptSetupRanges.expose.define.arg.end); - codes.push(`;\n`); - } - else { - codes.push(`const __VLS_exposed = {};\n`); - } - }, scriptSetupRanges.expose.define.start, scriptSetupRanges.expose.define.start]); + if (scriptSetupRanges.expose.define?.typeArg) { + setupCodeModifies.push([ + [ + `let __VLS_exposed!: `, + generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.expose.define.typeArg.start, scriptSetupRanges.expose.define.typeArg.end), + `;\n`, + ], + scriptSetupRanges.expose.define.start, + scriptSetupRanges.expose.define.start, + ]); + } + else if (scriptSetupRanges.expose.define?.arg) { + setupCodeModifies.push([ + [ + `const __VLS_exposed = `, + generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.expose.define.arg.start, scriptSetupRanges.expose.define.arg.end), + `;\n`, + ], + scriptSetupRanges.expose.define.start, + scriptSetupRanges.expose.define.start, + ]); + } + else { + setupCodeModifies.push([ + [`const __VLS_exposed = {};\n`], + scriptSetupRanges.expose.define.start, + scriptSetupRanges.expose.define.start, + ]); + } } setupCodeModifies = setupCodeModifies.sort((a, b) => a[1] - b[1]); if (setupCodeModifies.length) { - addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, setupCodeModifies[0][1]); + yield _(generateSourceCode(scriptSetup, scriptSetupRanges.importSectionEndOffset, setupCodeModifies[0][1])); while (setupCodeModifies.length) { - const [generate, _, end] = setupCodeModifies.shift()!; - generate(); + const [codes, _start, end] = setupCodeModifies.shift()!; + for (const code of codes) { + yield _(code); + } if (setupCodeModifies.length) { const nextStart = setupCodeModifies[0][1]; - addVirtualCode('scriptSetup', end, nextStart); + yield _(generateSourceCode(scriptSetup, end, nextStart)); } else { - addVirtualCode('scriptSetup', end); + yield _(generateSourceCode(scriptSetup, end)); } } } else { - addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset); + yield _(generateSourceCode(scriptSetup, scriptSetupRanges.importSectionEndOffset)); } if (scriptSetupRanges.props.define?.typeArg && scriptSetupRanges.props.withDefaults?.arg) { // fix https://github.com/vuejs/language-tools/issues/1187 - codes.push(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.props.withDefaults.arg.start, scriptSetupRanges.props.withDefaults.arg.end); - codes.push(`);\n`); + yield _(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); + yield _(generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.props.withDefaults.arg.start, scriptSetupRanges.props.withDefaults.arg.end)); + yield _(`);\n`); } if (!functional && scriptSetupRanges.defineProp.length) { - codes.push(`let __VLS_propsOption_defineProp!: {\n`); + yield _(`let __VLS_propsOption_defineProp!: {\n`); for (const defineProp of scriptSetupRanges.defineProp) { let propName = 'modelValue'; if (defineProp.name && defineProp.nameIsString) { // renaming support - addExtraReferenceVirtualCode('scriptSetup', defineProp.name.start, defineProp.name.end); + yield _(generateSourceCodeForExtraReference(scriptSetup, defineProp.name.start, defineProp.name.end)); } else if (defineProp.name) { propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end); - const start = muggle.getLength(codes); - definePropMirrors[propName] = [start, start + propName.length]; - codes.push(propName); + const start = getGeneratedLength(); + definePropMirrors.set(propName, start); + yield _(propName); } else { - codes.push(propName); + yield _(propName); } - codes.push(`: `); + yield _(`: `); let type = 'any'; if (!defineProp.nameIsString) { @@ -539,182 +714,169 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?: } if (defineProp.required) { - codes.push(`{ required: true, type: import('${vueCompilerOptions.lib}').PropType<${type}> },\n`); + yield _(`{ required: true, type: import('${vueCompilerOptions.lib}').PropType<${type}> },\n`); } else { - codes.push(`import('${vueCompilerOptions.lib}').PropType<${type}>,\n`); + yield _(`import('${vueCompilerOptions.lib}').PropType<${type}>,\n`); } } - codes.push(`};\n`); + yield _(`};\n`); } - generateTemplate(functional); + yield* generateTemplate(functional); if (mode === 'return' || mode === 'export') { - if (!vueCompilerOptions.skipTemplateCodegen && (htmlGen?.hasSlot || scriptSetupRanges?.slots.define)) { - usedHelperTypes.WithTemplateSlots = true; - codes.push(`const __VLS_component = `); - generateComponent(functional); - codes.push(`;\n`); - codes.push(mode === 'return' ? 'return ' : 'export default '); - codes.push(`{} as __VLS_WithTemplateSlots>;\n`); + if (!vueCompilerOptions.skipTemplateCodegen && (templateCodegen?.hasSlot || scriptSetupRanges?.slots.define)) { + yield _(`const __VLS_component = `); + yield* generateComponent(functional); + yield _(`;\n`); + yield _(mode === 'return' ? 'return ' : 'export default '); + yield _(`{} as ${helperTypes.WithTemplateSlots.name}>;\n`); } else { - codes.push(mode === 'return' ? 'return ' : 'export default '); - generateComponent(functional); - codes.push(`;\n`); + yield _(mode === 'return' ? 'return ' : 'export default '); + yield* generateComponent(functional); + yield _(`;\n`); } } - - return scriptSetupGeneratedOffset; } - function generateComponent(functional: boolean) { - + function* generateComponent(functional: boolean): Generator { if (!scriptSetupRanges) return; - if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { + if (script && scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { // use defineComponent() from user space code if it exist - addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start); - codes.push(`{\n`); + yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start)); + yield _(`{\n`); } else { - codes.push(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`); + yield _(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`); } - codes.push(`setup() {\n`); - codes.push(`return {\n`); - generateSetupReturns(); + yield _(`setup() {\n`); + yield _(`return {\n`); + yield* generateSetupReturns(); if (scriptSetupRanges.expose.define) { - codes.push(`...__VLS_exposed,\n`); + yield _(`...__VLS_exposed,\n`); } - codes.push(`};\n`); - codes.push(`},\n`); - generateComponentOptions(functional); - codes.push(`})`); + yield _(`};\n`); + yield _(`},\n`); + yield* generateComponentOptions(functional); + yield _(`})`); } - function generateComponentOptions(functional: boolean) { - if (scriptSetupRanges && !bypassDefineComponent) { + function* generateComponentOptions(functional: boolean): Generator { + if (scriptSetup && scriptSetupRanges && !bypassDefineComponent) { const ranges = scriptSetupRanges; - const propsCodegens: (() => void)[] = []; + const propsCodegens: (() => Generator)[] = []; if (ranges.props.define?.arg) { const arg = ranges.props.define.arg; - propsCodegens.push(() => { - addExtraReferenceVirtualCode('scriptSetup', arg.start, arg.end); + propsCodegens.push(function* () { + yield _(generateSourceCodeForExtraReference(scriptSetup!, arg.start, arg.end)); }); } if (ranges.props.define?.typeArg) { const typeArg = ranges.props.define.typeArg; - propsCodegens.push(() => { - - usedHelperTypes.DefinePropsToOptions = true; + propsCodegens.push(function* () { - codes.push(`{} as `); + yield _(`{} as `); if (ranges.props.withDefaults?.arg) { - usedHelperTypes.MergePropDefaults = true; - codes.push(`__VLS_WithDefaults<`); + yield _(`${helperTypes.WithDefaults.name}<`); } - codes.push(`__VLS_TypePropsToRuntimeProps<`); + yield _(`${helperTypes.TypePropsToOption.name}<`); if (functional) { - codes.push(`typeof __VLS_fnPropsTypeOnly`); + yield _(`typeof __VLS_fnPropsTypeOnly`); } else { - addExtraReferenceVirtualCode('scriptSetup', typeArg.start, typeArg.end); + yield _(generateSourceCodeForExtraReference(scriptSetup!, typeArg.start, typeArg.end)); } - codes.push(`>`); + yield _(`>`); if (ranges.props.withDefaults?.arg) { - codes.push(`, typeof __VLS_withDefaultsArg`); - codes.push(`>`); + yield _(`, typeof __VLS_withDefaultsArg`); + yield _(`>`); } }); } if (!functional && ranges.defineProp.length) { - propsCodegens.push(() => { - codes.push(`__VLS_propsOption_defineProp`); + propsCodegens.push(function* () { + yield _(`__VLS_propsOption_defineProp`); }); } if (propsCodegens.length === 1) { - codes.push(`props: `); + yield _(`props: `); for (const generate of propsCodegens) { - generate(); + yield* generate(); } - codes.push(`,\n`); + yield _(`,\n`); } else if (propsCodegens.length >= 2) { - codes.push(`props: {\n`); + yield _(`props: {\n`); for (const generate of propsCodegens) { - codes.push('...'); - generate(); - codes.push(',\n'); + yield _(`...`); + yield* generate(); + yield _(`,\n`); } - codes.push(`},\n`); + yield _(`},\n`); } if (ranges.emits.define) { - codes.push( - `emits: ({} as __VLS_NormalizeEmits),\n`, - ); + yield _(`emits: ({} as __VLS_NormalizeEmits),\n`); } } - if (scriptRanges?.exportDefault?.args) { - addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); + if (script && scriptRanges?.exportDefault?.args) { + yield _(generateSourceCode(script, scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1)); } } - function generateSetupReturns() { + function* generateSetupReturns(): Generator { if (scriptSetupRanges && bypassDefineComponent) { // fill $props if (scriptSetupRanges.props.define) { // NOTE: defineProps is inaccurate for $props - codes.push(`$props: __VLS_makeOptional(${scriptSetupRanges.props.name ?? `__VLS_props`}),\n`); - codes.push(`...${scriptSetupRanges.props.name ?? `__VLS_props`},\n`); + yield _(`$props: __VLS_makeOptional(${scriptSetupRanges.props.name ?? `__VLS_props`}),\n`); + yield _(`...${scriptSetupRanges.props.name ?? `__VLS_props`},\n`); } // fill $emit if (scriptSetupRanges.emits.define) { - codes.push(`$emit: ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`); + yield _(`$emit: ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`); } } } - function generateTemplate(functional: boolean) { + function* generateTemplate(functional: boolean): Generator { generatedTemplate = true; if (!vueCompilerOptions.skipTemplateCodegen) { - - generateExportOptions(); - generateConstNameOption(); - - codes.push(`function __VLS_template() {\n`); - - const templateGened = generateTemplateContext(); - - codes.push(`}\n`); - - generateComponentForTemplateUsage(functional, templateGened.cssIds); + yield* generateExportOptions(); + yield* generateConstNameOption(); + yield _(`function __VLS_template() {\n`); + const cssIds = new Set(); + yield* generateTemplateContext(cssIds); + yield _(`}\n`); + yield* generateComponentForTemplateUsage(functional, cssIds); } else { - codes.push(`function __VLS_template() {\n`); + yield _(`function __VLS_template() {\n`); const templateUsageVars = [...getTemplateUsageVars()]; - codes.push(`// @ts-ignore\n`); - codes.push(`[${templateUsageVars.join(', ')}]\n`); - codes.push(`return {};\n`); - codes.push(`}\n`); + yield _(`// @ts-ignore\n`); + yield _(`[${templateUsageVars.join(', ')}]\n`); + yield _(`return {};\n`); + yield _(`}\n`); } } - function generateComponentForTemplateUsage(functional: boolean, cssIds: Set) { + function* generateComponentForTemplateUsage(functional: boolean, cssIds: Set): Generator { if (scriptSetup && scriptSetupRanges) { - codes.push(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({\n`); - codes.push(`setup() {\n`); - codes.push(`return {\n`); - generateSetupReturns(); + yield _(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({\n`); + yield _(`setup() {\n`); + yield _(`return {\n`); + yield* generateSetupReturns(); // bindings const templateUsageVars = getTemplateUsageVars(); for (const [content, bindings] of [ @@ -728,119 +890,120 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?: if (!templateUsageVars.has(varName) && !cssIds.has(varName)) { continue; } - const templateStart = getLength(codes); - codes.push(varName); - const templateEnd = getLength(codes); - codes.push(`: ${varName} as typeof `); - - const scriptStart = getLength(codes); - codes.push(varName); - const scriptEnd = getLength(codes); - codes.push(',\n'); - - mirrorBehaviorMappings.push({ - sourceRange: [scriptStart, scriptEnd], - generatedRange: [templateStart, templateEnd], - data: [ - MirrorBehaviorCapabilities.full, - MirrorBehaviorCapabilities.full, - ], + const templateOffset = getGeneratedLength(); + yield _(varName); + yield _(`: ${varName} as typeof `); + + const scriptOffset = getGeneratedLength(); + yield _(varName); + yield _(`,\n`); + + linkedCodeMappings.push({ + sourceOffsets: [scriptOffset], + generatedOffsets: [templateOffset], + lengths: [varName.length], + data: undefined, }); } } - codes.push(`};\n`); // return { - codes.push(`},\n`); // setup() { - generateComponentOptions(functional); - codes.push(`});\n`); // defineComponent({ + yield _(`};\n`); // return { + yield _(`},\n`); // setup() { + yield* generateComponentOptions(functional); + yield _(`});\n`); // defineComponent { } else if (script) { - codes.push(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`); + yield _(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`); } else { - codes.push(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({});\n`); + yield _(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({});\n`); } } - function generateExportOptions() { - codes.push(`\n`); - codes.push(`const __VLS_componentsOption = `); + function* generateExportOptions(): Generator { + yield _(`\n`); + yield _(`const __VLS_componentsOption = `); if (script && scriptRanges?.exportDefault?.componentsOption) { const componentsOption = scriptRanges.exportDefault.componentsOption; - codes.push([ + yield _([ script.content.substring(componentsOption.start, componentsOption.end), 'script', componentsOption.start, - { - references: true, - rename: true, - }, + disableAllFeatures({ + navigation: true, + }), ]); } else { - codes.push('{}'); + yield _(`{}`); } - codes.push(`;\n`); + yield _(`;\n`); } - function generateConstNameOption() { - codes.push(`\n`); + function* generateConstNameOption(): Generator { + yield _(`\n`); if (script && scriptRanges?.exportDefault?.nameOption) { const nameOption = scriptRanges.exportDefault.nameOption; - codes.push(`const __VLS_name = `); - codes.push(`${script.content.substring(nameOption.start, nameOption.end)} as const`); - codes.push(`;\n`); + yield _(`const __VLS_name = `); + yield _(`${script.content.substring(nameOption.start, nameOption.end)} as const`); + yield _(`;\n`); } else if (scriptSetup) { - codes.push(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); + yield _(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); } else { - codes.push(`const __VLS_name = undefined;\n`); + yield _(`const __VLS_name = undefined;\n`); } } - function generateTemplateContext() { + function* generateTemplateContext(cssIds = new Set()): Generator { const useGlobalThisTypeInCtx = fileName.endsWith('.html'); - codes.push(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); - codes.push(`InstanceType<__VLS_PickNotAny {}>> & {\n`); + yield _(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); + yield _(`InstanceType<__VLS_PickNotAny {}>> & {\n`); /* CSS Module */ for (let i = 0; i < styles.length; i++) { const style = styles[i]; if (style.module) { - codes.push(`${style.module}: Record & __VLS_Prettify<{}`); + yield _(`${style.module}: Record & ${helperTypes.Prettify.name}<{}`); for (const className of style.classNames) { - generateCssClassProperty( + yield* generateCssClassProperty( i, - className.text.substring(1), - { start: className.offset, end: className.offset + className.text.length }, + className.text, + className.offset, 'string', false, true, ); } - codes.push('>;\n'); + yield _(`>;\n`); } } - codes.push(`};\n`); + yield _(`}`); + + if (bindingNames.size !== 0 && scriptSetupRanges?.props.define) { + yield _(` & Omit `'${name}'`).join(' | ')}>`); + } + + yield _(`;\n`); /* Components */ - codes.push('/* Components */\n'); - codes.push(`let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption;\n`); - codes.push(`let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target)}: typeof ${scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>;\n`); - codes.push(`let __VLS_localComponents!: typeof __VLS_otherComponents & Omit;\n`); - codes.push(`let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx;\n`); // for html completion, TS references... + yield _(`/* Components */\n`); + yield _(`let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption;\n`); + yield _(`let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target)}: typeof ${scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>;\n`); + yield _(`let __VLS_localComponents!: typeof __VLS_otherComponents & Omit;\n`); + yield _(`let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx;\n`); // for html completion, TS references... /* Style Scoped */ - codes.push('/* Style Scoped */\n'); - codes.push('type __VLS_StyleScopedClasses = {}'); + yield _(`/* Style Scoped */\n`); + yield _(`type __VLS_StyleScopedClasses = {}`); for (let i = 0; i < styles.length; i++) { const style = styles[i]; const option = vueCompilerOptions.experimentalResolveStyleCssClasses; if (option === 'always' || (option === 'scoped' && style.scoped)) { for (const className of style.classNames) { - generateCssClassProperty( + yield* generateCssClassProperty( i, - className.text.substring(1), - { start: className.offset, end: className.offset + className.text.length }, + className.text, + className.offset, 'boolean', true, !style.module, @@ -848,152 +1011,147 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?: } } } - codes.push(';\n'); - codes.push('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); - - codes.push(`/* CSS variable injection */\n`); - const cssIds = generateCssVars(); - codes.push(`/* CSS variable injection end */\n`); - - if (htmlGen) { - muggle.setTracking(false); - for (const s of htmlGen.codes) { - codes.push(s); - } - muggle.setTracking(true); - for (const s of htmlGen.codeStacks) { - codeStacks.push(s); + yield _(`;\n`); + yield _('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); + + yield _(`/* CSS variable injection */\n`); + yield* generateCssVars(cssIds); + yield _(`/* CSS variable injection end */\n`); + + if (templateCodegen) { + for (let i = 0; i < templateCodegen.tsCodes.length; i++) { + yield [ + templateCodegen.tsCodes[i], + templateCodegen.tsCodegenStacks[i], + ]; } } - - if (!htmlGen) { - codes.push(`// no template\n`); + else { + yield _(`// no template\n`); if (!scriptSetupRanges?.slots.define) { - codes.push(`const __VLS_slots = {};\n`); + yield _(`const __VLS_slots = {};\n`); } } - codes.push(`return ${scriptSetupRanges?.slots.name ?? '__VLS_slots'};\n`); + yield _(`return ${scriptSetupRanges?.slots.name ?? '__VLS_slots'};\n`); - return { cssIds }; - - function generateCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean, referencesCodeLens: boolean) { - codes.push(`\n & { `); - codes.push([ - '', - 'style_' + styleIndex, - classRange.start, - { - references: true, - referencesCodeLens, - }, - ]); - codes.push(`'`); - codes.push([ - className, - 'style_' + styleIndex, - [classRange.start, classRange.end], - { - references: true, - rename: { - normalize: normalizeCssRename, - apply: applyCssRename, - }, + } + function* generateCssClassProperty( + styleIndex: number, + classNameWithDot: string, + offset: number, + propertyType: string, + optional: boolean, + referencesCodeLens: boolean + ): Generator { + yield _(`\n & { `); + yield _([ + '', + 'style_' + styleIndex, + offset, + disableAllFeatures({ + navigation: true, + __referencesCodeLens: referencesCodeLens, + }), + ]); + yield _(`'`); + yield _([ + '', + 'style_' + styleIndex, + offset, + disableAllFeatures({ + navigation: { + resolveRenameNewName: normalizeCssRename, + resolveRenameEditText: applyCssRename, }, - ]); - codes.push(`'`); - codes.push([ - '', - 'style_' + styleIndex, - classRange.end, - {}, - ]); - codes.push(`${optional ? '?' : ''}: ${propertyType}`); - codes.push(` }`); - } - function generateCssVars() { - - const emptyLocalVars = new Map(); - const identifiers = new Set(); - - for (const style of styles) { - for (const cssBind of style.cssVars) { - walkInterpolationFragment( - ts, - cssBind.text, - ts.createSourceFile('/a.txt', cssBind.text, ts.ScriptTarget.ESNext), - (frag, fragOffset, onlyForErrorMapping) => { - if (fragOffset === undefined) { - codes.push(frag); - } - else { - codes.push([ - frag, - style.name, - cssBind.offset + fragOffset, - onlyForErrorMapping - ? { diagnostic: true } - : FileRangeCapabilities.full, - ]); - } - }, - emptyLocalVars, - identifiers, - vueCompilerOptions, - ); - codes.push(';\n'); + }), + ]); + yield _([ + classNameWithDot.substring(1), + 'style_' + styleIndex, + offset + 1, + disableAllFeatures({ __combineLastMappping: true }), + ]); + yield _(`'`); + yield _([ + '', + 'style_' + styleIndex, + offset + classNameWithDot.length, + disableAllFeatures({}), + ]); + yield _(`${optional ? '?' : ''}: ${propertyType}`); + yield _(` }`); + } + function* generateCssVars(cssIds: Set): Generator { + + const emptyLocalVars = new Map(); + + for (const style of styles) { + for (const cssBind of style.cssVars) { + for (const [segment, offset, onlyError] of eachInterpolationSegment( + ts, + cssBind.text, + ts.createSourceFile('/a.txt', cssBind.text, 99 satisfies ts.ScriptTarget.ESNext), + emptyLocalVars, + cssIds, + vueCompilerOptions, + )) { + if (offset === undefined) { + yield _(segment); + } + else { + yield _([ + segment, + style.name, + cssBind.offset + offset, + onlyError + ? disableAllFeatures({ verification: true }) + : enableAllFeatures({}), + ]); + } } + yield _(`;\n`); } - - return identifiers; } } function getTemplateUsageVars() { const usageVars = new Set(); - if (htmlGen) { + if (templateCodegen) { // fix import components unused report for (const varName of bindingNames) { - if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenateTag(varName)]) { + if (templateCodegen.tagNames.has(varName) || templateCodegen.tagNames.has(hyphenateTag(varName))) { usageVars.add(varName); } } - for (const tag of Object.keys(htmlGen.tagNames)) { + for (const tag of Object.keys(templateCodegen.tagNames)) { if (tag.indexOf('.') >= 0) { usageVars.add(tag.split('.')[0]); } } - for (const _id of htmlGen.accessedGlobalVariables) { + for (const _id of templateCodegen.accessedGlobalVariables) { usageVars.add(_id); } } return usageVars; } - function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end?: number) { - muggle.offsetStack(); - codes.push([ - (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end), - vueTag, + function generateSourceCode(block: SfcBlock, start: number, end?: number): Code { + return [ + block.content.substring(start, end), + block.name, start, - FileRangeCapabilities.full, // diagnostic also working for setup() returns unused in template checking - ]); - muggle.resetOffsetStack(); + enableAllFeatures({}), // diagnostic also working for setup() returns unused in template checking + ]; } - function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { - muggle.offsetStack(); - codes.push([ - (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end), - vueTag, + function generateSourceCodeForExtraReference(block: SfcBlock, start: number, end: number): Code { + return [ + block.content.substring(start, end), + block.name, start, - { - references: true, - definition: true, - rename: true, - }, - ]); - muggle.resetOffsetStack(); + disableAllFeatures({ navigation: true }), + ]; } } diff --git a/packages/language-core/src/generators/template.ts b/packages/language-core/src/generators/template.ts index f95543da24..26b88a2bf8 100644 --- a/packages/language-core/src/generators/template.ts +++ b/packages/language-core/src/generators/template.ts @@ -1,18 +1,18 @@ -import { FileRangeCapabilities } from '@volar/language-core'; -import { Segment } from '@volar/source-map'; +import { toString } from '@volar/language-core'; import * as CompilerDOM from '@vue/compiler-dom'; import { camelize, capitalize } from '@vue/shared'; import { minimatch } from 'minimatch'; -import * as muggle from 'muggle-string'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import { Sfc, VueCompilerOptions } from '../types'; +import { Code, CodeAndStack, Sfc, VueCodeInformation, VueCompilerOptions } from '../types'; import { hyphenateAttr, hyphenateTag } from '../utils/shared'; -import { collectVars, walkInterpolationFragment } from '../utils/transform'; - -const capabilitiesPresets = { - all: FileRangeCapabilities.full, - allWithHiddenParam: { - ...FileRangeCapabilities.full, __hint: { +import { collectVars, eachInterpolationSegment } from '../utils/transform'; +import { disableAllFeatures, enableAllFeatures, getStack, mergeFeatureSettings } from './utils'; + +const presetInfos = { + disabledAll: disableAllFeatures({}), + all: enableAllFeatures({}), + allWithHiddenParam: enableAllFeatures({ + __hint: { setting: 'vue.inlayHints.inlineHandlerLeading', label: '$event =>', tooltip: [ @@ -21,20 +21,20 @@ const capabilitiesPresets = { '[More info](https://github.com/vuejs/language-tools/issues/2445#issuecomment-1444771420)', ].join('\n\n'), paddingRight: true, - } /* TODO */ - } as FileRangeCapabilities, - noDiagnostic: { ...FileRangeCapabilities.full, diagnostic: false } satisfies FileRangeCapabilities, - diagnosticOnly: { diagnostic: true } satisfies FileRangeCapabilities, - tagHover: { hover: true } satisfies FileRangeCapabilities, - event: { hover: true, diagnostic: true } satisfies FileRangeCapabilities, - tagReference: { references: true, definition: true, rename: { normalize: undefined, apply: noEditApply } } satisfies FileRangeCapabilities, - attr: { hover: true, diagnostic: true, references: true, definition: true, rename: true } satisfies FileRangeCapabilities, - attrReference: { references: true, definition: true, rename: true } satisfies FileRangeCapabilities, - slotProp: { references: true, definition: true, rename: true, diagnostic: true } satisfies FileRangeCapabilities, - scopedClassName: { references: true, definition: true, rename: true, completion: true } satisfies FileRangeCapabilities, - slotName: { hover: true, diagnostic: true, references: true, definition: true, completion: true } satisfies FileRangeCapabilities, - slotNameExport: { hover: true, diagnostic: true, references: true, definition: true, /* referencesCodeLens: true */ } satisfies FileRangeCapabilities, - refAttr: { references: true, definition: true, rename: true } satisfies FileRangeCapabilities, + } + }), + noDiagnostics: enableAllFeatures({ verification: false }), + diagnosticOnly: disableAllFeatures({ verification: true }), + tagHover: disableAllFeatures({ semantic: { shouldHighlight: () => false } }), + event: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true }), + tagReference: disableAllFeatures({ navigation: { shouldRename: () => false } }), + attr: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true }), + attrReference: disableAllFeatures({ navigation: true }), + slotProp: disableAllFeatures({ navigation: true, verification: true }), + scopedClassName: disableAllFeatures({ navigation: true, completion: true }), + slotName: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true, completion: true }), + slotNameExport: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true, /* __navigationCodeLens: true */ }), + refAttr: disableAllFeatures({ navigation: true }), }; const formatBrackets = { normal: ['`${', '}`;'] as [string, string], @@ -63,9 +63,9 @@ const transformContext: CompilerDOM.TransformContext = { expressionPlugins: ['typescript'], }; -type Code = Segment; +type _CodeAndStack = [codeType: 'ts' | 'tsFormat' | 'inlineCss', ...codeAndStack: CodeAndStack]; -export function generate( +export function* generate( ts: typeof import('typescript/lib/tsserverlibrary'), compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, @@ -78,26 +78,69 @@ export function generate( codegenStack: boolean, ) { + const processDirectiveComment = (code: Code) => { + if (ignoreError && typeof code !== 'string') { + const data = code[3]; + if (data.verification) { + code[3] = { + ...data, + verification: false, + }; + } + } + if (expectErrorToken && typeof code !== 'string') { + const token = expectErrorToken; + const data = code[3]; + if (data.verification) { + code[3] = { + ...data, + verification: { + shouldReport: () => { + token.errors++; + return false; + }, + }, + }; + } + } + return code; + }; + const _ts = codegenStack + ? (code: Code): _CodeAndStack => ['ts', processDirectiveComment(code), getStack()] + : (code: Code): _CodeAndStack => ['ts', processDirectiveComment(code), '']; + const _tsFormat = codegenStack + ? (code: Code): _CodeAndStack => ['tsFormat', code, getStack()] + : (code: Code): _CodeAndStack => ['tsFormat', code, '']; + const _inlineCss = codegenStack + ? (code: Code): _CodeAndStack => ['inlineCss', code, getStack()] + : (code: Code): _CodeAndStack => ['inlineCss', code, '']; const nativeTags = new Set(vueCompilerOptions.nativeTags); - const [codes, codeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []]; - const [formatCodes, formatCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []]; - const [cssCodes, cssCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []]; - const slots = new Map(); + const slots = new Map(); const slotExps = new Map(); - const tagNames = collectTagOffsets(); + const tagOffsetsMap = collectTagOffsets(); const localVars = new Map(); - const tempVars: ReturnType[] = []; + const tempVars: { + text: string, + isShorthand: boolean, + offset: number, + }[][] = []; const accessedGlobalVariables = new Set(); const scopedClasses: { className: string, offset: number; }[] = []; const blockConditions: string[] = []; const hasSlotElements = new Set(); - const componentCtxVar2EmitEventsVar = new Map(); + const usedComponentCtxVars = new Set(); let hasSlot = false; - let elementIndex = 0; - let ignoreStart: undefined | number; - let expectedErrorStart: undefined | number; + let ignoreError = false; + let expectErrorToken: { errors: number; } | undefined; let expectedErrorNode: CompilerDOM.CommentNode | undefined; + let elementIndex = 0; if (slotsAssignName) { localVars.set(slotsAssignName, 1); @@ -107,189 +150,37 @@ export function generate( localVars.set(propsAssignName, 1); } - generatePreResolveComponents(); + yield* generatePreResolveComponents(); if (template.ast) { - visitNode(template.ast, undefined, undefined, undefined); + yield* generateAstNode(template.ast, undefined, undefined, undefined); } - generateStyleScopedClasses(); + yield* generateStyleScopedClasses(); if (!hasScriptSetupSlots) { - codes.push( - 'var __VLS_slots!:', - ...createSlotsTypeCode(), - ';\n', - ); + yield _ts('var __VLS_slots!:'); + yield* generateSlotsType(); + yield _ts(';\n'); } - generateAutoImportCompletionCode(); + yield* generateExtraAutoImport(); return { - codes, - codeStacks, - formatCodes, - formatCodeStacks, - cssCodes, - cssCodeStacks, - tagNames, + tagOffsetsMap, accessedGlobalVariables, hasSlot, }; - function createSlotsTypeCode(): Code[] { - const codes: Code[] = []; - for (const [exp, slot] of slotExps) { - hasSlot = true; - codes.push(`Partial, (_: typeof ${slot.varName}) => any>> &\n`); - } - codes.push(`{\n`); - for (const [name, slot] of slots) { - hasSlot = true; - codes.push( - ...createObjectPropertyCode([ - name, - 'template', - slot.loc, - { - ...capabilitiesPresets.slotNameExport, - referencesCodeLens: true, - }, - ], slot.nodeLoc), - ); - codes.push(`?(_: typeof ${slot.varName}): any,\n`); - } - codes.push(`}`); - return codes; - } - - function generateStyleScopedClasses() { - codes.push(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`); - for (const { className, offset } of scopedClasses) { - codes.push(`__VLS_styleScopedClasses[`); - codes.push(...createStringLiteralKeyCode([ - className, - 'template', - offset, - { - ...capabilitiesPresets.scopedClassName, - displayWithLink: stylesScopedClasses.has(className), - }, - ])); - codes.push(`];\n`); - } - codes.push('}\n'); - } - - function toCanonicalComponentName(tagText: string) { - return validTsVarReg.test(tagText) ? tagText : capitalize(camelize(tagText.replace(colonReg, '-'))); - } - - function getPossibleOriginalComponentName(tagText: string) { - return [...new Set([ - // order is important: https://github.com/vuejs/language-tools/issues/2010 - capitalize(camelize(tagText)), - camelize(tagText), - tagText, - ])]; - } - - function generatePreResolveComponents() { - - codes.push(`let __VLS_resolvedLocalAndGlobalComponents!: {}\n`); - - for (const tagName in tagNames) { - - if (nativeTags.has(tagName)) - continue; - - const isNamespacedTag = tagName.indexOf('.') >= 0; - if (isNamespacedTag) - continue; - - codes.push( - `& __VLS_WithComponent<'${toCanonicalComponentName(tagName)}', typeof __VLS_localComponents, `, - // order is important: https://github.com/vuejs/language-tools/issues/2010 - `"${capitalize(camelize(tagName))}", `, - `"${camelize(tagName)}", `, - `"${tagName}"`, - '>\n', - ); - } - - codes.push(`;\n`); - - for (const tagName in tagNames) { - - const tagOffsets = tagNames[tagName]; - const tagRanges: [number, number][] = tagOffsets.map(offset => [offset, offset + tagName.length]); - const names = nativeTags.has(tagName) ? [tagName] : getPossibleOriginalComponentName(tagName); - - for (const name of names) { - for (const tagRange of tagRanges) { - codes.push( - nativeTags.has(tagName) ? '__VLS_intrinsicElements' : '__VLS_components', - ...createPropertyAccessCode([ - name, - 'template', - tagRange, - { - ...capabilitiesPresets.tagReference, - rename: { - normalize: tagName === name ? capabilitiesPresets.tagReference.rename.normalize : camelizeComponentName, - apply: getTagRenameApply(tagName), - }, - ...nativeTags.has(tagName) ? { - ...capabilitiesPresets.tagHover, - ...capabilitiesPresets.diagnosticOnly, - } : {}, - }, - ]), - ';', - ); - } - } - codes.push('\n'); - - if (nativeTags.has(tagName)) - continue; - - const isNamespacedTag = tagName.indexOf('.') >= 0; - if (isNamespacedTag) - continue; - - codes.push( - '// @ts-ignore\n', // #2304 - '[', - ); - const validName = toCanonicalComponentName(tagName); - for (const tagRange of tagRanges) { - codes.push([ - validName, - 'template', - tagRange, - { - completion: { - additional: true, - autoImportOnly: true, - }, - }, - ]); - codes.push(','); - } - codes.push(`];\n`); - } - } - function collectTagOffsets() { - const tagOffsetsMap: Record = {}; + const tagOffsetsMap = new Map(); if (!template.ast) { return tagOffsetsMap; } - walkElementNodes(template.ast, node => { + for (const node of eachElementNode(template.ast)) { if (node.tag === 'slot') { // ignore } @@ -297,16 +188,20 @@ export function generate( for (const prop of node.props) { if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name === 'is' && prop.value) { const tag = prop.value.content; - tagOffsetsMap[tag] ??= []; - tagOffsetsMap[tag].push(prop.value.loc.start.offset + prop.value.loc.source.lastIndexOf(tag)); + let offsets = tagOffsetsMap.get(tag); + if (!offsets) { + tagOffsetsMap.set(tag, offsets = []); + } + offsets.push(prop.value.loc.start.offset + prop.value.loc.source.lastIndexOf(tag)); break; } } } else { - tagOffsetsMap[node.tag] ??= []; - - const offsets = tagOffsetsMap[node.tag]; + let offsets = tagOffsetsMap.get(node.tag); + if (!offsets) { + tagOffsetsMap.set(node.tag, offsets = []); + } const source = template.content.substring(node.loc.start.offset); const startTagOffset = node.loc.start.offset + source.indexOf(node.tag); @@ -318,75 +213,195 @@ export function generate( } } } - }); + } return tagOffsetsMap; } - function resolveComment() { - if (ignoreStart !== undefined) { - for (let i = ignoreStart; i < codes.length; i++) { - const code = codes[i]; - if (typeof code === 'string') { - continue; + function* generateExpectErrorComment(): Generator<_CodeAndStack> { + + if (expectErrorToken && expectedErrorNode) { + const token = expectErrorToken; + yield _ts([ + '', + 'template', + expectedErrorNode.loc.start.offset, + disableAllFeatures({ + verification: { + shouldReport: () => token.errors === 0, + }, + }), + ]); + yield _ts('// @ts-expect-error __VLS_TS_EXPECT_ERROR'); + yield _ts([ + '', + 'template', + expectedErrorNode.loc.end.offset, + disableAllFeatures({ __combineLastMappping: true }), + ]); + yield _ts('\n;\n'); + } + + ignoreError = false; + expectErrorToken = undefined; + expectedErrorNode = undefined; + } + + function* generateCanonicalComponentName(tagText: string, offset: number, info: VueCodeInformation): Generator<_CodeAndStack> { + if (validTsVarReg.test(tagText)) { + yield _ts([tagText, 'template', offset, info]); + } + else { + yield* generateCamelized( + capitalize(tagText.replace(colonReg, '-')), + offset, + info + ); + } + } + + function* generateSlotsType(): Generator<_CodeAndStack> { + for (const [exp, slot] of slotExps) { + hasSlot = true; + yield _ts(`Partial, (_: typeof ${slot.varName}) => any>> &\n`); + } + yield _ts(`{\n`); + for (const [_, slot] of slots) { + hasSlot = true; + if (slot.name && slot.loc !== undefined) { + yield* generateObjectProperty( + slot.name, + slot.loc, + mergeFeatureSettings(presetInfos.slotNameExport, disableAllFeatures({ __referencesCodeLens: true })), + slot.nodeLoc + ); + } + else { + yield _ts(['', 'template', slot.tagRange[0], mergeFeatureSettings(presetInfos.slotNameExport, disableAllFeatures({ __referencesCodeLens: true }))]); + yield _ts('default'); + yield _ts(['', 'template', slot.tagRange[1], disableAllFeatures({ __combineLastMappping: true })]); + } + yield _ts(`?(_: typeof ${slot.varName}): any,\n`); + } + yield _ts(`}`); + } + + function* generateStyleScopedClasses(): Generator<_CodeAndStack> { + yield _ts(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`); + for (const { className, offset } of scopedClasses) { + yield _ts(`__VLS_styleScopedClasses[`); + yield* generateStringLiteralKey( + className, + offset, + mergeFeatureSettings( + presetInfos.scopedClassName, + disableAllFeatures({ __displayWithLink: stylesScopedClasses.has(className) }), + ), + ); + yield _ts(`];\n`); + } + yield _ts('}\n'); + } + + function* generatePreResolveComponents(): Generator<_CodeAndStack> { + + yield _ts(`let __VLS_resolvedLocalAndGlobalComponents!: {}\n`); + + for (const [tagName] of tagOffsetsMap) { + + if (nativeTags.has(tagName)) + continue; + + const isNamespacedTag = tagName.indexOf('.') >= 0; + if (isNamespacedTag) + continue; + + yield _ts(`& __VLS_WithComponent<'${getCanonicalComponentName(tagName)}', typeof __VLS_localComponents, `); + // order is important: https://github.com/vuejs/language-tools/issues/2010 + yield _ts(`"${capitalize(camelize(tagName))}", `); + yield _ts(`"${camelize(tagName)}", `); + yield _ts(`"${tagName}"`); + yield _ts('>\n'); + } + + yield _ts(`;\n`); + + for (const [tagName, tagOffsets] of tagOffsetsMap) { + + for (const tagOffset of tagOffsets) { + if (nativeTags.has(tagName)) { + yield _ts(`__VLS_intrinsicElements`); + yield* generatePropertyAccess( + tagName, + tagOffset, + mergeFeatureSettings( + presetInfos.tagReference, + { + navigation: true + }, + ...[ + presetInfos.tagHover, + presetInfos.diagnosticOnly, + ], + ), + ); + yield _ts(';'); } - const cap = code[3]; - if (cap.diagnostic) { - code[3] = { - ...cap, - diagnostic: false, - }; + else if (validTsVarReg.test(camelize(tagName))) { + for (const shouldCapitalize of tagName[0] === tagName.toUpperCase() ? [false] : [true, false]) { + const expectName = shouldCapitalize ? capitalize(camelize(tagName)) : camelize(tagName); + yield _ts('__VLS_components.'); + yield* generateCamelized( + shouldCapitalize ? capitalize(tagName) : tagName, + tagOffset, + mergeFeatureSettings( + presetInfos.tagReference, + { + navigation: { + resolveRenameNewName: tagName !== expectName ? camelizeComponentName : undefined, + resolveRenameEditText: getTagRenameApply(tagName), + } + }, + ), + ); + yield _ts(';'); + } } } - ignoreStart = undefined; - } - if (expectedErrorStart !== undefined && expectedErrorStart !== codes.length && expectedErrorNode) { - let errors = 0; - const suppressError = () => { - errors++; - return false; - }; - for (let i = expectedErrorStart; i < codes.length; i++) { - const code = codes[i]; - if (typeof code === 'string') { - continue; - } - const cap = code[3]; - if (cap.diagnostic) { - code[3] = { - ...cap, - diagnostic: { - shouldReport: suppressError, - }, - }; + yield _ts('\n'); + + if ( + !nativeTags.has(tagName) + && validTsVarReg.test(camelize(tagName)) + ) { + yield _ts('// @ts-ignore\n'); // #2304 + yield _ts('['); + for (const tagOffset of tagOffsets) { + yield* generateCamelized( + capitalize(tagName), + tagOffset, + disableAllFeatures({ + completion: { + isAdditional: true, + onlyImport: true, + }, + }), + ); + yield _ts(','); } + yield _ts(`];\n`); } - codes.push( - [ - '// @ts-expect-error __VLS_TS_EXPECT_ERROR', - 'template', - [expectedErrorNode.loc.start.offset, expectedErrorNode.loc.end.offset], - { - diagnostic: { - shouldReport: () => errors === 0, - }, - }, - ], - '\n;\n', - ); - expectedErrorStart = undefined; - expectedErrorNode = undefined; } } - function visitNode( + function* generateAstNode( node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode | CompilerDOM.InterpolationNode | CompilerDOM.CompoundExpressionNode | CompilerDOM.TextNode | CompilerDOM.SimpleExpressionNode, parentEl: CompilerDOM.ElementNode | undefined, prevNode: CompilerDOM.TemplateChildNode | undefined, componentCtxVar: string | undefined, - ): void { + ): Generator<_CodeAndStack> { - resolveComment(); + yield* generateExpectErrorComment(); if (prevNode?.type === CompilerDOM.NodeTypes.COMMENT) { const commentText = prevNode.content.trim().split(' ')[0]; @@ -394,10 +409,10 @@ export function generate( return; } else if (commentText.match(/^@vue-ignore\b[\s\S]*/)) { - ignoreStart = codes.length; + ignoreError = true; } else if (commentText.match(/^@vue-expect-error\b[\s\S]*/)) { - expectedErrorStart = codes.length; + expectErrorToken = { errors: 0 }; expectedErrorNode = prevNode; } } @@ -405,33 +420,33 @@ export function generate( if (node.type === CompilerDOM.NodeTypes.ROOT) { let prev: CompilerDOM.TemplateChildNode | undefined; for (const childNode of node.children) { - visitNode(childNode, parentEl, prev, componentCtxVar); + yield* generateAstNode(childNode, parentEl, prev, componentCtxVar); prev = childNode; } - resolveComment(); + yield* generateExpectErrorComment(); } else if (node.type === CompilerDOM.NodeTypes.ELEMENT) { const vForNode = getVForNode(node); const vIfNode = getVIfNode(node); if (vForNode) { - visitVForNode(vForNode, parentEl, componentCtxVar); + yield* generateVFor(vForNode, parentEl, componentCtxVar); } else if (vIfNode) { - visitVIfNode(vIfNode, parentEl, componentCtxVar); + yield* generateVIf(vIfNode, parentEl, componentCtxVar); } else { - visitElementNode(node, parentEl, componentCtxVar); + yield* generateElement(node, parentEl, componentCtxVar); } } else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) { // {{ var }} - visitNode(node.content, parentEl, undefined, componentCtxVar); + yield* generateAstNode(node.content, parentEl, undefined, componentCtxVar); } else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) { // {{ ... }} {{ ... }} for (const childNode of node.children) { if (typeof childNode === 'object') { - visitNode(childNode, parentEl, undefined, componentCtxVar); + yield* generateAstNode(childNode, parentEl, undefined, componentCtxVar); } } } @@ -452,42 +467,38 @@ export function generate( content = content + rightCharacter; } - codes.push( - ...createInterpolationCode( - content, - node.content.loc, - start, - capabilitiesPresets.all, - '(', - ');\n', - ), + yield* generateInterpolation( + content, + node.content.loc, + start, + presetInfos.all, + '(', + ');\n', ); const lines = content.split('\n'); - formatCodes.push( - ...createFormatCode( - content, - start, - lines.length <= 1 ? formatBrackets.curly : [ - formatBrackets.curly[0], - lines[lines.length - 1].trim() === '' ? '' : formatBrackets.curly[1], - ], - ), + yield* generateTsFormat( + content, + start, + lines.length <= 1 ? formatBrackets.curly : [ + formatBrackets.curly[0], + lines[lines.length - 1].trim() === '' ? '' : formatBrackets.curly[1], + ], ); } else if (node.type === CompilerDOM.NodeTypes.IF) { // v-if / v-else-if / v-else - visitVIfNode(node, parentEl, componentCtxVar); + yield* generateVIf(node, parentEl, componentCtxVar); } else if (node.type === CompilerDOM.NodeTypes.FOR) { // v-for - visitVForNode(node, parentEl, componentCtxVar); + yield* generateVFor(node, parentEl, componentCtxVar); } else if (node.type === CompilerDOM.NodeTypes.TEXT) { // not needed progress } } - function visitVIfNode(node: CompilerDOM.IfNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) { + function* generateVIf(node: CompilerDOM.IfNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> { let originalBlockConditionsLength = blockConditions.length; @@ -496,52 +507,50 @@ export function generate( const branch = node.branches[i]; if (i === 0) - codes.push('if'); + yield _ts('if'); else if (branch.condition) - codes.push('else if'); + yield _ts('else if'); else - codes.push('else'); + yield _ts('else'); let addedBlockCondition = false; if (branch.condition?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - codes.push(` `); - const beforeCodeLength = codes.length; - codes.push( - ...createInterpolationCode( - branch.condition.content, - branch.condition.loc, - branch.condition.loc.start.offset, - capabilitiesPresets.all, - '(', - ')', - ), + yield _ts(` `); + yield* generateInterpolation( + branch.condition.content, + branch.condition.loc, + branch.condition.loc.start.offset, + presetInfos.all, + '(', + ')', ); - const afterCodeLength = codes.length; - - formatCodes.push( - ...createFormatCode( - branch.condition.content, - branch.condition.loc.start.offset, - formatBrackets.normal, - ), + blockConditions.push( + toString( + [...generateInterpolation(branch.condition.content, branch.condition.loc, undefined, undefined, '(', ')')] + .map(code => code[1]) + ) ); - - blockConditions.push(muggle.toString(codes.slice(beforeCodeLength, afterCodeLength))); addedBlockCondition = true; + + yield* generateTsFormat( + branch.condition.content, + branch.condition.loc.start.offset, + formatBrackets.normal, + ); } - codes.push(` {\n`); + yield _ts(` {\n`); let prev: CompilerDOM.TemplateChildNode | undefined; for (const childNode of branch.children) { - visitNode(childNode, parentEl, prev, componentCtxVar); + yield* generateAstNode(childNode, parentEl, prev, componentCtxVar); prev = childNode; } - resolveComment(); + yield* generateExpectErrorComment(); - generateAutoImportCompletionCode(); - codes.push('}\n'); + yield* generateExtraAutoImport(); + yield _ts('}\n'); if (addedBlockCondition) { blockConditions[blockConditions.length - 1] = `!(${blockConditions[blockConditions.length - 1]})`; @@ -551,57 +560,53 @@ export function generate( blockConditions.length = originalBlockConditionsLength; } - function visitVForNode(node: CompilerDOM.ForNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) { + function* generateVFor(node: CompilerDOM.ForNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> { const { source, value, key, index } = node.parseResult; const leftExpressionRange = value ? { start: (value ?? key ?? index).loc.start.offset, end: (index ?? key ?? value).loc.end.offset } : undefined; const leftExpressionText = leftExpressionRange ? node.loc.source.substring(leftExpressionRange.start - node.loc.start.offset, leftExpressionRange.end - node.loc.start.offset) : undefined; const forBlockVars: string[] = []; - codes.push(`for (const [`); + yield _ts(`for (const [`); if (leftExpressionRange && leftExpressionText) { const collectAst = createTsAst(node.parseResult, `const [${leftExpressionText}]`); - collectVars(ts, collectAst, forBlockVars); + collectVars(ts, collectAst, collectAst, forBlockVars); for (const varName of forBlockVars) localVars.set(varName, (localVars.get(varName) ?? 0) + 1); - codes.push([leftExpressionText, 'template', leftExpressionRange.start, capabilitiesPresets.all]); - formatCodes.push(...createFormatCode(leftExpressionText, leftExpressionRange.start, formatBrackets.normal)); + yield _ts([leftExpressionText, 'template', leftExpressionRange.start, presetInfos.all]); + yield* generateTsFormat(leftExpressionText, leftExpressionRange.start, formatBrackets.normal); } - codes.push(`] of __VLS_getVForSourceType`); + yield _ts(`] of __VLS_getVForSourceType`); if (source.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - codes.push( + yield _ts('('); + yield* generateInterpolation( + source.content, + source.loc, + source.loc.start.offset, + presetInfos.all, '(', - ...createInterpolationCode( - source.content, - source.loc, - source.loc.start.offset, - capabilitiesPresets.all, - '(', - ')', - ), - '!)', // #3102 - ') {\n', + ')', ); + yield _ts('!)'); // #3102 + yield _ts(') {\n'); let prev: CompilerDOM.TemplateChildNode | undefined; for (const childNode of node.children) { - visitNode(childNode, parentEl, prev, componentCtxVar); + yield* generateAstNode(childNode, parentEl, prev, componentCtxVar); prev = childNode; } - resolveComment(); + yield* generateExpectErrorComment(); - generateAutoImportCompletionCode(); - codes.push('}\n'); + yield* generateExtraAutoImport(); + yield _ts('}\n'); - formatCodes.push( - ...createFormatCode( - source.content, - source.loc.start.offset, - formatBrackets.normal, - ), + yield* generateTsFormat( + source.content, + source.loc.start.offset, + formatBrackets.normal, ); } @@ -609,9 +614,9 @@ export function generate( localVars.set(varName, localVars.get(varName)! - 1); } - function visitElementNode(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) { + function* generateElement(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> { - codes.push(`{\n`); + yield _ts(`{\n`); const startTagOffset = node.loc.start.offset + template.content.substring(node.loc.start.offset).indexOf(node.tag); let endTagOffset = !node.isSelfClosing && template.lang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined; @@ -655,175 +660,158 @@ export function generate( const isIntrinsicElement = nativeTags.has(tag) && tagOffsets.length; if (isIntrinsicElement) { - codes.push( - 'const ', - var_originalComponent, - ` = __VLS_intrinsicElements[`, - ...createStringLiteralKeyCode([ - tag, - 'template', - tagOffsets[0], - capabilitiesPresets.diagnosticOnly, - ]), - '];\n', + yield _ts('const '); + yield _ts(var_originalComponent); + yield _ts(` = __VLS_intrinsicElements[`); + yield* generateStringLiteralKey( + tag, + tagOffsets[0], + presetInfos.diagnosticOnly, ); + yield _ts('];\n'); } else if (isNamespacedTag) { - codes.push( - `const ${var_originalComponent} = `, - ...createInterpolationCode(tag, node.loc, startTagOffset, capabilitiesPresets.all, '', ''), - ';\n', - ); + yield _ts(`const ${var_originalComponent} = `); + yield* generateInterpolation(tag, node.loc, startTagOffset, presetInfos.all, '', ''); + yield _ts(';\n'); } else if (dynamicTagExp) { - codes.push( - `const ${var_originalComponent} = `, - ...createInterpolationCode(dynamicTagExp.loc.source, dynamicTagExp.loc, dynamicTagExp.loc.start.offset, capabilitiesPresets.all, '(', ')'), - ';\n', - ); + yield _ts(`const ${var_originalComponent} = `); + yield* generateInterpolation(dynamicTagExp.loc.source, dynamicTagExp.loc, dynamicTagExp.loc.start.offset, presetInfos.all, '(', ')'); + yield _ts(';\n'); } else { - codes.push( - `const ${var_originalComponent} = ({} as `, - ); - for (const componentName of getPossibleOriginalComponentName(tag)) { - codes.push( - `'${componentName}' extends keyof typeof __VLS_ctx ? `, - `{ '${toCanonicalComponentName(tag)}': typeof __VLS_ctx`, - ...createPropertyAccessCode(componentName), - ` }: `, + yield _ts(`const ${var_originalComponent} = ({} as `); + for (const componentName of getPossibleOriginalComponentNames(tag)) { + yield _ts(`'${componentName}' extends keyof typeof __VLS_ctx ? `); + yield _ts(`{ '${getCanonicalComponentName(tag)}': typeof __VLS_ctx`); + yield* generatePropertyAccess(componentName); + yield _ts(` }: `); + } + yield _ts(`typeof __VLS_resolvedLocalAndGlobalComponents)`); + if (tagOffsets.length) { + yield* generatePropertyAccess( + getCanonicalComponentName(tag), + tagOffsets[0], + presetInfos.diagnosticOnly, ); } - codes.push( - `typeof __VLS_resolvedLocalAndGlobalComponents)`, - ...(tagOffsets.length - ? createPropertyAccessCode([ - toCanonicalComponentName(tag), - 'template', - [tagOffsets[0], tagOffsets[0] + tag.length], - capabilitiesPresets.diagnosticOnly, - ]) - : createPropertyAccessCode(toCanonicalComponentName(tag)) - ), - ';\n', - ); + else { + yield* generatePropertyAccess(getCanonicalComponentName(tag)); + } + yield _ts(';\n'); } if (isIntrinsicElement) { - codes.push(`const ${var_functionalComponent} = __VLS_elementAsFunctionalComponent(${var_originalComponent});\n`,); + yield _ts(`const ${var_functionalComponent} = __VLS_elementAsFunctionalComponent(${var_originalComponent});\n`); } else { - codes.push( - `const ${var_functionalComponent} = __VLS_asFunctionalComponent(`, - `${var_originalComponent}, `, - `new ${var_originalComponent}({`, - ...createPropsCode(node, props, 'extraReferences'), - '})', - ');\n', - ); + yield _ts(`const ${var_functionalComponent} = __VLS_asFunctionalComponent(`); + yield _ts(`${var_originalComponent}, `); + yield _ts(`new ${var_originalComponent}({`); + yield* generateProps(node, props, 'extraReferences'); + yield _ts('})'); + yield _ts(');\n'); } for (const offset of tagOffsets) { if (isNamespacedTag || dynamicTagExp || isIntrinsicElement) { continue; } - const key = toCanonicalComponentName(tag); - codes.push(`({} as { ${key}: typeof ${var_originalComponent} }).`); - codes.push( - [ - key, - 'template', - [offset, offset + tag.length], - { - ...capabilitiesPresets.tagHover, - ...capabilitiesPresets.diagnosticOnly, - }, - ], - ';\n', + yield _ts(`({} as { ${getCanonicalComponentName(tag)}: typeof ${var_originalComponent} }).`); + yield* generateCanonicalComponentName( + tag, + offset, + mergeFeatureSettings( + presetInfos.tagHover, + presetInfos.diagnosticOnly, + ), ); + yield _ts(';\n'); } if (vueCompilerOptions.strictTemplates) { // with strictTemplates, generate once for props type-checking + instance type - codes.push( - `const ${var_componentInstance} = ${var_functionalComponent}(`, - // diagnostic start - tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly] - : dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly] - : '', - '{ ', - ...createPropsCode(node, props, 'normal', propsFailedExps), - '}', - // diagnostic end - tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly] - : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly] - : '', - `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`, + yield _ts(`const ${var_componentInstance} = ${var_functionalComponent}(`); + // diagnostic start + yield _ts( + tagOffsets.length ? ['', 'template', tagOffsets[0], presetInfos.diagnosticOnly] + : dynamicTagExp ? ['', 'template', startTagOffset, presetInfos.diagnosticOnly] + : '' + ); + yield _ts('{ '); + yield* generateProps(node, props, 'normal', propsFailedExps); + yield _ts('}'); + // diagnostic end + yield _ts( + tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, presetInfos.diagnosticOnly] + : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, presetInfos.diagnosticOnly] + : '' ); + yield _ts(`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`); } else { // without strictTemplates, this only for instacne type - codes.push( - `const ${var_componentInstance} = ${var_functionalComponent}(`, - '{ ', - ...createPropsCode(node, props, 'extraReferences'), - '}', - `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`, - ); + yield _ts(`const ${var_componentInstance} = ${var_functionalComponent}(`); + yield _ts('{ '); + yield* generateProps(node, props, 'extraReferences'); + yield _ts('}'); + yield _ts(`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`); // and this for props type-checking - codes.push( - `({} as (props: __VLS_FunctionalComponentProps & Record) => void)(`, - // diagnostic start - tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly] - : dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly] - : '', - '{ ', - ...createPropsCode(node, props, 'normal', propsFailedExps), - '}', - // diagnostic end - tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly] - : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly] - : '', - `);\n`, + yield _ts(`({} as (props: __VLS_FunctionalComponentProps & Record) => void)(`); + // diagnostic start + yield _ts( + tagOffsets.length ? ['', 'template', tagOffsets[0], presetInfos.diagnosticOnly] + : dynamicTagExp ? ['', 'template', startTagOffset, presetInfos.diagnosticOnly] + : '' + ); + yield _ts('{ '); + yield* generateProps(node, props, 'normal', propsFailedExps); + yield _ts('}'); + // diagnostic end + yield _ts( + tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, presetInfos.diagnosticOnly] + : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, presetInfos.diagnosticOnly] + : '' ); + yield _ts(`);\n`); } + let defineComponentCtxVar: string | undefined; + if (tag !== 'template' && tag !== 'slot') { - componentCtxVar = `__VLS_${elementIndex++}`; - const componentEventsVar = `__VLS_${elementIndex++}`; - codes.push(`const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!;\n`); - codes.push(`let ${componentEventsVar}!: __VLS_NormalizeEmits;\n`); - componentCtxVar2EmitEventsVar.set(componentCtxVar, componentEventsVar); + defineComponentCtxVar = `__VLS_${elementIndex++}`; + componentCtxVar = defineComponentCtxVar; parentEl = node; } + const componentEventsVar = `__VLS_${elementIndex++}`; + + let usedComponentEventsVar = false; + //#region // fix https://github.com/vuejs/language-tools/issues/1775 for (const failedExp of propsFailedExps) { - codes.push( - ...createInterpolationCode( - failedExp.loc.source, - failedExp.loc, - failedExp.loc.start.offset, - capabilitiesPresets.all, - '(', - ')', - ), - ';\n', + yield* generateInterpolation( + failedExp.loc.source, + failedExp.loc, + failedExp.loc.start.offset, + presetInfos.all, + '(', + ')', ); + yield _ts(';\n'); const fb = formatBrackets.normal; if (fb) { - formatCodes.push( - ...createFormatCode( - failedExp.loc.source, - failedExp.loc.start.offset, - fb, - ), + yield* generateTsFormat( + failedExp.loc.source, + failedExp.loc.start.offset, + fb, ); } } - generateInlineCss(props); + yield* generateInlineCss(props); const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data')); let inScope = false; @@ -834,107 +822,100 @@ export function generate( const scopeVar = `__VLS_${elementIndex++}`; const condition = `__VLS_withScope(__VLS_ctx, ${scopeVar})`; - codes.push(`const ${scopeVar} = `); - codes.push([ + yield _ts(`const ${scopeVar} = `); + yield _ts([ vScope.exp.loc.source, 'template', vScope.exp.loc.start.offset, - capabilitiesPresets.all, + presetInfos.all, ]); - codes.push(';\n'); - codes.push(`if (${condition}) {\n`); + yield _ts(';\n'); + yield _ts(`if (${condition}) {\n`); blockConditions.push(condition); inScope = true; } - generateDirectives(node); - generateElReferences(node); // + yield* generateDirectives(node); + yield* generateReferencesForElements(node); // if (shouldGenerateScopedClasses) { - generateClassScoped(node); + yield* generateReferencesForScopedCssClasses(node); } if (componentCtxVar) { - generateEvents(node, var_functionalComponent, var_componentInstance, componentCtxVar); + usedComponentCtxVars.add(componentCtxVar); + yield* generateEvents(node, var_functionalComponent, var_componentInstance, componentEventsVar, () => usedComponentEventsVar = true); } if (node.tag === 'slot') { - generateSlot(node, startTagOffset); + yield* generateSlot(node, startTagOffset); } if (inScope) { - codes.push('}\n'); + yield _ts('}\n'); blockConditions.length = originalConditionsNum; } //#endregion const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode; if (slotDir && componentCtxVar) { + usedComponentCtxVars.add(componentCtxVar); if (parentEl) { hasSlotElements.add(parentEl); } const slotBlockVars: string[] = []; - codes.push(`{\n`); + yield _ts(`{\n`); let hasProps = false; if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - formatCodes.push( - ...createFormatCode( - slotDir.exp.content, - slotDir.exp.loc.start.offset, - formatBrackets.params, - ), + yield* generateTsFormat( + slotDir.exp.content, + slotDir.exp.loc.start.offset, + formatBrackets.params, ); const slotAst = createTsAst(slotDir, `(${slotDir.exp.content}) => {}`); - collectVars(ts, slotAst, slotBlockVars); + collectVars(ts, slotAst, slotAst, slotBlockVars); hasProps = true; if (slotDir.exp.content.indexOf(':') === -1) { - codes.push( - 'const [', - [ - slotDir.exp.content, - 'template', - slotDir.exp.loc.start.offset, - capabilitiesPresets.all, - ], - `] = __VLS_getSlotParams(`, - ); + yield _ts('const ['); + yield _ts([ + slotDir.exp.content, + 'template', + slotDir.exp.loc.start.offset, + presetInfos.all, + ]); + yield _ts(`] = __VLS_getSlotParams(`); } else { - codes.push( - 'const ', - [ - slotDir.exp.content, - 'template', - slotDir.exp.loc.start.offset, - capabilitiesPresets.all, - ], - ` = __VLS_getSlotParam(`, - ); + yield _ts('const '); + yield _ts([ + slotDir.exp.content, + 'template', + slotDir.exp.loc.start.offset, + presetInfos.all, + ]); + yield _ts(` = __VLS_getSlotParam(`); } } - codes.push( - ['', 'template', (slotDir.arg ?? slotDir).loc.start.offset, capabilitiesPresets.diagnosticOnly], - `(${componentCtxVar}.slots!)`, - ...( - (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) - ? createPropertyAccessCode([ - slotDir.arg.loc.source, - 'template', - slotDir.arg.loc.start.offset, - slotDir.arg.isStatic ? capabilitiesPresets.slotName : capabilitiesPresets.all - ], slotDir.arg.loc) - : createPropertyAccessCode([ - 'default', - 'template', - [slotDir.loc.start.offset, slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0)], - { ...capabilitiesPresets.slotName, completion: false }, - ]) - ), - ['', 'template', (slotDir.arg ?? slotDir).loc.end.offset, capabilitiesPresets.diagnosticOnly], - ); + yield _ts(['', 'template', (slotDir.arg ?? slotDir).loc.start.offset, presetInfos.diagnosticOnly]); + yield _ts(`(${componentCtxVar}.slots!)`); + if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) { + yield* generatePropertyAccess( + slotDir.arg.loc.source, + slotDir.arg.loc.start.offset, + slotDir.arg.isStatic ? presetInfos.slotName : presetInfos.all, + slotDir.arg.loc + ); + } + else { + yield _ts('.'); + yield _ts(['', 'template', slotDir.loc.start.offset, { ...presetInfos.slotName, completion: false }] satisfies Code); + yield _ts('default'); + yield _ts(['', 'template', slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0), disableAllFeatures({ __combineLastMappping: true })] satisfies Code); + } + yield _ts(['', 'template', (slotDir.arg ?? slotDir).loc.end.offset, presetInfos.diagnosticOnly]); if (hasProps) { - codes.push(')'); + yield _ts(')'); } - codes.push(';\n'); + yield _ts(';\n'); slotBlockVars.forEach(varName => { localVars.set(varName, (localVars.get(varName) ?? 0) + 1); @@ -942,11 +923,11 @@ export function generate( let prev: CompilerDOM.TemplateChildNode | undefined; for (const childNode of node.children) { - visitNode(childNode, parentEl, prev, componentCtxVar); + yield* generateAstNode(childNode, parentEl, prev, componentCtxVar); prev = childNode; } - resolveComment(); - generateAutoImportCompletionCode(); + yield* generateExpectErrorComment(); + yield* generateExtraAutoImport(); slotBlockVars.forEach(varName => { localVars.set(varName, localVars.get(varName)! - 1); @@ -956,49 +937,51 @@ export function generate( isStatic = slotDir.arg.isStatic; } if (isStatic && slotDir && !slotDir.arg) { - codes.push( - `${componentCtxVar}.slots!['`, - [ - '', - 'template', - slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0), - { completion: true }, - ], - `'/* empty slot name completion */]\n`, - ); + yield _ts(`${componentCtxVar}.slots!['`); + yield _ts([ + '', + 'template', + slotDir.loc.start.offset + ( + slotDir.loc.source.startsWith('#') + ? '#'.length : slotDir.loc.source.startsWith('v-slot:') + ? 'v-slot:'.length + : 0 + ), + disableAllFeatures({ completion: true }), + ]); + yield _ts(`'/* empty slot name completion */]\n`); } - codes.push(`}\n`); + yield _ts(`}\n`); } else { let prev: CompilerDOM.TemplateChildNode | undefined; for (const childNode of node.children) { - visitNode(childNode, parentEl, prev, componentCtxVar); + yield* generateAstNode(childNode, parentEl, prev, componentCtxVar); prev = childNode; } - resolveComment(); + yield* generateExpectErrorComment(); // fix https://github.com/vuejs/language-tools/issues/932 if (!hasSlotElements.has(node) && node.children.length) { - codes.push( - `(${componentCtxVar}.slots!)`, - ...createPropertyAccessCode([ - 'default', - 'template', - [ - node.children[0].loc.start.offset, - node.children[node.children.length - 1].loc.end.offset, - ], - { references: true }, - ]), - ';\n', - ); + yield _ts(`(${componentCtxVar}.slots!).`); + yield _ts(['', 'template', node.children[0].loc.start.offset, disableAllFeatures({ navigation: true })]); + yield _ts('default'); + yield _ts(['', 'template', node.children[node.children.length - 1].loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(';\n'); } } - codes.push(`}\n`); + if (defineComponentCtxVar && usedComponentCtxVars.has(defineComponentCtxVar)) { + yield _ts(`const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!;\n`); + } + if (usedComponentEventsVar) { + yield _ts(`let ${componentEventsVar}!: __VLS_NormalizeEmits;\n`); + } + + yield _ts(`}\n`); } - function generateEvents(node: CompilerDOM.ElementNode, componentVar: string, componentInstanceVar: string, componentCtxVar: string) { + function* generateEvents(node: CompilerDOM.ElementNode, componentVar: string, componentInstanceVar: string, eventsVar: string, used: () => void): Generator<_CodeAndStack> { for (const prop of node.props) { if ( @@ -1006,24 +989,26 @@ export function generate( && prop.name === 'on' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - const eventsVar = componentCtxVar2EmitEventsVar.get(componentCtxVar); + used(); const eventVar = `__VLS_${elementIndex++}`; - codes.push( - `let ${eventVar} = { '${prop.arg.loc.source}': `, - `__VLS_pickEvent(${eventsVar}['${prop.arg.loc.source}'], ({} as __VLS_FunctionalComponentProps)`, - ...createPropertyAccessCode([ - camelize('on-' + prop.arg.loc.source), // onClickOutside - 'template', - [prop.arg.loc.start.offset, prop.arg.loc.end.offset], + yield _ts(`let ${eventVar} = { '${prop.arg.loc.source}': `); + yield _ts(`__VLS_pickEvent(`); + yield _ts(`${eventsVar}['${prop.arg.loc.source}'], `); + yield _ts(`({} as __VLS_FunctionalComponentProps)`); + const startCode: Code = [ + '', + 'template', + prop.arg.loc.start.offset, + mergeFeatureSettings( + presetInfos.attrReference, { - ...capabilitiesPresets.attrReference, - rename: { + navigation: { // @click-outside -> onClickOutside - normalize(newName) { + resolveRenameNewName(newName) { return camelize('on-' + newName); }, // onClickOutside -> @click-outside - apply(newName) { + resolveRenameEditText(newName) { const hName = hyphenateAttr(newName); if (hyphenateAttr(newName).startsWith('on-')) { return camelize(hName.slice('on-'.length)); @@ -1032,146 +1017,159 @@ export function generate( }, }, }, - ]), - `) };\n`, - `${eventVar} = { `, - ); - if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) { - codes.push( - '[(', - ...createInterpolationCode( - prop.arg.loc.source.slice(1, -1), - prop.arg.loc, - prop.arg.loc.start.offset + 1, - capabilitiesPresets.all, - '', - '', - ), - ')!]', + ), + ]; + if (validTsVarReg.test(camelize(prop.arg.loc.source))) { + yield _ts(`.`); + yield _ts(startCode); + yield _ts(`on`); + yield* generateCamelized( + capitalize(prop.arg.loc.source), + prop.arg.loc.start.offset, + disableAllFeatures({ __combineLastMappping: true }), ); } else { - codes.push( - ...createObjectPropertyCode([ - prop.arg.loc.source, - 'template', - prop.arg.loc.start.offset, - capabilitiesPresets.event, - ], prop.arg.loc) + yield _ts(`[`); + yield _ts(startCode); + yield _ts(`'`); + yield _ts(['', 'template', prop.arg.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts('on'); + yield* generateCamelized( + capitalize(prop.arg.loc.source), + prop.arg.loc.start.offset, + disableAllFeatures({ __combineLastMappping: true }), ); + yield _ts(`'`); + yield _ts(['', 'template', prop.arg.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(`]`); } - codes.push(`: `); - appendExpressionNode(prop); - codes.push(` };\n`); - } - else if ( - prop.type === CompilerDOM.NodeTypes.DIRECTIVE - && prop.name === 'on' + yield _ts(`) };\n`); + yield _ts(`${eventVar} = { `); + if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) { + yield _ts('[('); + yield* generateInterpolation( + prop.arg.loc.source.slice(1, -1), + prop.arg.loc, + prop.arg.loc.start.offset + 1, + presetInfos.all, + '', + '', + ); + yield _ts(')!]'); + } + else { + yield* generateObjectProperty( + prop.arg.loc.source, + prop.arg.loc.start.offset, + presetInfos.event, + prop.arg.loc + ); + } + yield _ts(`: `); + yield* appendExpressionNode(prop); + yield _ts(` };\n`); + } + else if ( + prop.type === CompilerDOM.NodeTypes.DIRECTIVE + && prop.name === 'on' && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { // for vue 2 nameless event // https://github.com/johnsoncodehk/vue-tsc/issues/67 - codes.push( - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - capabilitiesPresets.all, - '$event => {(', - ')}', - ), - ';\n', + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + presetInfos.all, + '$event => {(', + ')}', ); - formatCodes.push( - ...createFormatCode( - prop.exp.content, - prop.exp.loc.start.offset, - formatBrackets.normal, - ), + yield _ts(';\n'); + yield* generateTsFormat( + prop.exp.content, + prop.exp.loc.start.offset, + formatBrackets.normal, ); } + } + } - function appendExpressionNode(prop: CompilerDOM.DirectiveNode) { - if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { + function* appendExpressionNode(prop: CompilerDOM.DirectiveNode): Generator<_CodeAndStack> { + if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - const ast = createTsAst(prop.exp, prop.exp.content); - let isCompoundExpression = true; + const ast = createTsAst(prop.exp, prop.exp.content); + let isCompoundExpression = true; - if (ast.getChildCount() === 2) { // with EOF - ast.forEachChild(child_1 => { - if (ts.isExpressionStatement(child_1)) { - child_1.forEachChild(child_2 => { - if (ts.isArrowFunction(child_2)) { - isCompoundExpression = false; - } - else if (ts.isIdentifier(child_2)) { - isCompoundExpression = false; - } - }); + if (ast.statements.length === 1) { + ts.forEachChild(ast, child_1 => { + if (ts.isExpressionStatement(child_1)) { + ts.forEachChild(child_1, child_2 => { + if (ts.isArrowFunction(child_2)) { + isCompoundExpression = false; } - else if (ts.isFunctionDeclaration(child_1)) { + else if (ts.isIdentifier(child_2)) { isCompoundExpression = false; } }); } + else if (ts.isFunctionDeclaration(child_1)) { + isCompoundExpression = false; + } + }); + } - let prefix = '('; - let suffix = ')'; - let isFirstMapping = true; - - if (isCompoundExpression) { - - codes.push('$event => {\n'); - localVars.set('$event', (localVars.get('$event') ?? 0) + 1); + let prefix = '('; + let suffix = ')'; + let isFirstMapping = true; - prefix = ''; - suffix = ''; - for (const blockCondition of blockConditions) { - prefix += `if (!(${blockCondition})) return;\n`; - } - } + if (isCompoundExpression) { - codes.push( - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - () => { - if (isCompoundExpression && isFirstMapping) { - isFirstMapping = false; - return capabilitiesPresets.allWithHiddenParam; - } - return capabilitiesPresets.all; - }, - prefix, - suffix, - ) - ); + yield _ts('$event => {\n'); + localVars.set('$event', (localVars.get('$event') ?? 0) + 1); - if (isCompoundExpression) { - localVars.set('$event', localVars.get('$event')! - 1); + prefix = ''; + suffix = ''; + for (const blockCondition of blockConditions) { + prefix += `if (!(${blockCondition})) return;\n`; + } + } - codes.push(';\n'); - generateAutoImportCompletionCode(); - codes.push('}\n'); + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + () => { + if (isCompoundExpression && isFirstMapping) { + isFirstMapping = false; + return presetInfos.allWithHiddenParam; } + return presetInfos.all; + }, + prefix, + suffix, + ); - formatCodes.push( - ...createFormatCode( - prop.exp.content, - prop.exp.loc.start.offset, - isCompoundExpression ? formatBrackets.event : formatBrackets.normal, - ), - ); - } - else { - codes.push(`() => {}`); - } + if (isCompoundExpression) { + localVars.set('$event', localVars.get('$event')! - 1); + + yield _ts(';\n'); + yield* generateExtraAutoImport(); + yield _ts('}\n'); } + + yield* generateTsFormat( + prop.exp.content, + prop.exp.loc.start.offset, + isCompoundExpression ? formatBrackets.event : formatBrackets.normal, + ); + } + else { + yield _ts(`() => {}`); } } - function createPropsCode(node: CompilerDOM.ElementNode, props: CompilerDOM.ElementNode['props'], mode: 'normal' | 'extraReferences', propsFailedExps?: CompilerDOM.SimpleExpressionNode[]): Code[] { + function* generateProps(node: CompilerDOM.ElementNode, props: CompilerDOM.ElementNode['props'], mode: 'normal' | 'extraReferences', propsFailedExps?: CompilerDOM.SimpleExpressionNode[]): Generator<_CodeAndStack> { let styleAttrNum = 0; let classAttrNum = 0; @@ -1187,41 +1185,27 @@ export function generate( classAttrNum++; } - const codes: Code[] = []; - - let caps_all: FileRangeCapabilities = capabilitiesPresets.all; - let caps_diagnosticOnly: FileRangeCapabilities = capabilitiesPresets.diagnosticOnly; - let caps_attr: FileRangeCapabilities = capabilitiesPresets.attr; + let caps_all: VueCodeInformation = presetInfos.all; + let caps_diagnosticOnly: VueCodeInformation = presetInfos.diagnosticOnly; + let caps_attr: VueCodeInformation = presetInfos.attr; if (mode === 'extraReferences') { - caps_all = { - references: caps_all.references, - rename: caps_all.rename, - }; - caps_diagnosticOnly = { - references: caps_diagnosticOnly.references, - rename: caps_diagnosticOnly.rename, - }; - caps_attr = { - references: caps_attr.references, - rename: caps_attr.rename, - }; - } - - codes.push(`...{ `); + caps_all = disableAllFeatures({ navigation: caps_all.navigation }); + caps_diagnosticOnly = disableAllFeatures({ navigation: caps_diagnosticOnly.navigation }); + caps_attr = disableAllFeatures({ navigation: caps_attr.navigation }); + } + + yield _ts(`...{ `); for (const prop of props) { if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'on' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - codes.push( - ...createObjectPropertyCode(camelize('on-' + prop.arg.loc.source)), - ': {} as any, ', - ); + yield _ts(`'${camelize('on-' + prop.arg.loc.source)}': {} as any, `); } } - codes.push(`}, `); + yield _ts(`}, `); const canCamelize = !nativeTags.has(node.tag) || node.tagType === CompilerDOM.ElementTypes.COMPONENT; @@ -1233,7 +1217,7 @@ export function generate( && (!prop.exp || prop.exp.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) ) { - let attrNameText = + let propName = prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ? prop.arg.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY ? prop.arg.content @@ -1241,15 +1225,15 @@ export function generate( : getModelValuePropName(node, vueCompilerOptions.target, vueCompilerOptions); if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) { - attrNameText = attrNameText?.substring(1); + propName = propName?.substring(1); } if ( - attrNameText === undefined - || vueCompilerOptions.dataAttributes.some(pattern => minimatch(attrNameText!, pattern)) - || (attrNameText === 'style' && ++styleAttrNum >= 2) - || (attrNameText === 'class' && ++classAttrNum >= 2) - || (attrNameText === 'name' && node.tag === 'slot') // #2308 + propName === undefined + || vueCompilerOptions.dataAttributes.some(pattern => minimatch(propName!, pattern)) + || (propName === 'style' && ++styleAttrNum >= 2) + || (propName === 'class' && ++classAttrNum >= 2) + || (propName === 'name' && node.tag === 'slot') // #2308 ) { if (prop.exp && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) { propsFailedExps?.push(prop.exp); @@ -1257,161 +1241,99 @@ export function generate( continue; } - let camelized = false; - - if ( - canCamelize + const shouldCamelize = canCamelize && (!prop.arg || (prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)) // isStatic - && hyphenateAttr(attrNameText) === attrNameText - && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern)) - ) { - attrNameText = camelize(attrNameText); - camelized = true; - } - - // camelize name - codes.push([ - '', - 'template', - prop.loc.start.offset, - caps_diagnosticOnly, - ]); - if (!prop.arg) { - codes.push( - ...createObjectPropertyCode([ - attrNameText, - 'template', - [prop.loc.start.offset, prop.loc.start.offset + prop.loc.source.indexOf('=')], + && hyphenateAttr(propName) === propName + && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(propName!, pattern)); + + yield _ts(['', 'template', prop.loc.start.offset, caps_diagnosticOnly]); + yield* generateObjectProperty( + propName, + prop.arg + ? prop.arg.loc.start.offset + : prop.loc.start.offset, + prop.arg + ? mergeFeatureSettings( caps_attr, - ], (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {})), - ); - } - else if (prop.exp?.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY) { - codes.push( - ...createObjectPropertyCode([ - attrNameText, - 'template', - [prop.arg.loc.start.offset, prop.arg.loc.start.offset + attrNameText.length], // patch style attr, { - ...caps_attr, - rename: { - normalize: camelize, - apply: camelized ? hyphenateAttr : noEditApply, - }, + navigation: caps_attr.navigation ? { + resolveRenameNewName: camelize, + resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined, + } : undefined, }, - ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})), - ); - } - else { - codes.push( - ...createObjectPropertyCode([ - attrNameText, - 'template', - [prop.arg.loc.start.offset, prop.arg.loc.end.offset], - { - ...caps_attr, - rename: { - normalize: camelize, - apply: camelized ? hyphenateAttr : noEditApply, - }, - }, - ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})), - ); - } - codes.push(': ('); + ) + : caps_attr, + (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {}), + shouldCamelize, + ); + yield _ts(': ('); if (prop.exp && !(prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY)) { // style='z-index: 2' will compile to {'z-index':'2'} - codes.push( - ...createInterpolationCode( - prop.exp.loc.source, - prop.exp.loc, - prop.exp.loc.start.offset, - caps_all, - '(', - ')', - ), + yield* generateInterpolation( + prop.exp.loc.source, + prop.exp.loc, + prop.exp.loc.start.offset, + caps_all, + '(', + ')', ); if (mode === 'normal') { - formatCodes.push( - ...createFormatCode( - prop.exp.loc.source, - prop.exp.loc.start.offset, - formatBrackets.normal, - ), + yield* generateTsFormat( + prop.exp.loc.source, + prop.exp.loc.start.offset, + formatBrackets.normal, ); } } else { - codes.push('{}'); + yield _ts('{}'); } - codes.push(')'); - codes.push([ + yield _ts(')'); + yield _ts([ '', 'template', prop.loc.end.offset, caps_diagnosticOnly, ]); - codes.push(', '); + yield _ts(', '); } else if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE) { - let attrNameText = prop.name; - if ( - vueCompilerOptions.dataAttributes.some(pattern => minimatch(attrNameText!, pattern)) - || (attrNameText === 'style' && ++styleAttrNum >= 2) - || (attrNameText === 'class' && ++classAttrNum >= 2) - || (attrNameText === 'name' && node.tag === 'slot') // #2308 - ) { - continue; - } + vueCompilerOptions.dataAttributes.some(pattern => minimatch(prop.name, pattern)) + || (prop.name === 'style' && ++styleAttrNum >= 2) + || (prop.name === 'class' && ++classAttrNum >= 2) + || (prop.name === 'name' && node.tag === 'slot') // #2308 + ) continue; - let camelized = false; - - if ( - canCamelize + const shouldCamelize = canCamelize && hyphenateAttr(prop.name) === prop.name - && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern)) - ) { - attrNameText = camelize(prop.name); - camelized = true; - } + && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(prop.name, pattern)); - // camelize name - codes.push([ - '', - 'template', + yield _ts(['', 'template', prop.loc.start.offset, caps_diagnosticOnly]); + yield* generateObjectProperty( + prop.name, prop.loc.start.offset, - caps_diagnosticOnly, - ]); - codes.push( - ...createObjectPropertyCode([ - attrNameText, - 'template', - [prop.loc.start.offset, prop.loc.start.offset + prop.name.length], - { - ...caps_attr, - rename: { - normalize: camelize, - apply: camelized ? hyphenateAttr : noEditApply, - }, - }, - ], (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {})) + shouldCamelize + ? mergeFeatureSettings(caps_attr, { + navigation: caps_attr.navigation ? { + resolveRenameNewName: camelize, + resolveRenameEditText: hyphenateAttr, + } : undefined, + }) + : caps_attr, + (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {}), + shouldCamelize, ); - codes.push(': ('); + yield _ts(': ('); if (prop.value) { - generateAttrValue(prop.value); + yield* generateAttrValue(prop.value, caps_all); } else { - codes.push('true'); + yield _ts('true'); } - codes.push(')'); - codes.push([ - '', - 'template', - prop.loc.end.offset, - caps_diagnosticOnly, - ]); - codes.push(', '); + yield _ts(')'); + yield _ts(['', 'template', prop.loc.end.offset, caps_diagnosticOnly]); + yield _ts(', '); } else if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE @@ -1419,27 +1341,23 @@ export function generate( && !prop.arg && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - codes.push( - ['', 'template', prop.exp.loc.start.offset, capabilitiesPresets.diagnosticOnly], - '...', - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - caps_all, - '(', - ')', - ), - ['', 'template', prop.exp.loc.end.offset, capabilitiesPresets.diagnosticOnly], - ', ', + yield _ts(['', 'template', prop.exp.loc.start.offset, presetInfos.diagnosticOnly]); + yield _ts('...'); + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + caps_all, + '(', + ')', ); + yield _ts(['', 'template', prop.exp.loc.end.offset, presetInfos.diagnosticOnly]); + yield _ts(', '); if (mode === 'normal') { - formatCodes.push( - ...createFormatCode( - prop.exp.content, - prop.exp.loc.start.offset, - formatBrackets.normal, - ), + yield* generateTsFormat( + prop.exp.content, + prop.exp.loc.start.offset, + formatBrackets.normal, ); } } @@ -1448,34 +1366,9 @@ export function generate( // tsCodeGen.addText("/* " + [prop.type, prop.name, prop.arg?.loc.source, prop.exp?.loc.source, prop.loc.source].join(", ") + " */ "); } } - - return codes; - - function generateAttrValue(attrNode: CompilerDOM.TextNode) { - const char = attrNode.loc.source.startsWith("'") ? "'" : '"'; - codes.push(char); - let start = attrNode.loc.start.offset; - let end = attrNode.loc.end.offset; - let content = attrNode.loc.source; - if ( - (content.startsWith('"') && content.endsWith('"')) - || (content.startsWith("'") && content.endsWith("'")) - ) { - start++; - end--; - content = content.slice(1, -1); - } - codes.push([ - toUnicodeIfNeed(content), - 'template', - [start, end], - caps_all, - ]); - codes.push(char); - } } - function generateInlineCss(props: CompilerDOM.ElementNode['props']) { + function* generateInlineCss(props: CompilerDOM.ElementNode['props']): Generator<_CodeAndStack> { for (const prop of props) { if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE @@ -1490,19 +1383,22 @@ export function generate( const end = prop.arg.loc.source.lastIndexOf(endCrt); const content = prop.arg.loc.source.substring(start, end); - cssCodes.push(`x { `); - cssCodes.push([ + yield _inlineCss(`x { `); + yield _inlineCss([ content, 'template', prop.arg.loc.start.offset + start, - capabilitiesPresets.all, + enableAllFeatures({ + format: false, + structure: false, + }), ]); - cssCodes.push(` }\n`); + yield _inlineCss(` }\n`); } } } - function generateDirectives(node: CompilerDOM.ElementNode) { + function* generateDirectives(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> { for (const prop of node.props) { if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE @@ -1516,120 +1412,101 @@ export function generate( accessedGlobalVariables.add(camelize('v-' + prop.name)); if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && !prop.arg.isStatic) { - codes.push( - ...createInterpolationCode( - prop.arg.content, - prop.arg.loc, - prop.arg.loc.start.offset + prop.arg.loc.source.indexOf(prop.arg.content), - capabilitiesPresets.all, - '(', - ')', - ), - ';\n', + yield* generateInterpolation( + prop.arg.content, + prop.arg.loc, + prop.arg.loc.start.offset + prop.arg.loc.source.indexOf(prop.arg.content), + presetInfos.all, + '(', + ')', ); - formatCodes.push( - ...createFormatCode( - prop.arg.content, - prop.arg.loc.start.offset, - formatBrackets.normal, - ), + yield _ts(';\n'); + yield* generateTsFormat( + prop.arg.content, + prop.arg.loc.start.offset, + formatBrackets.normal, ); } - codes.push( - [ - '', - 'template', - prop.loc.start.offset, - capabilitiesPresets.diagnosticOnly, - ], - `__VLS_directiveFunction(__VLS_ctx.`, - [ - camelize('v-' + prop.name), - 'template', - [prop.loc.start.offset, prop.loc.start.offset + 'v-'.length + prop.name.length], + yield _ts(['', 'template', prop.loc.start.offset, presetInfos.diagnosticOnly]); + yield _ts(`__VLS_directiveFunction(__VLS_ctx.`); + yield* generateCamelized( + 'v-' + prop.name, + prop.loc.start.offset, + mergeFeatureSettings( + presetInfos.noDiagnostics, { - ...capabilitiesPresets.noDiagnostic, completion: { // fix https://github.com/vuejs/language-tools/issues/1905 - additional: true, + isAdditional: true, }, - rename: { - normalize: camelize, - apply: getPropRenameApply(prop.name), + navigation: { + resolveRenameNewName: camelize, + resolveRenameEditText: getPropRenameApply(prop.name), }, }, - ], - ')', - '(', + ), ); + yield _ts(')'); + yield _ts('('); + if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - codes.push( - ['', 'template', prop.exp.loc.start.offset, capabilitiesPresets.diagnosticOnly], - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - capabilitiesPresets.all, - '(', - ')', - ), - ['', 'template', prop.exp.loc.end.offset, capabilitiesPresets.diagnosticOnly], + yield _ts(['', 'template', prop.exp.loc.start.offset, presetInfos.diagnosticOnly]); + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + presetInfos.all, + '(', + ')', ); - formatCodes.push( - ...createFormatCode( - prop.exp.content, - prop.exp.loc.start.offset, - formatBrackets.normal, - ), + yield _ts(['', 'template', prop.exp.loc.end.offset, presetInfos.diagnosticOnly]); + yield* generateTsFormat( + prop.exp.content, + prop.exp.loc.start.offset, + formatBrackets.normal, ); } else { - codes.push('undefined'); + yield _ts('undefined'); } - codes.push( - ')', - ['', 'template', prop.loc.end.offset, capabilitiesPresets.diagnosticOnly], - ';\n', - ); + yield _ts(')'); + yield _ts(['', 'template', prop.loc.end.offset, presetInfos.diagnosticOnly]); + yield _ts(';\n'); } } } - function generateElReferences(node: CompilerDOM.ElementNode) { + function* generateReferencesForElements(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> { for (const prop of node.props) { if ( prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name === 'ref' && prop.value ) { - codes.push( - '// @ts-ignore\n', - ...createInterpolationCode( - prop.value.content, - prop.value.loc, - prop.value.loc.start.offset + 1, - capabilitiesPresets.refAttr, - '(', - ')', - ), - ';\n', + yield _ts('// @ts-ignore\n'); + yield* generateInterpolation( + prop.value.content, + prop.value.loc, + prop.value.loc.start.offset + 1, + presetInfos.refAttr, + '(', + ')', ); + yield _ts(';\n'); } } } - function generateClassScoped(node: CompilerDOM.ElementNode) { + function* generateReferencesForScopedCssClasses(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> { for (const prop of node.props) { if ( prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name === 'class' && prop.value ) { - let startOffset = prop.value.loc.start.offset; let tempClassName = ''; - for (const char of (prop.value.loc.source + ' ')) { if (char.trim() === '' || char === '"' || char === "'") { if (tempClassName !== '') { @@ -1650,40 +1527,38 @@ export function generate( && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.content === 'class' ) { - codes.push(`__VLS_styleScopedClasses = (`); - codes.push([ + yield _ts(`__VLS_styleScopedClasses = (`); + yield _ts([ prop.exp.content, 'template', prop.exp.loc.start.offset, - capabilitiesPresets.scopedClassName, + presetInfos.scopedClassName, ]); - codes.push(`);\n`); + yield _ts(`);\n`); } } } - function generateSlot(node: CompilerDOM.ElementNode, startTagOffset: number) { + function* generateSlot(node: CompilerDOM.ElementNode, startTagOffset: number): Generator<_CodeAndStack> { const varSlot = `__VLS_${elementIndex++}`; const slotNameExpNode = getSlotNameExpNode(); if (hasScriptSetupSlots) { - codes.push( - '__VLS_normalizeSlot(', - ['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly], - `${slotsAssignName ?? '__VLS_slots'}[`, - ['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly], - slotNameExpNode?.content ?? `('${getSlotName()}' as const)`, - ['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly], - ']', - ['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly], - ')?.(', - ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly], - '{\n', - ); + yield _ts('__VLS_normalizeSlot('); + yield _ts(['', 'template', node.loc.start.offset, presetInfos.diagnosticOnly]); + yield _ts(`${slotsAssignName ?? '__VLS_slots'}[`); + yield _ts(['', 'template', node.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(slotNameExpNode?.content ?? `('${getSlotName()?.[0] ?? 'default'}' as const)`); + yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(']'); + yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(')?.('); + yield _ts(['', 'template', startTagOffset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts('{\n'); } else { - codes.push(`var ${varSlot} = {\n`); + yield _ts(`var ${varSlot} = {\n`); } for (const prop of node.props) { if ( @@ -1691,18 +1566,16 @@ export function generate( && !prop.arg && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - codes.push( - '...', - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - capabilitiesPresets.attrReference, - '(', - ')', - ), - ',\n', + yield _ts('...'); + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + presetInfos.attrReference, + '(', + ')', ); + yield _ts(',\n'); } else if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE @@ -1710,59 +1583,64 @@ export function generate( && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.content !== 'name' ) { - codes.push( - ...createObjectPropertyCode([ - prop.arg.content, - 'template', - [prop.arg.loc.start.offset, prop.arg.loc.end.offset], + yield* generateObjectProperty( + prop.arg.content, + prop.arg.loc.start.offset, + mergeFeatureSettings( + presetInfos.slotProp, { - ...capabilitiesPresets.slotProp, - rename: { - normalize: camelize, - apply: getPropRenameApply(prop.arg.content), + navigation: { + resolveRenameNewName: camelize, + resolveRenameEditText: getPropRenameApply(prop.arg.content), }, }, - ], prop.arg.loc), - ': ', - ...createInterpolationCode( - prop.exp.content, - prop.exp.loc, - prop.exp.loc.start.offset, - capabilitiesPresets.attrReference, - '(', - ')', ), - ',\n', + prop.arg.loc ); + yield _ts(': '); + yield* generateInterpolation( + prop.exp.content, + prop.exp.loc, + prop.exp.loc.start.offset, + presetInfos.attrReference, + '(', + ')', + ); + yield _ts(',\n'); } else if ( prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name !== 'name' // slot name ) { - codes.push( - ...createObjectPropertyCode([ - prop.name, - 'template', - prop.loc.start.offset, + yield* generateObjectProperty( + prop.name, + prop.loc.start.offset, + mergeFeatureSettings( + presetInfos.attr, { - ...capabilitiesPresets.attr, - rename: { - normalize: camelize, - apply: getPropRenameApply(prop.name), + navigation: { + resolveRenameNewName: camelize, + resolveRenameEditText: getPropRenameApply(prop.name), }, }, - ], prop.loc), - ': (', - prop.value !== undefined ? `"${toUnicodeIfNeed(prop.value.content)}"` : 'true', - '),\n', + ), + prop.loc + ); + yield _ts(': ('); + yield _ts( + prop.value !== undefined + ? `"${needToUnicode(prop.value.content) ? toUnicode(prop.value.content) : prop.value.content}"` + : 'true' ); + yield _ts('),\n'); } } - codes.push( - '}', - hasScriptSetupSlots ? ['', 'template', startTagOffset + node.tag.length, capabilitiesPresets.diagnosticOnly] : '', - hasScriptSetupSlots ? `);\n` : `;\n` - ); + yield _ts('}'); + if (hasScriptSetupSlots) { + yield _ts(['', 'template', startTagOffset + node.tag.length, presetInfos.diagnosticOnly]); + yield _ts(`)`); + } + yield _ts(`;\n`); if (hasScriptSetupSlots) { return; @@ -1770,31 +1648,31 @@ export function generate( if (slotNameExpNode) { const varSlotExp = `__VLS_${elementIndex++}`; - codes.push(`var ${varSlotExp} = `); + yield _ts(`var ${varSlotExp} = `); if (typeof slotNameExpNode === 'string') { - codes.push(slotNameExpNode); + yield _ts(slotNameExpNode); } else { - codes.push( - ...createInterpolationCode( - slotNameExpNode.content, - slotNameExpNode, - undefined, undefined, - '(', - ')', - ), + yield* generateInterpolation( + slotNameExpNode.content, + slotNameExpNode, + undefined, undefined, + '(', + ')', ); } - codes.push(` as const;\n`); + yield _ts(` as const;\n`); slotExps.set(varSlotExp, { varName: varSlot, }); } else { const slotName = getSlotName(); - slots.set(slotName, { + slots.set(slotName?.[0] ?? 'default', { + name: slotName?.[0], + loc: slotName?.[1], + tagRange: [startTagOffset, startTagOffset + node.tag.length], varName: varSlot, - loc: [startTagOffset, startTagOffset + node.tag.length], nodeLoc: node.loc, }); } @@ -1803,11 +1681,13 @@ export function generate( for (const prop2 of node.props) { if (prop2.name === 'name' && prop2.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop2.value) { if (prop2.value.content) { - return prop2.value.content; + return [ + prop2.value.content, + prop2.loc.start.offset + prop2.loc.source.indexOf(prop2.value.content, prop2.name.length), + ] as const; } } } - return 'default'; } function getSlotNameExpNode() { for (const prop2 of node.props) { @@ -1820,100 +1700,173 @@ export function generate( } } - function generateAutoImportCompletionCode() { + function* generateExtraAutoImport(): Generator<_CodeAndStack> { if (!tempVars.length) return; - codes.push('// @ts-ignore\n'); // #2304 - codes.push('['); + yield _ts('// @ts-ignore\n'); // #2304 + yield _ts('['); for (const _vars of tempVars) { for (const v of _vars) { - codes.push([v.text, 'template', v.offset, { completion: { additional: true } }]); - codes.push(','); + yield _ts([ + v.text, + 'template', + v.offset, + disableAllFeatures({ completion: { isAdditional: true }, }), + ]); + yield _ts(','); } } - codes.push('];\n'); + yield _ts('];\n'); tempVars.length = 0; } - // functional like + function* generateAttrValue(attrNode: CompilerDOM.TextNode, info: VueCodeInformation): Generator<_CodeAndStack> { + const char = attrNode.loc.source.startsWith("'") ? "'" : '"'; + yield _ts(char); + let start = attrNode.loc.start.offset; + let end = attrNode.loc.end.offset; + let content = attrNode.loc.source; + if ( + (content.startsWith('"') && content.endsWith('"')) + || (content.startsWith("'") && content.endsWith("'")) + ) { + start++; + end--; + content = content.slice(1, -1); + } + if (needToUnicode(content)) { + yield _ts(['', 'template', start, info]); + yield _ts(toUnicode(content)); + yield _ts(['', 'template', end, disableAllFeatures({ __combineLastMappping: true })]); + } + else { + yield _ts([content, 'template', start, info]); + } + yield _ts(char); + } - function createFormatCode(mapCode: string, sourceOffset: number, formatWrapper: [string, string]): Code[] { - return [ - formatWrapper[0], - [mapCode, 'template', sourceOffset, { completion: true /* fix vue-autoinsert-parentheses not working */ }], - formatWrapper[1], - '\n', - ]; + function* generateCamelized(code: string, offset: number, info: VueCodeInformation): Generator<_CodeAndStack> { + const parts = code.split('-'); + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (part !== '') { + yield _ts([ + i === 0 + ? part + : capitalize(part), + 'template', + offset, + i === 0 + ? info + : disableAllFeatures({ __combineLastMappping: true }), + ]); + } + offset += part.length + 1; + } } - function createObjectPropertyCode(a: Code, astHolder?: any): Code[] { - const aStr = typeof a === 'string' ? a : a[0]; - if (validTsVarReg.test(aStr)) { - return [a]; - } - else if (aStr.startsWith('[') && aStr.endsWith(']') && astHolder) { - const range = typeof a === 'object' ? a[2] : undefined; - const data = typeof a === 'object' ? a[3] : undefined; - return createInterpolationCode( - aStr, - astHolder, - range && typeof range === 'object' ? range[0] : range, - data, - '', - '', - ); + function* generateTsFormat(code: string, offset: number, formatWrapper: [string, string]): Generator<_CodeAndStack> { + yield _tsFormat(formatWrapper[0]); + yield _tsFormat([ + code, + 'template', + offset, + mergeFeatureSettings( + presetInfos.disabledAll, + { + format: true, + // autoInserts: true, // TODO: support vue-autoinsert-parentheses + }, + ), + ]); + yield _tsFormat(formatWrapper[1]); + yield _tsFormat('\n'); + } + + function* generateObjectProperty(code: string, offset: number, info: VueCodeInformation, astHolder?: any, shouldCamelize = false): Generator<_CodeAndStack> { + if (code.startsWith('[') && code.endsWith(']') && astHolder) { + yield* generateInterpolation(code, astHolder, offset, info, '', ''); + } + else if (shouldCamelize) { + if (validTsVarReg.test(camelize(code))) { + yield* generateCamelized(code, offset, info); + } + else { + yield _ts(['', 'template', offset, info]); + yield _ts('"'); + yield* generateCamelized(code, offset, disableAllFeatures({ __combineLastMappping: true })); + yield _ts('"'); + yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]); + } } else { - return createStringLiteralKeyCode(a); + if (validTsVarReg.test(code)) { + yield _ts([code, 'template', offset, info]); + } + else { + yield* generateStringLiteralKey(code, offset, info); + } } } - function createInterpolationCode( + function* generateInterpolation( _code: string, astHolder: any, start: number | undefined, - data: FileRangeCapabilities | (() => FileRangeCapabilities) | undefined, + data: VueCodeInformation | (() => VueCodeInformation) | undefined, prefix: string, suffix: string, - ): Code[] { + ): Generator<_CodeAndStack> { const code = prefix + _code + suffix; const ast = createTsAst(astHolder, code); - const codes: Code[] = []; - const vars = walkInterpolationFragment(ts, code, ast, (frag, fragOffset, isJustForErrorMapping) => { - if (fragOffset === undefined) { - codes.push(frag); + const vars: { + text: string, + isShorthand: boolean, + offset: number, + }[] = []; + for (let [section, offset, onlyError] of eachInterpolationSegment( + ts, + code, + ast, + localVars, + accessedGlobalVariables, + vueCompilerOptions, + vars, + )) { + if (offset === undefined) { + yield _ts(section); } else { - fragOffset -= prefix.length; + offset -= prefix.length; let addSuffix = ''; - const overLength = fragOffset + frag.length - _code.length; + const overLength = offset + section.length - _code.length; if (overLength > 0) { - addSuffix = frag.substring(frag.length - overLength); - frag = frag.substring(0, frag.length - overLength); + addSuffix = section.substring(section.length - overLength); + section = section.substring(0, section.length - overLength); } - if (fragOffset < 0) { - codes.push(frag.substring(0, -fragOffset)); - frag = frag.substring(-fragOffset); - fragOffset = 0; + if (offset < 0) { + yield _ts(section.substring(0, -offset)); + section = section.substring(-offset); + offset = 0; } if (start !== undefined && data !== undefined) { - codes.push([ - frag, + yield _ts([ + section, 'template', - start + fragOffset, - isJustForErrorMapping - ? capabilitiesPresets.diagnosticOnly + start + offset, + onlyError + ? presetInfos.diagnosticOnly : typeof data === 'function' ? data() : data, ]); } else { - codes.push(frag); + yield _ts(section); } - codes.push(addSuffix); + yield _ts(addSuffix); } - }, localVars, accessedGlobalVariables, vueCompilerOptions); + } if (start !== undefined) { for (const v of vars) { v.offset = start + v.offset - prefix.length; @@ -1922,72 +1875,77 @@ export function generate( tempVars.push(vars); } } - return codes; } - function createTsAst(astHolder: any, text: string) { - if (astHolder.__volar_ast_text !== text) { - astHolder.__volar_ast_text = text; - astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, ts.ScriptTarget.ESNext); + function* generatePropertyAccess(code: string, offset?: number, info?: VueCodeInformation, astHolder?: any): Generator<_CodeAndStack> { + if (!compilerOptions.noPropertyAccessFromIndexSignature && validTsVarReg.test(code)) { + yield _ts('.'); + yield _ts(offset !== undefined && info + ? [code, 'template', offset, info] + : code); + } + else if (code.startsWith('[') && code.endsWith(']')) { + yield* generateInterpolation(code, astHolder, offset, info, '', ''); + } + else { + yield _ts('['); + yield* generateStringLiteralKey(code, offset, info); + yield _ts(']'); } - return astHolder.__volar_ast as ts.SourceFile; } - function createPropertyAccessCode(a: Code, astHolder?: any): Code[] { - const aStr = typeof a === 'string' ? a : a[0]; - if (!compilerOptions.noPropertyAccessFromIndexSignature && validTsVarReg.test(aStr)) { - return ['.', a]; - } - else if (aStr.startsWith('[') && aStr.endsWith(']')) { - if (typeof a === 'string' || !astHolder) { - return [a]; - } - else { - return createInterpolationCode( - a[0], - astHolder, - typeof a[2] === 'number' ? a[2] : a[2][0], - a[3], - '', - '', - ); - } + function* generateStringLiteralKey(code: string, offset?: number, info?: VueCodeInformation): Generator<_CodeAndStack> { + if (offset === undefined || !info) { + yield _ts(`"${code}"`); } else { - return ['[', ...createStringLiteralKeyCode(a), ']']; + yield _ts(['', 'template', offset, info]); + yield _ts('"'); + yield _ts([code, 'template', offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts('"'); + yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]); } } - function createStringLiteralKeyCode(a: Code): Code[] { - let codes: Code[] = ['"', a, '"']; - if (typeof a === 'object') { - const start = typeof a[2] === 'number' ? a[2] : a[2][0]; - const end = typeof a[2] === 'number' ? a[2] : a[2][1]; - codes = [ - ['', 'template', start, a[3]], - ...codes, - ['', 'template', end, a[3]], - ]; - } - return codes; + function createTsAst(astHolder: any, text: string) { + if (astHolder.__volar_ast_text !== text) { + astHolder.__volar_ast_text = text; + astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, 99 satisfies ts.ScriptTarget.ESNext); + } + return astHolder.__volar_ast as ts.SourceFile; } -}; +} + +function getCanonicalComponentName(tagText: string) { + return validTsVarReg.test(tagText) + ? tagText + : capitalize(camelize(tagText.replace(colonReg, '-'))); +} + +function getPossibleOriginalComponentNames(tagText: string) { + return [...new Set([ + // order is important: https://github.com/vuejs/language-tools/issues/2010 + capitalize(camelize(tagText)), + camelize(tagText), + tagText, + ])]; +} -export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode, cb: (node: CompilerDOM.ElementNode) => void) { +export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator { if (node.type === CompilerDOM.NodeTypes.ROOT) { for (const child of node.children) { - walkElementNodes(child, cb); + yield* eachElementNode(child); } } else if (node.type === CompilerDOM.NodeTypes.ELEMENT) { const patchForNode = getVForNode(node); if (patchForNode) { - walkElementNodes(patchForNode, cb); + yield* eachElementNode(patchForNode); } else { - cb(node); + yield node; for (const child of node.children) { - walkElementNodes(child, cb); + yield* eachElementNode(child); } } } @@ -1996,23 +1954,20 @@ export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.Templa for (let i = 0; i < node.branches.length; i++) { const branch = node.branches[i]; for (const childNode of branch.children) { - walkElementNodes(childNode, cb); + yield* eachElementNode(childNode); } } } else if (node.type === CompilerDOM.NodeTypes.FOR) { // v-for for (const child of node.children) { - walkElementNodes(child, cb); + yield* eachElementNode(child); } } } -function toUnicodeIfNeed(str: string) { - if (str.indexOf('\\') === -1 && str.indexOf('\n') === -1) { - return str; - } - return toUnicode(str); +function needToUnicode(str: string) { + return str.indexOf('\\') >= 0 || str.indexOf('\n') >= 0; } function toUnicode(str: string) { @@ -2030,15 +1985,11 @@ function camelizeComponentName(newName: string) { } function getTagRenameApply(oldName: string) { - return oldName === hyphenateTag(oldName) ? hyphenateTag : noEditApply; + return oldName === hyphenateTag(oldName) ? hyphenateTag : undefined; } function getPropRenameApply(oldName: string) { - return oldName === hyphenateAttr(oldName) ? hyphenateAttr : noEditApply; -} - -function noEditApply(n: string) { - return n; + return oldName === hyphenateAttr(oldName) ? hyphenateAttr : undefined; } function getModelValuePropName(node: CompilerDOM.ElementNode, vueVersion: number, vueCompilerOptions: VueCompilerOptions) { diff --git a/packages/language-core/src/generators/utils.ts b/packages/language-core/src/generators/utils.ts new file mode 100644 index 0000000000..3556496cbc --- /dev/null +++ b/packages/language-core/src/generators/utils.ts @@ -0,0 +1,55 @@ +import { Code, CodeAndStack, VueCodeInformation } from '../types'; + +export function withStack(code: Code): CodeAndStack { + return [code, getStack()]; +} + +// TODO: import from muggle-string +export function getStack() { + const stack = new Error().stack!; + let source = stack.split('\n')[3].trim(); + if (source.endsWith(')')) { + source = source.slice(source.lastIndexOf('(') + 1, -1); + } + else { + source = source.slice(source.lastIndexOf(' ') + 1); + } + return source; +} + +export function disableAllFeatures(override: Partial): VueCodeInformation { + return { + verification: false, + completion: false, + semantic: false, + navigation: false, + structure: false, + format: false, + ...override, + }; +} + +export function enableAllFeatures(override: Partial): VueCodeInformation { + return { + verification: true, + completion: true, + semantic: true, + navigation: true, + structure: true, + format: true, + ...override, + }; +} + +export function mergeFeatureSettings(base: VueCodeInformation, ...others: Partial[]): VueCodeInformation { + const result: VueCodeInformation = { ...base }; + for (const info of others) { + for (const key in info) { + const value = info[key as keyof VueCodeInformation]; + if (value) { + result[key as keyof VueCodeInformation] = value as any; + } + } + } + return result; +} diff --git a/packages/language-core/src/index.ts b/packages/language-core/src/index.ts index 30594dbcbd..0fe246adef 100644 --- a/packages/language-core/src/index.ts +++ b/packages/language-core/src/index.ts @@ -8,10 +8,8 @@ export * from './utils/ts'; export * from './utils/parseSfc'; export * as scriptRanges from './parsers/scriptRanges'; -export * as sharedTypes from './utils/globalTypes'; export * from './utils/shared'; export { tsCodegen } from './plugins/vue-tsx'; export * from '@volar/language-core'; -export * from '@volar/source-map'; export type * as CompilerDOM from '@vue/compiler-dom'; diff --git a/packages/language-core/src/languageModule.ts b/packages/language-core/src/languageModule.ts index dcf477b78d..fd3ef93813 100644 --- a/packages/language-core/src/languageModule.ts +++ b/packages/language-core/src/languageModule.ts @@ -1,9 +1,8 @@ -import type { Language } from '@volar/language-core'; +import type { LanguagePlugin } from '@volar/language-core'; import * as path from 'path-browserify'; -import { getDefaultVueLanguagePlugins } from './plugins'; +import { getDefaultVueLanguagePlugins, createPluginContext } from './plugins'; import { VueFile } from './virtualFile/vueFile'; import { VueCompilerOptions, VueLanguagePlugin } from './types'; -import * as sharedTypes from './utils/globalTypes'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { resolveVueCompilerOptions } from './utils/ts'; @@ -33,32 +32,43 @@ function getVueFileRegistry(key: string, plugins: VueLanguagePlugin[]) { return fileRegistry; } +function getFileRegistryKey( + compilerOptions: ts.CompilerOptions, + vueCompilerOptions: VueCompilerOptions, + plugins: ReturnType[], + globalTypesHolder: string | undefined, +) { + const values = [ + globalTypesHolder, + ...Object.keys(vueCompilerOptions) + .sort() + .filter(key => key !== 'plugins') + .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]), + [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())] + .sort() + .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]), + ]; + return JSON.stringify(values); +} + export function createVueLanguage( ts: typeof import('typescript/lib/tsserverlibrary'), compilerOptions: ts.CompilerOptions = {}, _vueCompilerOptions: Partial = {}, codegenStack: boolean = false, -): Language { + globalTypesHolder?: string +): LanguagePlugin { const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions); - const plugins = getDefaultVueLanguagePlugins( + const allowLanguageIds = new Set(['vue']); + const pluginContext = createPluginContext( ts, compilerOptions, vueCompilerOptions, codegenStack, + globalTypesHolder, ); - const keys = [ - ...Object.keys(vueCompilerOptions) - .sort() - .filter(key => key !== 'plugins') - .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]), - [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())] - .sort() - .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]), - ]; - const fileRegistry = getVueFileRegistry(JSON.stringify(keys), _vueCompilerOptions.plugins ?? []); - - const allowLanguageIds = new Set(['vue']); + const plugins = getDefaultVueLanguagePlugins(pluginContext); if (vueCompilerOptions.extensions.includes('.md')) { allowLanguageIds.add('markdown'); @@ -67,49 +77,79 @@ export function createVueLanguage( allowLanguageIds.add('html'); } + let fileRegistry: Map | undefined; + return { - createVirtualFile(fileName, snapshot, languageId) { - if ( - (languageId && allowLanguageIds.has(languageId)) - || (!languageId && vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))) - ) { + createVirtualFile(fileName, languageId, snapshot) { + if (allowLanguageIds.has(languageId)) { + + if (!fileRegistry) { + + pluginContext.globalTypesHolder ??= fileName; + + fileRegistry = getVueFileRegistry( + getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder), + vueCompilerOptions.plugins, + ); + } + if (fileRegistry.has(fileName)) { const reusedVueFile = fileRegistry.get(fileName)!; reusedVueFile.update(snapshot); return reusedVueFile; } - const vueFile = new VueFile(fileName, snapshot, vueCompilerOptions, plugins, ts, codegenStack); + const vueFile = new VueFile(fileName, languageId, snapshot, vueCompilerOptions, plugins, ts, codegenStack); fileRegistry.set(fileName, vueFile); return vueFile; } }, - updateVirtualFile(sourceFile, snapshot) { - sourceFile.update(snapshot); + updateVirtualFile(vueFile, snapshot) { + vueFile.update(snapshot); }, - resolveHost(host) { - const sharedTypesSnapshot = ts.ScriptSnapshot.fromString(sharedTypes.getTypesCode(vueCompilerOptions)); - const sharedTypesFileName = path.join(host.rootPath, sharedTypes.baseName); - return { - ...host, - resolveModuleName(moduleName, impliedNodeFormat) { - if (impliedNodeFormat === ts.ModuleKind.ESNext && vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))) { - return `${moduleName}.js`; - } - return host.resolveModuleName?.(moduleName, impliedNodeFormat) ?? moduleName; - }, - getScriptFileNames() { - return [ - sharedTypesFileName, - ...host.getScriptFileNames(), - ]; - }, - getScriptSnapshot(fileName) { - if (fileName === sharedTypesFileName) { - return sharedTypesSnapshot; + disposeVirtualFile(vueFile, files) { + fileRegistry?.delete(vueFile.fileName); + if (vueFile.fileName === pluginContext.globalTypesHolder) { + if (fileRegistry?.size) { + for (const [fileName, file] of fileRegistry!) { + pluginContext.globalTypesHolder = fileName; + + fileRegistry = getVueFileRegistry( + getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder), + vueCompilerOptions.plugins, + ); + + files?.updateSourceFile( + file.fileName, + file.languageId, + // force dirty + { ...file.snapshot }, + ); + break; } - return host.getScriptSnapshot(fileName); - }, - }; + } + else { + fileRegistry = undefined; + pluginContext.globalTypesHolder = undefined; + } + } + }, + typescript: { + extraFileExtensions: vueCompilerOptions.extensions.map(ext => ({ + extension: ext, + isMixedContent: true, + scriptKind: 7 satisfies ts.ScriptKind.Deferred, + })), + resolveSourceFileName(tsFileName) { + const baseName = path.basename(tsFileName); + if (baseName.indexOf('.vue.') >= 0) { // .vue.ts .vue.d.ts .vue.js .vue.jsx .vue.tsx + return tsFileName.substring(0, tsFileName.lastIndexOf('.vue.') + '.vue'.length); + } + }, + resolveModuleName(moduleName, impliedNodeFormat) { + if (impliedNodeFormat === 99 satisfies ts.ModuleKind.ESNext && vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))) { + return `${moduleName}.js`; + } + }, }, }; } @@ -122,9 +162,10 @@ export function createLanguages( compilerOptions: ts.CompilerOptions = {}, vueCompilerOptions: Partial = {}, codegenStack: boolean = false, -): Language[] { + globalTypesHolder?: string +): LanguagePlugin[] { return [ - createVueLanguage(ts, compilerOptions, vueCompilerOptions, codegenStack), + createVueLanguage(ts, compilerOptions, vueCompilerOptions, codegenStack, globalTypesHolder), ...vueCompilerOptions.experimentalAdditionalLanguageModules?.map(module => require(module)) ?? [], ]; } diff --git a/packages/language-core/src/parsers/scriptRanges.ts b/packages/language-core/src/parsers/scriptRanges.ts index e8331b2d13..1c8ba7a0e8 100644 --- a/packages/language-core/src/parsers/scriptRanges.ts +++ b/packages/language-core/src/parsers/scriptRanges.ts @@ -1,6 +1,6 @@ import type { TextRange } from '../types'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import { getStartEnd, parseBindingRanges } from './scriptSetupRanges'; +import { getNodeText, getStartEnd, parseBindingRanges } from './scriptSetupRanges'; export interface ScriptRanges extends ReturnType { } @@ -17,12 +17,12 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr const bindings = hasScriptSetup ? parseBindingRanges(ts, ast) : []; - ast.forEachChild(raw => { + ts.forEachChild(ast, raw => { if (ts.isExportAssignment(raw)) { let node: ts.AsExpression | ts.ExportAssignment | ts.ParenthesizedExpression = raw; - while (ts.isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882 + while (isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882 node = node.expression; } @@ -39,12 +39,13 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr if (obj) { let componentsOptionNode: ts.ObjectLiteralExpression | undefined; let nameOptionNode: ts.Expression | undefined; - obj.forEachChild(node => { + ts.forEachChild(obj, node => { if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) { - if (node.name.escapedText === 'components' && ts.isObjectLiteralExpression(node.initializer)) { + const name = getNodeText(ts, node.name, ast); + if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) { componentsOptionNode = node.initializer; } - if (node.name.escapedText === 'name') { + if (name === 'name') { nameOptionNode = node.initializer; } } @@ -68,6 +69,11 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr }; function _getStartEnd(node: ts.Node) { - return getStartEnd(node, ast); + return getStartEnd(ts, node, ast); + } + + // isAsExpression is missing in tsc + function isAsExpression(node: ts.Node): node is ts.AsExpression { + return node.kind === ts.SyntaxKind.AsExpression; } } diff --git a/packages/language-core/src/parsers/scriptSetupRanges.ts b/packages/language-core/src/parsers/scriptSetupRanges.ts index a5a1e97228..8aded9fd62 100644 --- a/packages/language-core/src/parsers/scriptSetupRanges.ts +++ b/packages/language-core/src/parsers/scriptSetupRanges.ts @@ -34,8 +34,8 @@ export function parseScriptSetupRanges( define?: ReturnType; } = {}; - const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition'); - const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition'); + const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition'); + const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition'); const defineProp: { name: TextRange | undefined; nameIsString: boolean; @@ -44,10 +44,10 @@ export function parseScriptSetupRanges( required: boolean; }[] = []; const bindings = parseBindingRanges(ts, ast); - const text = ast.getFullText(); + const text = ast.text; const leadingCommentEndOffset = ts.getLeadingCommentRanges(text, 0)?.reverse()[0].end ?? 0; - ast.forEachChild(node => { + ts.forEachChild(ast, node => { const isTypeExport = (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword); if ( !foundNonImportExportNode @@ -57,18 +57,18 @@ export function parseScriptSetupRanges( // fix https://github.com/vuejs/language-tools/issues/1223 && !ts.isImportEqualsDeclaration(node) ) { - const commentRanges = ts.getLeadingCommentRanges(text, node.getFullStart()); + const commentRanges = ts.getLeadingCommentRanges(text, node.pos); if (commentRanges?.length) { const commentRange = commentRanges.sort((a, b) => a.pos - b.pos)[0]; importSectionEndOffset = commentRange.pos; } else { - importSectionEndOffset = node.getStart(ast); + importSectionEndOffset = getStartEnd(ts, node, ast).start; } foundNonImportExportNode = true; } }); - ast.forEachChild(child => visitNode(child, [ast])); + ts.forEachChild(ast, child => visitNode(child, [ast])); return { leadingCommentEndOffset, @@ -82,7 +82,7 @@ export function parseScriptSetupRanges( }; function _getStartEnd(node: ts.Node) { - return getStartEnd(node, ast); + return getStartEnd(ts, node, ast); } function parseDefineFunction(node: ts.CallExpression): TextRange & { @@ -102,7 +102,7 @@ export function parseScriptSetupRanges( ts.isCallExpression(node) && ts.isIdentifier(node.expression) ) { - const callText = node.expression.getText(ast); + const callText = getNodeText(ts, node.expression, ast); if (vueCompilerOptions.macros.defineModel.includes(callText)) { let name: TextRange | undefined; let options: ts.Node | undefined; @@ -121,7 +121,7 @@ export function parseScriptSetupRanges( let required = false; if (options && ts.isObjectLiteralExpression(options)) { for (const property of options.properties) { - if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) { + if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) { required = true; break; } @@ -142,7 +142,7 @@ export function parseScriptSetupRanges( const secondArg = node.arguments[1]; if (ts.isObjectLiteralExpression(secondArg)) { for (const property of secondArg.properties) { - if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) { + if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) { required = true; break; } @@ -181,13 +181,13 @@ export function parseScriptSetupRanges( else if (vueCompilerOptions.macros.defineSlots.includes(callText)) { slots.define = parseDefineFunction(node); if (ts.isVariableDeclaration(parent)) { - slots.name = parent.name.getText(ast); + slots.name = getNodeText(ts, parent.name, ast); } } else if (vueCompilerOptions.macros.defineEmits.includes(callText)) { emits.define = parseDefineFunction(node); if (ts.isVariableDeclaration(parent)) { - emits.name = parent.name.getText(ast); + emits.name = getNodeText(ts, parent.name, ast); } } else if (vueCompilerOptions.macros.defineExpose.includes(callText)) { @@ -199,7 +199,7 @@ export function parseScriptSetupRanges( for (let i = parents.length - 1; i >= 0; i--) { if (ts.isStatement(parents[i])) { const statement = parents[i]; - statement.forEachChild(child => { + ts.forEachChild(statement, child => { const range = _getStartEnd(child); statementRange ??= range; statementRange.end = range.end; @@ -217,7 +217,7 @@ export function parseScriptSetupRanges( }; if (ts.isVariableDeclaration(parent)) { - props.name = parent.name.getText(ast); + props.name = getNodeText(ts, parent.name, ast); } if (node.arguments.length) { props.define.arg = _getStartEnd(node.arguments[0]); @@ -233,11 +233,11 @@ export function parseScriptSetupRanges( props.withDefaults.arg = _getStartEnd(arg); } if (ts.isVariableDeclaration(parent)) { - props.name = parent.name.getText(ast); + props.name = getNodeText(ts, parent.name, ast); } } } - node.forEachChild(child => { + ts.forEachChild(node, child => { parents.push(node); visitNode(child, parents); parents.pop(); @@ -247,7 +247,7 @@ export function parseScriptSetupRanges( export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlibrary'), sourceFile: ts.SourceFile) { const bindings: TextRange[] = []; - sourceFile.forEachChild(node => { + ts.forEachChild(sourceFile, node => { if (ts.isVariableStatement(node)) { for (const node_2 of node.declarationList.declarations) { const vars = _findBindingVars(node_2.name); @@ -290,7 +290,7 @@ export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlib }); return bindings; function _getStartEnd(node: ts.Node) { - return getStartEnd(node, sourceFile); + return getStartEnd(ts, node, sourceFile); } function _findBindingVars(left: ts.BindingName) { return findBindingVars(ts, left, sourceFile); @@ -303,7 +303,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar return vars; function worker(_node: ts.Node) { if (ts.isIdentifier(_node)) { - vars.push(getStartEnd(_node, sourceFile)); + vars.push(getStartEnd(ts, _node, sourceFile)); } // { ? } = ... // [ ? ] = ... @@ -320,7 +320,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar } // { foo } = ... else if (ts.isShorthandPropertyAssignment(_node)) { - vars.push(getStartEnd(_node.name, sourceFile)); + vars.push(getStartEnd(ts, _node.name, sourceFile)); } // { ...? } = ... // [ ...? ] = ... @@ -330,9 +330,22 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar } } -export function getStartEnd(node: ts.Node, sourceFile: ts.SourceFile) { +export function getStartEnd( + ts: typeof import('typescript/lib/tsserverlibrary'), + node: ts.Node, + sourceFile: ts.SourceFile +) { return { - start: node.getStart(sourceFile), - end: node.getEnd(), + start: (ts as any).getTokenPosOfNode(node, sourceFile) as number, + end: node.end, }; } + +export function getNodeText( + ts: typeof import('typescript/lib/tsserverlibrary'), + node: ts.Node, + sourceFile: ts.SourceFile +) { + const { start, end } = getStartEnd(ts, node, sourceFile); + return sourceFile.text.substring(start, end); +} diff --git a/packages/language-core/src/plugins.ts b/packages/language-core/src/plugins.ts index 775a1adfa2..027e4a13e1 100644 --- a/packages/language-core/src/plugins.ts +++ b/packages/language-core/src/plugins.ts @@ -12,25 +12,13 @@ import { VueCompilerOptions, VueLanguagePlugin } from './types'; import * as CompilerDOM from '@vue/compiler-dom'; import * as CompilerVue2 from './utils/vue2TemplateCompiler'; -export function getDefaultVueLanguagePlugins( +export function createPluginContext( ts: typeof import('typescript/lib/tsserverlibrary'), compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, codegenStack: boolean, + globalTypesHolder: string | undefined, ) { - - const plugins: VueLanguagePlugin[] = [ - useMdFilePlugin, // .md for VitePress - useHtmlFilePlugin, // .html for PetiteVue - useVueFilePlugin, // .vue and others for Vue - useHtmlTemplatePlugin, - useVueSfcStyles, - useVueSfcCustomBlocks, - useVueSfcScriptsFormat, - useVueSfcTemplate, - useVueTsx, - ...vueCompilerOptions.plugins, - ]; const pluginCtx: Parameters[0] = { modules: { '@vue/compiler-dom': vueCompilerOptions.target < 3 @@ -44,9 +32,28 @@ export function getDefaultVueLanguagePlugins( compilerOptions, vueCompilerOptions, codegenStack, + globalTypesHolder, }; + return pluginCtx; +} + +export function getDefaultVueLanguagePlugins(pluginContext: Parameters[0]) { + + const plugins: VueLanguagePlugin[] = [ + useMdFilePlugin, // .md for VitePress + useHtmlFilePlugin, // .html for PetiteVue + useVueFilePlugin, // .vue and others for Vue + useHtmlTemplatePlugin, + useVueSfcStyles, + useVueSfcCustomBlocks, + useVueSfcScriptsFormat, + useVueSfcTemplate, + useVueTsx, + ...pluginContext.vueCompilerOptions.plugins, + ]; + const pluginInstances = plugins - .map(plugin => plugin(pluginCtx)) + .map(plugin => plugin(pluginContext)) .sort((a, b) => { const aOrder = a.order ?? 0; const bOrder = b.order ?? 0; diff --git a/packages/language-core/src/plugins/file-md.ts b/packages/language-core/src/plugins/file-md.ts index b4d37b339b..d8a22f8c6c 100644 --- a/packages/language-core/src/plugins/file-md.ts +++ b/packages/language-core/src/plugins/file-md.ts @@ -1,4 +1,4 @@ -import { buildMappings, Segment, SourceMap, toString } from '@volar/source-map'; +import { buildMappings, Segment, SourceMap, toString } from '@volar/language-core'; import type { SFCBlock } from '@vue/compiler-sfc'; import { VueLanguagePlugin } from '../types'; import { parse } from '../utils/parseSfc'; @@ -74,8 +74,8 @@ const plugin: VueLanguagePlugin = () => { return sfc; function transformRange(block: SFCBlock) { - block.loc.start.offset = file2VueSourceMap.toSourceOffset(block.loc.start.offset)?.[0] ?? -1; - block.loc.end.offset = file2VueSourceMap.toSourceOffset(block.loc.end.offset)?.[0] ?? -1; + block.loc.start.offset = file2VueSourceMap.getSourceOffset(block.loc.start.offset)?.[0] ?? -1; + block.loc.end.offset = file2VueSourceMap.getSourceOffset(block.loc.end.offset)?.[0] ?? -1; } }; } diff --git a/packages/language-core/src/plugins/vue-sfc-customblocks.ts b/packages/language-core/src/plugins/vue-sfc-customblocks.ts index b2bdf2e79e..7ec8d1d5d0 100644 --- a/packages/language-core/src/plugins/vue-sfc-customblocks.ts +++ b/packages/language-core/src/plugins/vue-sfc-customblocks.ts @@ -1,4 +1,4 @@ -import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core'; +import { enableAllFeatures } from '../generators/utils'; import { VueLanguagePlugin } from '../types'; const customBlockReg = /^(.*)\.customBlock_([^_]+)_(\d+)\.([^.]+)$/; @@ -24,12 +24,11 @@ const plugin: VueLanguagePlugin = () => { const index = parseInt(match[3]); const customBlock = sfc.customBlocks[index]; - embeddedFile.capabilities = FileCapabilities.full; embeddedFile.content.push([ customBlock.content, customBlock.name, 0, - FileRangeCapabilities.full, + enableAllFeatures({}), ]); } }, diff --git a/packages/language-core/src/plugins/vue-sfc-scripts.ts b/packages/language-core/src/plugins/vue-sfc-scripts.ts index e50695bf7c..df45f8cb0e 100644 --- a/packages/language-core/src/plugins/vue-sfc-scripts.ts +++ b/packages/language-core/src/plugins/vue-sfc-scripts.ts @@ -1,4 +1,4 @@ -import { FileCapabilities, FileKind } from '@volar/language-core'; +import { disableAllFeatures } from '../generators/utils'; import { VueLanguagePlugin } from '../types'; const scriptFormatReg = /^(.*)\.script_format\.([^.]+)$/; @@ -26,18 +26,14 @@ const plugin: VueLanguagePlugin = () => { const scriptSetupMatch = embeddedFile.fileName.match(scriptSetupFormatReg); const script = scriptMatch ? sfc.script : scriptSetupMatch ? sfc.scriptSetup : undefined; if (script) { - embeddedFile.kind = FileKind.TextFile; - embeddedFile.capabilities = { - ...FileCapabilities.full, - diagnostic: false, - codeAction: false, - inlayHint: false, - }; embeddedFile.content.push([ script.content, script.name, 0, - {}, + disableAllFeatures({ + structure: true, + format: true, + }), ]); } }, diff --git a/packages/language-core/src/plugins/vue-sfc-styles.ts b/packages/language-core/src/plugins/vue-sfc-styles.ts index b750349a1d..b661a8d88f 100644 --- a/packages/language-core/src/plugins/vue-sfc-styles.ts +++ b/packages/language-core/src/plugins/vue-sfc-styles.ts @@ -1,4 +1,4 @@ -import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core'; +import { enableAllFeatures } from '../generators/utils'; import { VueLanguagePlugin } from '../types'; const styleReg = /^(.*)\.style_(\d+)\.([^.]+)$/; @@ -24,12 +24,11 @@ const plugin: VueLanguagePlugin = () => { const index = parseInt(match[2]); const style = sfc.styles[index]; - embeddedFile.capabilities = FileCapabilities.full; embeddedFile.content.push([ style.content, style.name, 0, - FileRangeCapabilities.full, + enableAllFeatures({}), ]); } }, diff --git a/packages/language-core/src/plugins/vue-sfc-template.ts b/packages/language-core/src/plugins/vue-sfc-template.ts index b21e8e8cd4..1b366f235a 100644 --- a/packages/language-core/src/plugins/vue-sfc-template.ts +++ b/packages/language-core/src/plugins/vue-sfc-template.ts @@ -1,4 +1,4 @@ -import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core'; +import { enableAllFeatures } from '../generators/utils'; import { VueLanguagePlugin } from '../types'; const templateReg = /^(.*)\.template\.([^.]+)$/; @@ -19,12 +19,11 @@ const plugin: VueLanguagePlugin = () => { resolveEmbeddedFile(_fileName, sfc, embeddedFile) { const match = embeddedFile.fileName.match(templateReg); if (match && sfc.template) { - embeddedFile.capabilities = FileCapabilities.full; embeddedFile.content.push([ sfc.template.content, sfc.template.name, 0, - FileRangeCapabilities.full, + enableAllFeatures({}), ]); } }, diff --git a/packages/language-core/src/plugins/vue-tsx.ts b/packages/language-core/src/plugins/vue-tsx.ts index b3cb5e826b..1291312261 100644 --- a/packages/language-core/src/plugins/vue-tsx.ts +++ b/packages/language-core/src/plugins/vue-tsx.ts @@ -1,11 +1,11 @@ +import { CodeInformation, Mapping, Segment, StackNode, track } from '@volar/language-core'; import { computed, computedSet } from 'computeds'; import { generate as generateScript } from '../generators/script'; import { generate as generateTemplate } from '../generators/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import { Sfc, VueLanguagePlugin } from '../types'; -import { FileCapabilities, FileKind } from '@volar/language-core'; -import * as muggle from 'muggle-string'; +import { Code, Sfc, VueLanguagePlugin } from '../types'; +import { enableAllFeatures } from '../generators/utils'; const templateFormatReg = /^\.template_format\.ts$/; const templateStyleCssReg = /^\.template_style\.css$/; @@ -43,41 +43,39 @@ const plugin: VueLanguagePlugin = (ctx) => { resolveEmbeddedFile(fileName, sfc, embeddedFile) { const _tsx = useTsx(fileName, sfc); + const lang = _tsx.lang(); const suffix = embeddedFile.fileName.replace(fileName, ''); - if (suffix === '.' + _tsx.lang()) { - embeddedFile.kind = FileKind.TypeScriptHostFile; - embeddedFile.capabilities = { - ...FileCapabilities.full, - foldingRange: false, - documentFormatting: false, - documentSymbol: false, + if (suffix === '.' + lang) { + embeddedFile.typescript = { + scriptKind: lang === 'js' ? ctx.modules.typescript.ScriptKind.JS + : lang === 'jsx' ? ctx.modules.typescript.ScriptKind.JSX + : lang === 'tsx' ? ctx.modules.typescript.ScriptKind.TSX + : ctx.modules.typescript.ScriptKind.TS }; const tsx = _tsx.generatedScript(); if (tsx) { - const [content, contentStacks] = ctx.codegenStack ? muggle.track([...tsx.codes], [...tsx.codeStacks]) : [[...tsx.codes], [...tsx.codeStacks]]; + const [content, contentStacks] = ctx.codegenStack ? track([...tsx.codes], [...tsx.codeStacks]) : [[...tsx.codes], [...tsx.codeStacks]]; + content.forEach(code => { + if (typeof code !== 'string') { + code[3].structure = false; + code[3].format = false; + } + }); embeddedFile.content = content; embeddedFile.contentStacks = contentStacks; - embeddedFile.mirrorBehaviorMappings = [...tsx.mirrorBehaviorMappings]; + embeddedFile.linkedCodeMappings = [...tsx.linkedCodeMappings]; } } else if (suffix.match(templateFormatReg)) { embeddedFile.parentFileName = fileName + '.template.' + sfc.template?.lang; - embeddedFile.kind = FileKind.TextFile; - embeddedFile.capabilities = { - ...FileCapabilities.full, - diagnostic: false, - foldingRange: false, - codeAction: false, - inlayHint: false, - }; const template = _tsx.generatedTemplate(); if (template) { const [content, contentStacks] = ctx.codegenStack - ? muggle.track([...template.formatCodes], [...template.formatCodeStacks]) - : [[...template.formatCodes], [...template.formatCodeStacks]]; + ? track([...template.formatCodes], template.formatCodeStacks.map(stack => ({ stack, length: 1 }))) + : [[...template.formatCodes], template.formatCodeStacks.map(stack => ({ stack, length: 1 }))]; embeddedFile.content = content; embeddedFile.contentStacks = contentStacks; } @@ -90,7 +88,7 @@ const plugin: VueLanguagePlugin = (ctx) => { cssVar.text, style.name, cssVar.offset, - {}, + enableAllFeatures({}), ]); embeddedFile.content.push(');\n'); } @@ -103,14 +101,11 @@ const plugin: VueLanguagePlugin = (ctx) => { const template = _tsx.generatedTemplate(); if (template) { const [content, contentStacks] = ctx.codegenStack - ? muggle.track([...template.cssCodes], [...template.cssCodeStacks]) - : [[...template.cssCodes], [...template.cssCodeStacks]]; - embeddedFile.content = content; + ? track([...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 }))) + : [[...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 }))]; + embeddedFile.content = content as Segment[]; embeddedFile.contentStacks = contentStacks; } - - // for color pickers support - embeddedFile.capabilities.documentSymbol = true; } }, }; @@ -125,9 +120,13 @@ const plugin: VueLanguagePlugin = (ctx) => { export default plugin; -function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOptions, codegenStack, modules }: Parameters[0]) { +function createTsx( + fileName: string, + _sfc: Sfc, + ctx: Parameters[0], +) { - const ts = modules.typescript; + const ts = ctx.modules.typescript; const lang = computed(() => { return !_sfc.script && !_sfc.scriptSetup ? 'ts' : _sfc.scriptSetup && _sfc.scriptSetup.lang !== 'js' ? _sfc.scriptSetup.lang @@ -141,11 +140,11 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp ); const scriptSetupRanges = computed(() => _sfc.scriptSetup - ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, vueCompilerOptions) + ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, ctx.vueCompilerOptions) : undefined ); const shouldGenerateScopedClasses = computed(() => { - const option = vueCompilerOptions.experimentalResolveStyleCssClasses; + const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses; return _sfc.styles.some(s => { return option === 'always' || (option === 'scoped' && s.scoped); }); @@ -159,7 +158,7 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp } for (const style of _sfc.styles) { - const option = vueCompilerOptions.experimentalResolveStyleCssClasses; + const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses; if (option === 'always' || (option === 'scoped' && style.scoped)) { for (const className of style.classNames) { classes.add(className.text.substring(1)); @@ -174,36 +173,108 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp if (!_sfc.template) return; - return generateTemplate( + const tsCodes: Code[] = []; + const tsFormatCodes: Code[] = []; + const inlineCssCodes: Code[] = []; + const tsCodegenStacks: string[] = []; + const tsFormatCodegenStacks: string[] = []; + const inlineCssCodegenStacks: string[] = []; + const codegen = generateTemplate( ts, - compilerOptions, - vueCompilerOptions, + ctx.compilerOptions, + ctx.vueCompilerOptions, _sfc.template, shouldGenerateScopedClasses(), stylesScopedClasses(), hasScriptSetupSlots(), slotsAssignName(), propsAssignName(), - codegenStack, + ctx.codegenStack, ); + + let current = codegen.next(); + + while (!current.done) { + const [type, code, stack] = current.value; + if (type === 'ts') { + tsCodes.push(code); + } + else if (type === 'tsFormat') { + tsFormatCodes.push(code); + } + else if (type === 'inlineCss') { + inlineCssCodes.push(code); + } + if (ctx.codegenStack) { + if (type === 'ts') { + tsCodegenStacks.push(stack); + } + else if (type === 'tsFormat') { + tsFormatCodegenStacks.push(stack); + } + else if (type === 'inlineCss') { + inlineCssCodegenStacks.push(stack); + } + } + current = codegen.next(); + } + + return { + ...current.value, + codes: tsCodes, + codeStacks: tsCodegenStacks, + formatCodes: tsFormatCodes, + formatCodeStacks: tsFormatCodegenStacks, + cssCodes: inlineCssCodes, + cssCodeStacks: inlineCssCodegenStacks, + }; }); const hasScriptSetupSlots = computed(() => !!scriptSetupRanges()?.slots.define); const slotsAssignName = computed(() => scriptSetupRanges()?.slots.name); const propsAssignName = computed(() => scriptSetupRanges()?.props.name); - const generatedScript = computed(() => generateScript( - ts, - fileName, - _sfc.script, - _sfc.scriptSetup, - _sfc.styles, - lang(), - scriptRanges(), - scriptSetupRanges(), - generatedTemplate(), - compilerOptions, - vueCompilerOptions, - codegenStack, - )); + const generatedScript = computed(() => { + const codes: Code[] = []; + const codeStacks: StackNode[] = []; + const linkedCodeMappings: Mapping[] = []; + const _template = generatedTemplate(); + let generatedLength = 0; + for (const [code, stack] of generateScript( + ts, + fileName, + _sfc.script, + _sfc.scriptSetup, + _sfc.styles, + lang(), + scriptRanges(), + scriptSetupRanges(), + _template ? { + tsCodes: _template.codes, + tsCodegenStacks: _template.codeStacks, + accessedGlobalVariables: _template.accessedGlobalVariables, + hasSlot: _template.hasSlot, + tagNames: new Set(_template.tagOffsetsMap.keys()), + } : undefined, + ctx.compilerOptions, + ctx.vueCompilerOptions, + ctx.globalTypesHolder, + () => generatedLength, + linkedCodeMappings, + ctx.codegenStack, + )) { + codes.push(code); + if (ctx.codegenStack) { + codeStacks.push({ stack, length: 1 }); + } + generatedLength += typeof code === 'string' + ? code.length + : code[0].length; + }; + return { + codes, + codeStacks, + linkedCodeMappings, + }; + }); return { scriptRanges, diff --git a/packages/language-core/src/types.ts b/packages/language-core/src/types.ts index 4096cadafd..cca3f9cfc0 100644 --- a/packages/language-core/src/types.ts +++ b/packages/language-core/src/types.ts @@ -2,6 +2,7 @@ import type * as CompilerDOM from '@vue/compiler-dom'; import type { SFCParseResult } from '@vue/compiler-sfc'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type { VueEmbeddedFile } from './virtualFile/embeddedFile'; +import type { CodeInformation, Segment } from '@volar/language-core'; export type { SFCParseResult } from '@vue/compiler-sfc'; @@ -10,6 +11,23 @@ export type RawVueCompilerOptions = Partial; + export interface VueCompilerOptions { target: number; lib: string; @@ -31,7 +49,6 @@ export interface VueCompilerOptions { withDefaults: string[]; }; plugins: VueLanguagePlugin[]; - hooks: string[]; // experimental experimentalDefinePropProposal: 'kevinEdition' | 'johnsonEdition' | false; @@ -49,6 +66,7 @@ export type VueLanguagePlugin = (ctx: { compilerOptions: ts.CompilerOptions; vueCompilerOptions: VueCompilerOptions; codegenStack: boolean; + globalTypesHolder: string | undefined; }) => { version: 1; name?: string; diff --git a/packages/language-core/src/utils/globalTypes.ts b/packages/language-core/src/utils/globalTypes.ts deleted file mode 100644 index df049fca19..0000000000 --- a/packages/language-core/src/utils/globalTypes.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { VueCompilerOptions } from '../types'; -import { getSlotsPropertyName } from './shared'; - -export const baseName = '__VLS_types.d.ts'; - -export function getTypesCode(vueCompilerOptions: VueCompilerOptions) { - return ` -// @ts-nocheck - -type __VLS_IntrinsicElements = __VLS_PickNotAny>>; -type __VLS_Element = __VLS_PickNotAny; - -type __VLS_IsAny = 0 extends 1 & T ? true : false; -type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; - -type __VLS_Prettify = { [K in keyof T]: T[K]; } & {}; - -type __VLS_OmitKeepDiscriminatedUnion = - T extends any - ? Pick> - : never; - -type __VLS_GlobalComponents = - __VLS_PickNotAny - & __VLS_PickNotAny - & __VLS_PickNotAny - & Pick; - -declare const __VLS_intrinsicElements: __VLS_IntrinsicElements; - -// v-for -declare function __VLS_getVForSourceType(source: number): [number, number, number][]; -declare function __VLS_getVForSourceType(source: string): [string, number, number][]; -declare function __VLS_getVForSourceType(source: T): [ - T[number], // item - number, // key - number, // index -][]; -declare function __VLS_getVForSourceType }>(source: T): [ - T extends { [Symbol.iterator](): Iterator } ? T1 : never, // item - number, // key - undefined, // index -][]; -declare function __VLS_getVForSourceType(source: T): [ - T[keyof T], // item - keyof T, // key - number, // index -][]; - -declare function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>; -declare function __VLS_getSlotParam(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>[0]; -declare function __VLS_directiveFunction(dir: T): - T extends import('${vueCompilerOptions.lib}').ObjectDirective | import('${vueCompilerOptions.lib}').FunctionDirective ? (value: V) => void - : T; -declare function __VLS_withScope(ctx: T, scope: K): ctx is T & K; -declare function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; - -type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; -type __VLS_WithComponent = - N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : - N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : - N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : - N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N1] } : - N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N2] } : - N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N3] } : - ${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: unknown }'} - -type __VLS_FillingEventArg_ParametersLength any> = __VLS_IsAny> extends true ? -1 : Parameters['length']; -type __VLS_FillingEventArg = E extends (...args: any) => any ? __VLS_FillingEventArg_ParametersLength extends 0 ? ($event?: undefined) => ReturnType : E : E; -declare function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): - T extends new (...args: any) => any - ? (props: (K extends { $props: infer Props } ? Props : any)${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: { - attrs?: any, - slots?: K extends { ${getSlotsPropertyName(vueCompilerOptions.target)}: infer Slots } ? Slots : any, - emit?: K extends { $emit: infer Emit } ? Emit : any - }) => JSX.Element & { __ctx?: typeof ctx & { props?: typeof props; expose?(exposed: K): void; } } - : T extends () => any ? (props: {}, ctx?: any) => ReturnType - : T extends (...args: any) => any ? T - : (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; -declare function __VLS_elementAsFunctionalComponent(t: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; -declare function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; -declare function __VLS_pickEvent(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg< - __VLS_PickNotAny< - __VLS_AsFunctionOrAny, - __VLS_AsFunctionOrAny - > -> | undefined; -declare function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): __VLS_PickNotAny< - '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any - , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any ->; -type __VLS_FunctionalComponentProps = - '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never - : T extends (props: infer P, ...args: any) => any ? P : - {}; -type __VLS_AsFunctionOrAny = unknown extends F ? any : ((...args: any) => any) extends F ? F : any; - -declare function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; - -/** - * emit - */ -// fix https://github.com/vuejs/language-tools/issues/926 -type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; -type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R - ? U extends T - ? never - : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) - : never; -type __VLS_OverloadUnion = Exclude< - __VLS_OverloadUnionInner<(() => never) & T>, - T extends () => never ? never : () => never ->; -type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F - ? F extends (event: infer E, ...args: infer A) => any - ? { [K in E & string]: (...args: A) => void; } - : never - : never; -type __VLS_NormalizeEmits = __VLS_Prettify< - __VLS_UnionToIntersection< - __VLS_ConstructorOverloads & { - [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never - } - > ->; -`.trim(); -} diff --git a/packages/language-core/src/utils/transform.ts b/packages/language-core/src/utils/transform.ts index 9465908195..e80adec24e 100644 --- a/packages/language-core/src/utils/transform.ts +++ b/packages/language-core/src/utils/transform.ts @@ -1,87 +1,87 @@ import { isGloballyWhitelisted } from '@vue/shared'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { VueCompilerOptions } from '../types'; +import { getNodeText, getStartEnd } from '../parsers/scriptSetupRanges'; -export function walkInterpolationFragment( +export function* eachInterpolationSegment( ts: typeof import('typescript/lib/tsserverlibrary'), code: string, ast: ts.SourceFile, - cb: (fragment: string, offset: number | undefined, isJustForErrorMapping?: boolean) => void, localVars: Map, identifiers: Set, vueOptions: VueCompilerOptions, -) { - - let ctxVars: { + ctxVars: { text: string, isShorthand: boolean, offset: number, - }[] = []; + }[] = [] +): Generator<[fragment: string, offset: number | undefined, isJustForErrorMapping?: boolean]> { const varCb = (id: ts.Identifier, isShorthand: boolean) => { + const text = getNodeText(ts, id, ast); if ( - localVars.get(id.text) || + localVars.get(text) || // https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352 - isGloballyWhitelisted(id.text) || - id.text === 'require' || - id.text.startsWith('__VLS_') + isGloballyWhitelisted(text) || + text === 'require' || + text.startsWith('__VLS_') ) { // localVarOffsets.push(localVar.getStart(ast)); } else { ctxVars.push({ - text: id.text, + text, isShorthand: isShorthand, - offset: id.getStart(ast), + offset: getStartEnd(ts, id, ast).start, }); - identifiers.add(id.text); + identifiers.add(text); } }; - ast.forEachChild(node => walkIdentifiers(ts, node, varCb, localVars)); + ts.forEachChild(ast, node => walkIdentifiers(ts, node, ast, varCb, localVars)); ctxVars = ctxVars.sort((a, b) => a.offset - b.offset); if (ctxVars.length) { if (ctxVars[0].isShorthand) { - cb(code.substring(0, ctxVars[0].offset + ctxVars[0].text.length), 0); - cb(': ', undefined); + yield [code.substring(0, ctxVars[0].offset + ctxVars[0].text.length), 0]; + yield [': ', undefined]; } else { - cb(code.substring(0, ctxVars[0].offset), 0); + yield [code.substring(0, ctxVars[0].offset), 0]; } for (let i = 0; i < ctxVars.length - 1; i++) { // fix https://github.com/vuejs/language-tools/issues/1205 // fix https://github.com/vuejs/language-tools/issues/1264 - cb('', ctxVars[i + 1].offset, true); + yield ['', ctxVars[i + 1].offset, true]; if (vueOptions.experimentalUseElementAccessInTemplate) { const varStart = ctxVars[i].offset; const varEnd = ctxVars[i].offset + ctxVars[i].text.length; - cb('__VLS_ctx[', undefined); - cb('', varStart, true); - cb("'", undefined); - cb(code.substring(varStart, varEnd), varStart); - cb("'", undefined); - cb('', varEnd, true); - cb(']', undefined); + yield ['__VLS_ctx[', undefined]; + yield ['', varStart, true]; + yield ["'", undefined]; + yield [code.substring(varStart, varEnd), varStart]; + yield ["'", undefined]; + yield ['', varEnd, true]; + yield [']', undefined]; if (ctxVars[i + 1].isShorthand) { - cb(code.substring(varEnd, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), varEnd); - cb(': ', undefined); + yield [code.substring(varEnd, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), varEnd]; + yield [': ', undefined]; } else { - cb(code.substring(varEnd, ctxVars[i + 1].offset), varEnd); + yield [code.substring(varEnd, ctxVars[i + 1].offset), varEnd]; } } else { - cb('__VLS_ctx.', undefined); + yield ['__VLS_ctx.', undefined]; if (ctxVars[i + 1].isShorthand) { - cb(code.substring(ctxVars[i].offset, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), ctxVars[i].offset); - cb(': ', undefined); + yield [code.substring(ctxVars[i].offset, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), ctxVars[i].offset]; + yield [': ', undefined]; } else { - cb(code.substring(ctxVars[i].offset, ctxVars[i + 1].offset), ctxVars[i].offset); + yield [code.substring(ctxVars[i].offset, ctxVars[i + 1].offset), ctxVars[i].offset]; } } } @@ -89,31 +89,30 @@ export function walkInterpolationFragment( if (vueOptions.experimentalUseElementAccessInTemplate) { const varStart = ctxVars[ctxVars.length - 1].offset; const varEnd = ctxVars[ctxVars.length - 1].offset + ctxVars[ctxVars.length - 1].text.length; - cb('__VLS_ctx[', undefined); - cb('', varStart, true); - cb("'", undefined); - cb(code.substring(varStart, varEnd), varStart); - cb("'", undefined); - cb('', varEnd, true); - cb(']', undefined); - cb(code.substring(varEnd), varEnd); + yield ['__VLS_ctx[', undefined]; + yield ['', varStart, true]; + yield ["'", undefined]; + yield [code.substring(varStart, varEnd), varStart]; + yield ["'", undefined]; + yield ['', varEnd, true]; + yield [']', undefined]; + yield [code.substring(varEnd), varEnd]; } else { - cb('', ctxVars[ctxVars.length - 1].offset, true); - cb('__VLS_ctx.', undefined); - cb(code.substring(ctxVars[ctxVars.length - 1].offset), ctxVars[ctxVars.length - 1].offset); + yield ['', ctxVars[ctxVars.length - 1].offset, true]; + yield ['__VLS_ctx.', undefined]; + yield [code.substring(ctxVars[ctxVars.length - 1].offset), ctxVars[ctxVars.length - 1].offset]; } } else { - cb(code, 0); + yield [code, 0]; } - - return ctxVars; } function walkIdentifiers( ts: typeof import('typescript/lib/tsserverlibrary'), node: ts.Node, + ast: ts.SourceFile, cb: (varNode: ts.Identifier, isShorthand: boolean) => void, localVars: Map, blockVars: string[] = [], @@ -127,34 +126,34 @@ function walkIdentifiers( cb(node.name, true); } else if (ts.isPropertyAccessExpression(node)) { - walkIdentifiers(ts, node.expression, cb, localVars, blockVars, false); + walkIdentifiers(ts, node.expression, ast, cb, localVars, blockVars, false); } else if (ts.isVariableDeclaration(node)) { - collectVars(ts, node.name, blockVars); + collectVars(ts, node.name, ast, blockVars); for (const varName of blockVars) { localVars.set(varName, (localVars.get(varName) ?? 0) + 1); } if (node.initializer) - walkIdentifiers(ts, node.initializer, cb, localVars, blockVars, false); + walkIdentifiers(ts, node.initializer, ast, cb, localVars, blockVars, false); } else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) { const functionArgs: string[] = []; for (const param of node.parameters) { - collectVars(ts, param.name, functionArgs); + collectVars(ts, param.name, ast, functionArgs); if (param.type) { - walkIdentifiers(ts, param.type, cb, localVars, blockVars, false); + walkIdentifiers(ts, param.type, ast, cb, localVars, blockVars, false); } } for (const varName of functionArgs) localVars.set(varName, (localVars.get(varName) ?? 0) + 1); - walkIdentifiers(ts, node.body, cb, localVars, blockVars, false); + walkIdentifiers(ts, node.body, ast, cb, localVars, blockVars, false); for (const varName of functionArgs) localVars.set(varName, localVars.get(varName)! - 1); @@ -164,31 +163,31 @@ function walkIdentifiers( if (ts.isPropertyAssignment(prop)) { // fix https://github.com/vuejs/language-tools/issues/1176 if (ts.isComputedPropertyName(prop.name)) { - walkIdentifiers(ts, prop.name.expression, cb, localVars, blockVars, false); + walkIdentifiers(ts, prop.name.expression, ast, cb, localVars, blockVars, false); } - walkIdentifiers(ts, prop.initializer, cb, localVars, blockVars, false); + walkIdentifiers(ts, prop.initializer, ast, cb, localVars, blockVars, false); } // fix https://github.com/vuejs/language-tools/issues/1156 else if (ts.isShorthandPropertyAssignment(prop)) { - walkIdentifiers(ts, prop, cb, localVars, blockVars, false); + walkIdentifiers(ts, prop, ast, cb, localVars, blockVars, false); } // fix https://github.com/vuejs/language-tools/issues/1148#issuecomment-1094378126 else if (ts.isSpreadAssignment(prop)) { // TODO: cannot report "Spread types may only be created from object types.ts(2698)" - walkIdentifiers(ts, prop.expression, cb, localVars, blockVars, false); + walkIdentifiers(ts, prop.expression, ast, cb, localVars, blockVars, false); } } } else if (ts.isTypeReferenceNode(node)) { // fix https://github.com/vuejs/language-tools/issues/1422 - node.forEachChild(node => walkIdentifiersInTypeReference(ts, node, cb)); + ts.forEachChild(node, node => walkIdentifiersInTypeReference(ts, node, cb)); } else { const _blockVars = blockVars; if (ts.isBlock(node)) { blockVars = []; } - node.forEachChild(node => walkIdentifiers(ts, node, cb, localVars, blockVars, false)); + ts.forEachChild(node, node => walkIdentifiers(ts, node, ast, cb, localVars, blockVars, false)); if (ts.isBlock(node)) { for (const varName of blockVars) { localVars.set(varName, localVars.get(varName)! - 1); @@ -213,31 +212,32 @@ function walkIdentifiersInTypeReference( cb(node.exprName, false); } else { - node.forEachChild(node => walkIdentifiersInTypeReference(ts, node, cb)); + ts.forEachChild(node, node => walkIdentifiersInTypeReference(ts, node, cb)); } } export function collectVars( ts: typeof import('typescript/lib/tsserverlibrary'), node: ts.Node, + ast: ts.SourceFile, result: string[], ) { if (ts.isIdentifier(node)) { - result.push(node.text); + result.push(getNodeText(ts, node, ast)); } else if (ts.isObjectBindingPattern(node)) { for (const el of node.elements) { - collectVars(ts, el.name, result); + collectVars(ts, el.name, ast, result); } } else if (ts.isArrayBindingPattern(node)) { for (const el of node.elements) { if (ts.isBindingElement(el)) { - collectVars(ts, el.name, result); + collectVars(ts, el.name, ast, result); } } } else { - node.forEachChild(node => collectVars(ts, node, result)); + ts.forEachChild(node, node => collectVars(ts, node, ast, result)); } } diff --git a/packages/language-core/src/utils/ts.ts b/packages/language-core/src/utils/ts.ts index 64b18fa817..6f751fb495 100644 --- a/packages/language-core/src/utils/ts.ts +++ b/packages/language-core/src/utils/ts.ts @@ -178,11 +178,6 @@ function getPartialVueCompilerOptions( result.plugins = plugins; } - if (rawOptions.hooks) { - result.hooks = rawOptions.hooks - .map(resolvePath) - .filter((hook): hook is NonNullable => !!hook); - } if (rawOptions.experimentalAdditionalLanguageModules) { result.experimentalAdditionalLanguageModules = rawOptions.experimentalAdditionalLanguageModules .map(resolvePath) @@ -268,7 +263,6 @@ export function resolveVueCompilerOptions(vueOptions: Partial[], @@ -32,7 +30,7 @@ export function computedFiles( } return blocks; }); - const pluginsResult = plugins.map(plugin => compiledPluginFiles(plugins, plugin, fileName, sfc, nameToBlock, codegenStack)); + const pluginsResult = plugins.map(plugin => computedPluginFiles(plugins, plugin, fileName, sfc, nameToBlock, codegenStack)); const flatResult = computed(() => pluginsResult.map(r => r()).flat()); const structuredResult = computed(() => { @@ -50,7 +48,10 @@ export function computedFiles( for (const { file, snapshot, mappings, codegenStacks } of remain) { embeddedFiles.push({ - ...file, + fileName: file.fileName, + languageId: resolveCommonLanguageId(file.fileName), + typescript: file.typescript, + linkedCodeMappings: file.linkedCodeMappings, snapshot, mappings, codegenStacks, @@ -66,7 +67,10 @@ export function computedFiles( const { file, snapshot, mappings, codegenStacks } = remain[i]; if (!file.parentFileName) { embeddedFiles.push({ - ...file, + fileName: file.fileName, + languageId: resolveCommonLanguageId(file.fileName), + typescript: file.typescript, + linkedCodeMappings: file.linkedCodeMappings, snapshot, mappings, codegenStacks, @@ -78,7 +82,10 @@ export function computedFiles( const parent = findParentStructure(file.parentFileName, embeddedFiles); if (parent) { parent.embeddedFiles.push({ - ...file, + fileName: file.fileName, + languageId: resolveCommonLanguageId(file.fileName), + typescript: file.typescript, + linkedCodeMappings: file.linkedCodeMappings, snapshot, mappings, codegenStacks, @@ -105,7 +112,7 @@ export function computedFiles( return structuredResult; } -function compiledPluginFiles( +function computedPluginFiles( plugins: ReturnType[], plugin: ReturnType, fileName: string, @@ -128,7 +135,7 @@ function compiledPluginFiles( for (const embeddedFileName of embeddedFileNames) { if (!embeddedFiles[embeddedFileName]) { embeddedFiles[embeddedFileName] = computed(() => { - const [content, stacks] = codegenStack ? muggle.track([]) : [[], []]; + const [content, stacks] = codegenStack ? track([]) : [[], []]; const file = new VueEmbeddedFile(embeddedFileName, content, stacks); for (const plugin of plugins) { if (!plugin.resolveEmbeddedFile) { @@ -174,27 +181,37 @@ function compiledPluginFiles( return computed(() => { return files().map(_file => { + const { file, snapshot } = _file(); const mappings = buildMappings(file.content); + let lastValidMapping: typeof mappings[number]; + for (const mapping of mappings) { if (mapping.source !== undefined) { const block = nameToBlock()[mapping.source]; if (block) { - mapping.sourceRange = [ - mapping.sourceRange[0] + block.startTagEnd, - mapping.sourceRange[1] + block.startTagEnd, - ]; + mapping.sourceOffsets = mapping.sourceOffsets.map(offset => offset + block.startTagEnd); } else { // ignore } mapping.source = undefined; } + if (mapping.data.__combineLastMappping) { + lastValidMapping!.sourceOffsets.push(...mapping.sourceOffsets); + lastValidMapping!.generatedOffsets.push(...mapping.generatedOffsets); + lastValidMapping!.lengths.push(...mapping.lengths); + continue; + } + else { + lastValidMapping = mapping; + } } + return { file, snapshot, - mappings, + mappings: mappings.filter(mapping => !mapping.data.__combineLastMappping), codegenStacks: buildStacks(file.content, file.contentStacks), }; }); diff --git a/packages/language-core/src/virtualFile/computedMappings.ts b/packages/language-core/src/virtualFile/computedMappings.ts index 490c063841..a4661aec48 100644 --- a/packages/language-core/src/virtualFile/computedMappings.ts +++ b/packages/language-core/src/virtualFile/computedMappings.ts @@ -1,16 +1,15 @@ -import { FileRangeCapabilities } from '@volar/language-core'; -import { Mapping, Segment } from '@volar/source-map'; -import * as muggle from 'muggle-string'; -import type * as ts from 'typescript/lib/tsserverlibrary'; -import { Sfc } from '../types'; +import { Mapping, Segment, replaceSourceRange } from '@volar/language-core'; import { computed } from 'computeds'; +import type * as ts from 'typescript/lib/tsserverlibrary'; +import { enableAllFeatures } from '../generators/utils'; +import { Sfc, VueCodeInformation } from '../types'; export function computedMappings( snapshot: () => ts.IScriptSnapshot, sfc: Sfc ) { return computed(() => { - const str: Segment[] = [[snapshot().getText(0, snapshot().getLength()), undefined, 0, FileRangeCapabilities.full]]; + const str: Segment[] = [[snapshot().getText(0, snapshot().getLength()), undefined, 0, enableAllFeatures({})]]; for (const block of [ sfc.script, sfc.scriptSetup, @@ -19,26 +18,20 @@ export function computedMappings( ...sfc.customBlocks, ]) { if (block) { - muggle.replaceSourceRange( - str, undefined, block.startTagEnd, block.endTagStart, - [ - block.content, - undefined, - block.startTagEnd, - {}, - ], - ); + replaceSourceRange(str, undefined, block.startTagEnd, block.endTagStart, '\n\n'); } } - return str.map>((m) => { - const text = m[0]; - const start = m[2] as number; - const end = start + text.length; - return { - sourceRange: [start, end], - generatedRange: [start, end], - data: m[3] as FileRangeCapabilities, - }; - }); + return str + .filter(s => typeof s !== 'string') + .map>((m) => { + const text = m[0]; + const start = m[2] as number; + return { + sourceOffsets: [start], + generatedOffsets: [start], + lengths: [text.length], + data: m[3] as VueCodeInformation, + }; + }); }); } diff --git a/packages/language-core/src/virtualFile/computedSfc.ts b/packages/language-core/src/virtualFile/computedSfc.ts index af6bb7c0e7..8b107549e4 100644 --- a/packages/language-core/src/virtualFile/computedSfc.ts +++ b/packages/language-core/src/virtualFile/computedSfc.ts @@ -43,7 +43,7 @@ export function computedSfc( const _src = src(); return _src ? untrackedSnapshot().getText(0, base.startTagEnd).lastIndexOf(_src) - base.startTagEnd : -1; }); - const ast = computed(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, ts.ScriptTarget.Latest)); + const ast = computed(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, 99 satisfies ts.ScriptTarget.Latest)); return mergeObject(base, { get src() { return src(); }, get srcOffset() { return srcOffset(); }, @@ -64,7 +64,7 @@ export function computedSfc( const _generic = generic(); return _generic !== undefined ? untrackedSnapshot().getText(0, base.startTagEnd).lastIndexOf(_generic) - base.startTagEnd : -1; }); - const ast = computed(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, ts.ScriptTarget.Latest)); + const ast = computed(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, 99 satisfies ts.ScriptTarget.Latest)); return mergeObject(base, { get generic() { return generic(); }, get genericOffset() { return genericOffset(); }, diff --git a/packages/language-core/src/virtualFile/embeddedFile.ts b/packages/language-core/src/virtualFile/embeddedFile.ts index 001ec2e871..31d7d646b3 100644 --- a/packages/language-core/src/virtualFile/embeddedFile.ts +++ b/packages/language-core/src/virtualFile/embeddedFile.ts @@ -1,16 +1,15 @@ -import { FileCapabilities, FileKind, FileRangeCapabilities, MirrorBehaviorCapabilities } from '@volar/language-core'; -import { Mapping, Segment, StackNode } from '@volar/source-map'; +import { Mapping, StackNode, VirtualFile } from '@volar/language-core'; +import { Code } from '../types'; export class VueEmbeddedFile { public parentFileName?: string; - public kind = FileKind.TextFile; - public capabilities: FileCapabilities = {}; - public mirrorBehaviorMappings: Mapping<[MirrorBehaviorCapabilities, MirrorBehaviorCapabilities]>[] = []; + public typescript: VirtualFile['typescript']; + public linkedCodeMappings: Mapping[] = []; constructor( public fileName: string, - public content: Segment[], + public content: Code[], public contentStacks: StackNode[], ) { } } diff --git a/packages/language-core/src/virtualFile/vueFile.ts b/packages/language-core/src/virtualFile/vueFile.ts index c0c0ab48fb..7a48859032 100644 --- a/packages/language-core/src/virtualFile/vueFile.ts +++ b/packages/language-core/src/virtualFile/vueFile.ts @@ -1,5 +1,4 @@ -import { FileCapabilities, FileKind, VirtualFile, forEachEmbeddedFile } from '@volar/language-core'; -import { Stack } from '@volar/source-map'; +import { Stack, VirtualFile, forEachEmbeddedFile } from '@volar/language-core'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { VueCompilerOptions, VueLanguagePlugin } from '../types'; import { computedFiles } from './computedFiles'; @@ -25,20 +24,16 @@ export class VueFile implements VirtualFile { // others - capabilities = FileCapabilities.full; - kind = FileKind.TextFile; codegenStacks: Stack[] = []; get embeddedFiles() { return this.getEmbeddedFiles(); } - get mainScriptName() { - let res: string = ''; - forEachEmbeddedFile(this, file => { - if (file.kind === FileKind.TypeScriptHostFile && file.fileName.replace(this.fileName, '').match(jsxReg)) { - res = file.fileName; + get mainTsFile() { + for (const file of forEachEmbeddedFile(this)) { + if (file.typescript && file.fileName.substring(this.fileName.length).match(jsxReg)) { + return file; } - }); - return res; + } } get snapshot() { return this._snapshot(); @@ -49,6 +44,7 @@ export class VueFile implements VirtualFile { constructor( public fileName: string, + public languageId: string, public initSnapshot: ts.IScriptSnapshot, public vueCompilerOptions: VueCompilerOptions, public plugins: ReturnType[], diff --git a/packages/language-plugin-pug/package.json b/packages/language-plugin-pug/package.json index 7de2260710..14a77e56c2 100644 --- a/packages/language-plugin-pug/package.json +++ b/packages/language-plugin-pug/package.json @@ -17,7 +17,7 @@ "@vue/language-core": "1.8.26" }, "dependencies": { - "@volar/source-map": "~1.11.1", - "volar-service-pug": "0.0.17" + "@volar/source-map": "2.0.0-alpha.12", + "volar-service-pug": "0.0.25" } } diff --git a/packages/language-plugin-pug/src/index.ts b/packages/language-plugin-pug/src/index.ts index d6e078a922..63a5cea945 100644 --- a/packages/language-plugin-pug/src/index.ts +++ b/packages/language-plugin-pug/src/index.ts @@ -1,5 +1,5 @@ import type { VueLanguagePlugin } from '@vue/language-core'; -import * as pug from 'volar-service-pug/out/languageService'; +import * as pug from 'volar-service-pug/lib/languageService'; import { SourceMap } from '@volar/source-map'; const plugin: VueLanguagePlugin = ({ modules }) => { @@ -38,7 +38,7 @@ const plugin: VueLanguagePlugin = ({ modules }) => { get(target, prop) { if (prop === 'offset') { const htmlOffset = target.offset; - for (const mapped of map.toSourceOffsets(htmlOffset)) { + for (const mapped of map.getSourceOffsets(htmlOffset)) { return mapped[0]; } return -1; diff --git a/packages/language-server/bin/vue-language-server.js b/packages/language-server/bin/vue-language-server.js index 690f1c0429..e83a6216f1 100755 --- a/packages/language-server/bin/vue-language-server.js +++ b/packages/language-server/bin/vue-language-server.js @@ -1,8 +1,7 @@ #!/usr/bin/env node -if (process.argv.includes("--version")) { - const pkgJSON = require("../package.json"); - console.log(`${pkgJSON["version"]}`); +if (process.argv.includes('--version')) { + console.log(require('../package.json').version); } else { - require("../out/nodeServer.js"); + require('../out/nodeServer.js'); } diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 7e1b8aa360..cea7305c35 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -16,9 +16,9 @@ "directory": "packages/language-server" }, "dependencies": { - "@volar/language-core": "~1.11.1", - "@volar/language-server": "~1.11.1", - "@volar/typescript": "~1.11.1", + "@volar/language-core": "2.0.0-alpha.12", + "@volar/language-server": "2.0.0-alpha.12", + "@volar/typescript": "2.0.0-alpha.12", "@vue/language-core": "1.8.26", "@vue/language-service": "1.8.26", "vscode-languageserver-protocol": "^3.17.5", diff --git a/packages/language-server/src/languageServerPlugin.ts b/packages/language-server/src/languageServerPlugin.ts deleted file mode 100644 index 3cba8789e4..0000000000 --- a/packages/language-server/src/languageServerPlugin.ts +++ /dev/null @@ -1,148 +0,0 @@ -import * as embedded from '@volar/language-core'; -import { LanguageServerPlugin, Connection } from '@volar/language-server'; -import * as vue from '@vue/language-service'; -import * as vue2 from '@vue/language-core'; -import * as nameCasing from '@vue/language-service'; -import { DetectNameCasingRequest, GetConvertAttrCasingEditsRequest, GetConvertTagCasingEditsRequest, ParseSFCRequest, GetComponentMeta, GetDragAndDragImportEditsRequest } from './protocol'; -import { VueServerInitializationOptions } from './types'; -import type * as ts from 'typescript/lib/tsserverlibrary'; -import * as componentMeta from 'vue-component-meta/out/base'; -import { VueCompilerOptions } from '@vue/language-core'; -import { createSys } from '@volar/typescript'; - -export function createServerPlugin(connection: Connection) { - - const plugin: LanguageServerPlugin = (initOptions: VueServerInitializationOptions, modules): ReturnType => { - - if (!modules.typescript) { - console.warn('No typescript found, vue-language-server will not work.'); - return {}; - } - - const ts = modules.typescript; - const vueFileExtensions: string[] = ['vue']; - const hostToVueOptions = new WeakMap(); - - if (initOptions.additionalExtensions) { - for (const additionalExtension of initOptions.additionalExtensions) { - vueFileExtensions.push(additionalExtension); - } - } - - return { - extraFileExtensions: vueFileExtensions.map(ext => ({ extension: ext, isMixedContent: true, scriptKind: ts.ScriptKind.Deferred })), - watchFileExtensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'json', ...vueFileExtensions], - async resolveConfig(config, ctx) { - - const vueOptions = await getVueCompilerOptions(); - - if (ctx) { - hostToVueOptions.set(ctx.host, vue.resolveVueCompilerOptions(vueOptions)); - } - - return vue.resolveConfig( - ts, - config, - ctx?.host.getCompilationSettings() ?? {}, - vueOptions, - initOptions.codegenStack, - ); - - async function getVueCompilerOptions() { - - const ts = modules.typescript; - - let vueOptions: Partial = {}; - - if (ts && ctx) { - const sys = createSys(ts, ctx.env); - let sysVersion: number | undefined; - let newSysVersion = await sys.sync(); - - while (sysVersion !== newSysVersion) { - sysVersion = newSysVersion; - if (typeof ctx?.project.tsConfig === 'string' && ts) { - vueOptions = vue2.createParsedCommandLine(ts, sys, ctx.project.tsConfig).vueOptions; - } - else if (typeof ctx?.project.tsConfig === 'object' && ts) { - vueOptions = vue2.createParsedCommandLineByJson(ts, sys, ctx.host.rootPath, ctx.project.tsConfig).vueOptions; - } - newSysVersion = await sys.sync(); - } - } - - vueOptions.extensions = [ - ...vueOptions.extensions ?? ['.vue'], - ...vueFileExtensions.map(ext => '.' + ext), - ]; - vueOptions.extensions = [...new Set(vueOptions.extensions)]; - - return vueOptions; - } - }, - onInitialized(getService, env) { - - connection.onRequest(ParseSFCRequest.type, params => { - return vue2.parse(params); - }); - - connection.onRequest(DetectNameCasingRequest.type, async params => { - const languageService = await getService(params.textDocument.uri); - if (languageService) { - return nameCasing.detect(ts, languageService.context, params.textDocument.uri, hostToVueOptions.get(languageService.context.rawHost)!); - } - }); - - connection.onRequest(GetConvertTagCasingEditsRequest.type, async params => { - const languageService = await getService(params.textDocument.uri); - if (languageService) { - return nameCasing.convertTagName(ts, languageService.context, params.textDocument.uri, params.casing, hostToVueOptions.get(languageService.context.rawHost)!); - } - }); - - connection.onRequest(GetDragAndDragImportEditsRequest.type, async params => { - const languageService = await getService(params.uri); - if (languageService) { - return nameCasing.getDragImportEdits(ts, languageService.context, params.uri, params.importUri, params.casing); - } - }); - - connection.onRequest(GetConvertAttrCasingEditsRequest.type, async params => { - const languageService = await getService(params.textDocument.uri); - if (languageService) { - const vueOptions = hostToVueOptions.get(languageService.context.host); - if (vueOptions) { - return nameCasing.convertAttrName(ts, languageService.context, params.textDocument.uri, params.casing, hostToVueOptions.get(languageService.context.rawHost)!); - } - } - }); - - const checkers = new WeakMap(); - - connection.onRequest(GetComponentMeta.type, async params => { - - const languageService = await getService(params.uri); - if (!languageService) - return; - - const host = languageService.context.rawHost; - - let checker = checkers.get(host); - if (!checker) { - checker = componentMeta.baseCreate( - ts, - host, - hostToVueOptions.get(host)!, - {}, - host.rootPath + '/tsconfig.json.global.vue', - ); - checkers.set(host, checker); - } - return checker.getComponentMeta(env.uriToFileName(params.uri)); - }); - }, - }; - }; - - return plugin; -} diff --git a/packages/language-server/src/nodeServer.ts b/packages/language-server/src/nodeServer.ts index f1b9409fdd..0f502b84ba 100644 --- a/packages/language-server/src/nodeServer.ts +++ b/packages/language-server/src/nodeServer.ts @@ -1,7 +1,152 @@ -import { createConnection, startLanguageServer } from '@volar/language-server/node'; -import { createServerPlugin } from './languageServerPlugin'; +import { createConnection, createNodeServer, createSimpleProjectProvider, createTypeScriptProjectProvider } from '@volar/language-server/node'; +import { ServerProject } from '@volar/language-server'; +import * as vue2 from '@vue/language-core'; +import { VueCompilerOptions } from '@vue/language-core'; +import * as nameCasing from '@vue/language-service'; +import * as vue from '@vue/language-service'; +import * as componentMeta from 'vue-component-meta/out/base'; +import { DetectNameCasingRequest, GetComponentMeta, GetConvertAttrCasingEditsRequest, GetConvertTagCasingEditsRequest, ParseSFCRequest } from './protocol'; +import { VueInitializationOptions } from './types'; +import { createSys } from '@volar/typescript'; const connection = createConnection(); -const plugin = createServerPlugin(connection); +const server = createNodeServer(connection); +const checkers = new WeakMap(); +const envToVueOptions = new WeakMap(); -startLanguageServer(connection, plugin); +connection.listen(); + +connection.onInitialize(params => { + + const options: VueInitializationOptions = params.initializationOptions; + const vueFileExtensions: string[] = ['vue']; + + if (options.vue?.additionalExtensions) { + for (const additionalExtension of options.vue.additionalExtensions) { + vueFileExtensions.push(additionalExtension); + } + } + + return server.initialize( + params, + options.vue?.hybridMode ? createSimpleProjectProvider : createTypeScriptProjectProvider, + { + watchFileExtensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'json', ...vueFileExtensions], + getServicePlugins() { + const ts = getTsLib(); + const services = vue.resolveServices({}, ts, env => envToVueOptions.get(env)!); + + return Object.values(services); + }, + async getLanguagePlugins(serviceEnv, projectContext) { + const ts = getTsLib(); + const [commandLine, vueOptions] = await parseCommandLine(); + const resolvedVueOptions = vue.resolveVueCompilerOptions(vueOptions); + const languages = vue.resolveLanguages({}, ts, commandLine?.options ?? {}, resolvedVueOptions, options.codegenStack); + + envToVueOptions.set(serviceEnv, resolvedVueOptions); + + return Object.values(languages); + + async function parseCommandLine() { + + let commandLine: vue2.ParsedCommandLine | undefined; + let vueOptions: Partial = {}; + + if (projectContext.typescript) { + const sys = createSys(ts, serviceEnv, serviceEnv.uriToFileName(serviceEnv.workspaceFolder.toString())); + let sysVersion: number | undefined; + let newSysVersion = await sys.sync(); + + while (sysVersion !== newSysVersion) { + sysVersion = newSysVersion; + if (projectContext.typescript.configFileName) { + commandLine = vue2.createParsedCommandLine(ts, sys, projectContext.typescript.configFileName); + } + newSysVersion = await sys.sync(); + } + } + + if (commandLine) { + vueOptions = commandLine.vueOptions; + } + vueOptions.extensions = [ + ...vueOptions.extensions ?? ['.vue'], + ...vueFileExtensions.map(ext => '.' + ext), + ]; + vueOptions.extensions = [...new Set(vueOptions.extensions)]; + + return [commandLine, vueOptions] as const; + } + }, + }, + ); +}); + +connection.onInitialized(() => { + server.initialized(); +}); + +connection.onShutdown(() => { + server.shutdown(); +}); + +connection.onRequest(ParseSFCRequest.type, params => { + return vue2.parse(params); +}); + +connection.onRequest(DetectNameCasingRequest.type, async params => { + const languageService = await getService(params.textDocument.uri); + if (languageService) { + return nameCasing.detect(getTsLib(), languageService.context, params.textDocument.uri, envToVueOptions.get(languageService.context.env)!); + } +}); + +connection.onRequest(GetConvertTagCasingEditsRequest.type, async params => { + const languageService = await getService(params.textDocument.uri); + if (languageService) { + return nameCasing.convertTagName(getTsLib(), languageService.context, params.textDocument.uri, params.casing, envToVueOptions.get(languageService.context.env)!); + } +}); + +connection.onRequest(GetConvertAttrCasingEditsRequest.type, async params => { + const languageService = await getService(params.textDocument.uri); + if (languageService) { + const vueOptions = envToVueOptions.get(languageService.context.env); + if (vueOptions) { + return nameCasing.convertAttrName(getTsLib(), languageService.context, params.textDocument.uri, params.casing, envToVueOptions.get(languageService.context.env)!); + } + } +}); + +connection.onRequest(GetComponentMeta.type, async params => { + + const project = await server.projects.getProject(params.uri); + const langaugeService = project.getLanguageService(); + + let checker = checkers.get(project); + if (!checker) { + checker = componentMeta.baseCreate( + getTsLib(), + langaugeService.context.language.typescript!.configFileName, + langaugeService.context.language.typescript!.projectHost, + envToVueOptions.get(langaugeService.context.env)!, + {}, + langaugeService.context.language.typescript!.languageServiceHost.getCurrentDirectory() + '/tsconfig.json.global.vue', + ); + checkers.set(project, checker); + } + return checker?.getComponentMeta(langaugeService.context.env.uriToFileName(params.uri)); +}); + +function getTsLib() { + const ts = server.modules.typescript; + if (!ts) { + throw 'typescript not found'; + } + return ts; +} + +async function getService(uri: string) { + return (await server.projects.getProject(uri)).getLanguageService(); +} diff --git a/packages/language-server/src/protocol.ts b/packages/language-server/src/protocol.ts index e7638c67f8..a36d7c0752 100644 --- a/packages/language-server/src/protocol.ts +++ b/packages/language-server/src/protocol.ts @@ -31,21 +31,6 @@ export namespace GetConvertTagCasingEditsRequest { export const type = new vscode.RequestType('vue/convertTagNameCasing'); } -export namespace GetDragAndDragImportEditsRequest { - export type ParamsType = { - uri: string, - importUri: string, - casing: TagNameCasing, - }; - export type ResponseType = { - insertText: string; - insertTextFormat: vscode.InsertTextFormat; - additionalEdits: vscode.TextEdit[]; - } | null | undefined; - export type ErrorType = never; - export const type = new vscode.RequestType('vue/dragImportEdits'); -} - export namespace GetConvertAttrCasingEditsRequest { export type ParamsType = { textDocument: vscode.TextDocumentIdentifier, diff --git a/packages/language-server/src/types.ts b/packages/language-server/src/types.ts index f1b17acf48..2d1efe03b8 100644 --- a/packages/language-server/src/types.ts +++ b/packages/language-server/src/types.ts @@ -1,8 +1,11 @@ import { InitializationOptions } from "@volar/language-server"; -export type VueServerInitializationOptions = InitializationOptions & { - /** - * @example ['vue1', 'vue2'] - */ - additionalExtensions?: string[]; +export type VueInitializationOptions = InitializationOptions & { + vue?: { + /** + * @example ['vue1', 'vue2'] + */ + additionalExtensions?: string[]; + hybridMode?: boolean; + }; }; diff --git a/packages/language-server/src/webServer.ts b/packages/language-server/src/webServer.ts deleted file mode 100644 index d64a772dc3..0000000000 --- a/packages/language-server/src/webServer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createConnection, startLanguageServer } from '@volar/language-server/browser'; -import { createServerPlugin } from './languageServerPlugin'; - -const connection = createConnection(); -const plugin = createServerPlugin(connection); - -startLanguageServer(connection, plugin); diff --git a/packages/language-service/package.json b/packages/language-service/package.json index 2622ae73cd..d0a5aab70c 100644 --- a/packages/language-service/package.json +++ b/packages/language-service/package.json @@ -17,29 +17,29 @@ "update-html-data": "node ./scripts/update-html-data.js" }, "dependencies": { - "@volar/language-core": "~1.11.1", - "@volar/language-service": "~1.11.1", - "@volar/typescript": "~1.11.1", + "@volar/language-core": "2.0.0-alpha.12", + "@volar/language-service": "2.0.0-alpha.12", + "@volar/typescript": "2.0.0-alpha.12", "@vue/compiler-dom": "^3.3.0", "@vue/language-core": "1.8.26", "@vue/shared": "^3.3.0", "computeds": "^0.0.1", "path-browserify": "^1.0.1", - "volar-service-css": "0.0.17", - "volar-service-emmet": "0.0.17", - "volar-service-html": "0.0.17", - "volar-service-json": "0.0.17", - "volar-service-pug": "0.0.17", - "volar-service-pug-beautify": "0.0.17", - "volar-service-typescript": "0.0.17", - "volar-service-typescript-twoslash-queries": "0.0.17", + "volar-service-css": "0.0.25", + "volar-service-emmet": "0.0.25", + "volar-service-html": "0.0.25", + "volar-service-json": "0.0.25", + "volar-service-pug": "0.0.25", + "volar-service-pug-beautify": "0.0.25", + "volar-service-typescript": "0.0.25", + "volar-service-typescript-twoslash-queries": "0.0.25", "vscode-html-languageservice": "^5.1.0", "vscode-languageserver-textdocument": "^1.0.11" }, "devDependencies": { "@types/node": "latest", "@types/path-browserify": "latest", - "@volar/kit": "~1.11.1", + "@volar/kit": "2.0.0-alpha.12", "vscode-languageserver-protocol": "^3.17.5", "vscode-uri": "^3.0.8" } diff --git a/packages/language-service/src/helpers.ts b/packages/language-service/src/helpers.ts index 34cfe02890..10e5b38421 100644 --- a/packages/language-service/src/helpers.ts +++ b/packages/language-service/src/helpers.ts @@ -1,10 +1,8 @@ -import * as vue from '@vue/language-core'; -import type { CompilerDOM } from '@vue/language-core'; import * as embedded from '@volar/language-core'; -import { computed } from 'computeds'; -import { sharedTypes } from '@vue/language-core'; +import type { CompilerDOM } from '@vue/language-core'; +import * as vue from '@vue/language-core'; import { camelize, capitalize } from '@vue/shared'; - +import { computed } from 'computeds'; import type * as ts from 'typescript/lib/tsserverlibrary'; export function getPropsByTag( @@ -175,17 +173,15 @@ export function getComponentNames( export function getElementAttrs( ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, - tsLsHost: ts.LanguageServiceHost, + fileName: string, tagName: string, ) { - const sharedTypesFileName = tsLsHost.getCurrentDirectory() + '/' + sharedTypes.baseName; - let tsSourceFile: ts.SourceFile | undefined; - if (tsSourceFile = tsLs.getProgram()?.getSourceFile(sharedTypesFileName)) { + if (tsSourceFile = tsLs.getProgram()?.getSourceFile(fileName)) { - const typeNode = tsSourceFile.statements.find((node): node is ts.TypeAliasDeclaration => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElements'); + const typeNode = tsSourceFile.statements.find((node): node is ts.TypeAliasDeclaration => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElementsCompletion'); const checker = tsLs.getProgram()?.getTypeChecker(); if (checker && typeNode) { @@ -215,14 +211,9 @@ function getVariableType( return; } - let file: embedded.VirtualFile | undefined; - let tsSourceFile: ts.SourceFile | undefined; + const file = sourceFile.mainTsFile; - embedded.forEachEmbeddedFile(sourceFile, embedded => { - if (embedded.fileName === sourceFile.mainScriptName) { - file = embedded; - } - }); + let tsSourceFile: ts.SourceFile | undefined; if (file && (tsSourceFile = tsLs.getProgram()?.getSourceFile(file.fileName))) { @@ -281,7 +272,7 @@ export function getTemplateTagsAndAttrs(sourceFile: embedded.VirtualFile): Tags const ast = sourceFile.sfc.template?.ast; const tags: Tags = new Map(); if (ast) { - vue.walkElementNodes(ast, node => { + for (const node of vue.eachElementNode(ast)) { if (!tags.has(node.tag)) { tags.set(node.tag, { offsets: [], attrs: new Map() }); @@ -323,7 +314,7 @@ export function getTemplateTagsAndAttrs(sourceFile: embedded.VirtualFile): Tags tag.attrs.get(name)!.offsets.push(offset); } } - }); + } } return tags; }); diff --git a/packages/language-service/src/ideFeatures/dragImport.ts b/packages/language-service/src/ideFeatures/dragImport.ts deleted file mode 100644 index 1c4a9f1050..0000000000 --- a/packages/language-service/src/ideFeatures/dragImport.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ServiceContext } from '@volar/language-service'; -import { VueFile } from '@vue/language-core'; -import { camelize, capitalize, hyphenate } from '@vue/shared'; -import * as path from 'path-browserify'; -import type * as vscode from 'vscode-languageserver-protocol'; -import { createAddComponentToOptionEdit, getLastImportNode } from '../plugins/vue-extract-file'; -import { TagNameCasing } from '../types'; - -export function getDragImportEdits( - ts: typeof import('typescript/lib/tsserverlibrary'), - ctx: ServiceContext, - uri: string, - importUri: string, - casing: TagNameCasing -): { - insertText: string; - insertTextFormat: vscode.InsertTextFormat; - additionalEdits: vscode.TextEdit[]; -} | undefined { - - let baseName = importUri.substring(importUri.lastIndexOf('/') + 1); - baseName = baseName.substring(0, baseName.lastIndexOf('.')); - - const newName = capitalize(camelize(baseName)); - const document = ctx!.getTextDocument(uri)!; - const [vueFile] = ctx!.documents.getVirtualFileByUri(document.uri) as [VueFile, any]; - const { sfc } = vueFile; - const script = sfc.scriptSetup ?? sfc.script; - - if (!sfc.template || !script) - return; - - const lastImportNode = getLastImportNode(ts, script.ast); - const edits: vscode.TextEdit[] = [ - { - range: lastImportNode ? { - start: document.positionAt(script.startTagEnd + lastImportNode.end), - end: document.positionAt(script.startTagEnd + lastImportNode.end), - } : { - start: document.positionAt(script.startTagEnd), - end: document.positionAt(script.startTagEnd), - }, - newText: `\nimport ${newName} from './${path.relative(path.dirname(uri), importUri) || importUri.substring(importUri.lastIndexOf('/') + 1)}'`, - }, - ]; - - if (sfc.script) { - const edit = createAddComponentToOptionEdit(ts, sfc.script.ast, newName); - if (edit) { - edits.push({ - range: { - start: document.positionAt(sfc.script.startTagEnd + edit.range.start), - end: document.positionAt(sfc.script.startTagEnd + edit.range.end), - }, - newText: edit.newText, - }); - } - } - - return { - insertText: `<${casing === TagNameCasing.Kebab ? hyphenate(newName) : newName}$0 />`, - insertTextFormat: 2 satisfies typeof vscode.InsertTextFormat.Snippet, - additionalEdits: edits, - }; -} diff --git a/packages/language-service/src/ideFeatures/nameCasing.ts b/packages/language-service/src/ideFeatures/nameCasing.ts index d64264b907..0a860a84b5 100644 --- a/packages/language-service/src/ideFeatures/nameCasing.ts +++ b/packages/language-service/src/ideFeatures/nameCasing.ts @@ -7,13 +7,13 @@ import { AttrNameCasing, TagNameCasing } from '../types'; export async function convertTagName( ts: typeof import('typescript/lib/tsserverlibrary'), - context: ServiceContext, + context: ServiceContext, uri: string, casing: TagNameCasing, vueCompilerOptions: VueCompilerOptions, ) { - const rootFile = context.documents.getSourceByUri(uri)?.root; + const rootFile = context.language.files.getSourceFile(context.env.uriToFileName(uri))?.virtualFile?.[0]; if (!(rootFile instanceof VueFile)) return; @@ -21,9 +21,9 @@ export async function convertTagName( if (!desc.template) return; - const languageService = context.inject('typescript/languageService'); + const languageService = context.inject('typescript/languageService'); const template = desc.template; - const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName); + const document = context.documents.get(context.env.fileNameToUri(rootFile.fileName), rootFile.languageId, rootFile.snapshot); const edits: vscode.TextEdit[] = []; const components = getComponentNames(ts, languageService, rootFile, vueCompilerOptions); const tags = getTemplateTagsAndAttrs(rootFile); @@ -56,7 +56,7 @@ export async function convertAttrName( vueCompilerOptions: VueCompilerOptions, ) { - const rootFile = context.documents.getSourceByUri(uri)?.root; + const rootFile = context.language.files.getSourceFile(context.env.uriToFileName(uri))?.virtualFile?.[0]; if (!(rootFile instanceof VueFile)) return; @@ -64,9 +64,9 @@ export async function convertAttrName( if (!desc.template) return; - const languageService = context.inject('typescript/languageService'); + const languageService = context.inject('typescript/languageService'); const template = desc.template; - const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName); + const document = context.documents.get(context.env.fileNameToUri(rootFile.fileName), rootFile.languageId, rootFile.snapshot); const edits: vscode.TextEdit[] = []; const components = getComponentNames(ts, languageService, rootFile, vueCompilerOptions); const tags = getTemplateTagsAndAttrs(rootFile); @@ -100,14 +100,14 @@ export async function convertAttrName( export async function getNameCasing( ts: typeof import('typescript/lib/tsserverlibrary'), context: ServiceContext, - uri: string, + fileName: string, vueCompilerOptions: VueCompilerOptions, ) { - const detected = detect(ts, context, uri, vueCompilerOptions); + const detected = detect(ts, context, fileName, vueCompilerOptions); const [attr, tag] = await Promise.all([ - context.env.getConfiguration?.<'autoKebab' | 'autoCamel' | 'kebab' | 'camel'>('vue.complete.casing.props', uri), - context.env.getConfiguration?.<'autoKebab' | 'autoPascal' | 'kebab' | 'pascal'>('vue.complete.casing.tags', uri), + context.env.getConfiguration?.<'autoKebab' | 'autoCamel' | 'kebab' | 'camel'>('vue.complete.casing.props', context.env.fileNameToUri(fileName)), + context.env.getConfiguration?.<'autoKebab' | 'autoPascal' | 'kebab' | 'pascal'>('vue.complete.casing.tags', context.env.fileNameToUri(fileName)), ]); const tagNameCasing = detected.tag.length === 1 && (tag === 'autoPascal' || tag === 'autoKebab') ? detected.tag[0] : (tag === 'autoKebab' || tag === 'kebab') ? TagNameCasing.Kebab : TagNameCasing.Pascal; const attrNameCasing = detected.attr.length === 1 && (attr === 'autoCamel' || attr === 'autoKebab') ? detected.attr[0] : (attr === 'autoCamel' || attr === 'camel') ? AttrNameCasing.Camel : AttrNameCasing.Kebab; @@ -121,14 +121,14 @@ export async function getNameCasing( export function detect( ts: typeof import('typescript/lib/tsserverlibrary'), context: ServiceContext, - uri: string, + fileName: string, vueCompilerOptions: VueCompilerOptions, ): { tag: TagNameCasing[], attr: AttrNameCasing[], } { - const rootFile = context.documents.getSourceByUri(uri)?.root; + const rootFile = context.language.files.getSourceFile(fileName)?.virtualFile?.[0]; if (!(rootFile instanceof VueFile)) { return { tag: [], @@ -136,7 +136,7 @@ export function detect( }; } - const languageService = context.inject('typescript/languageService'); + const languageService = context.inject('typescript/languageService'); return { tag: getTagNameCase(rootFile), diff --git a/packages/language-service/src/index.ts b/packages/language-service/src/index.ts index 4626d9053b..75191f5bb7 100644 --- a/packages/language-service/src/index.ts +++ b/packages/language-service/src/index.ts @@ -1,7 +1,6 @@ export * from '@volar/language-service'; export * from '@vue/language-core'; export * from './ideFeatures/nameCasing'; -export * from './ideFeatures/dragImport'; export * from './languageService'; export { TagNameCasing, AttrNameCasing } from './types'; export { Provide } from './plugins/vue'; diff --git a/packages/language-service/src/languageService.ts b/packages/language-service/src/languageService.ts index 5b5a92a1da..f357e871ea 100644 --- a/packages/language-service/src/languageService.ts +++ b/packages/language-service/src/languageService.ts @@ -1,273 +1,290 @@ -import { Config, Service, ServiceContext } from '@volar/language-service'; -import { VueFile, createLanguages, hyphenateTag, resolveVueCompilerOptions, scriptRanges } from '@vue/language-core'; +import { ServiceEnvironment, ServicePlugin } from '@volar/language-service'; +import { LanguagePlugin, VueFile, createLanguages, hyphenateTag, scriptRanges } from '@vue/language-core'; import { capitalize } from '@vue/shared'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import type { Data } from 'volar-service-typescript/out/features/completions/basic'; +import type { Data } from 'volar-service-typescript/lib/features/completions/basic'; import type * as html from 'vscode-html-languageservice'; import type * as vscode from 'vscode-languageserver-protocol'; import { getNameCasing } from './ideFeatures/nameCasing'; import { TagNameCasing, VueCompilerOptions } from './types'; // volar services -import * as CssService from 'volar-service-css'; -import * as EmmetService from 'volar-service-emmet'; -import * as HtmlService from 'volar-service-html'; -import * as JsonService from 'volar-service-json'; -import * as PugService from 'volar-service-pug'; -import * as PugFormatService from 'volar-service-pug-beautify'; -import * as TsService from 'volar-service-typescript'; -import * as TsTqService from 'volar-service-typescript-twoslash-queries'; +import { create as createCssService } from 'volar-service-css'; +import { create as createEmmetService } from 'volar-service-emmet'; +import { create as createHtmlService } from 'volar-service-html'; +import { create as createJsonService } from 'volar-service-json'; +import { create as createPugService } from 'volar-service-pug'; +import { create as createPugFormatService } from 'volar-service-pug-beautify'; +import { create as createTsService, Provide as TSProvide } from 'volar-service-typescript'; +import { create as createTsTqService } from 'volar-service-typescript-twoslash-queries'; // our services -import * as VueService from './plugins/vue'; -import * as AutoDotValueService from './plugins/vue-autoinsert-dotvalue'; -import * as AutoWrapParenthesesService from './plugins/vue-autoinsert-parentheses'; -import * as AutoAddSpaceService from './plugins/vue-autoinsert-space'; -import * as ReferencesCodeLensService from './plugins/vue-codelens-references'; -import * as DirectiveCommentsService from './plugins/vue-directive-comments'; -import * as ExtractComponentService from './plugins/vue-extract-file'; -import * as VueTemplateLanguageService from './plugins/vue-template'; -import * as ToggleVBindService from './plugins/vue-toggle-v-bind-codeaction'; -import * as VueTqService from './plugins/vue-twoslash-queries'; -import * as VisualizeHiddenCallbackParamService from './plugins/vue-visualize-hidden-callback-param'; +import { create as createVueService } from './plugins/vue'; +import { create as createDocumentDropService } from './plugins/vue-document-drop'; +import { create as createAutoDotValueService } from './plugins/vue-autoinsert-dotvalue'; +import { create as createAutoWrapParenthesesService } from './plugins/vue-autoinsert-parentheses'; +import { create as createAutoAddSpaceService } from './plugins/vue-autoinsert-space'; +import { create as createReferencesCodeLensService } from './plugins/vue-codelens-references'; +import { create as createDirectiveCommentsService } from './plugins/vue-directive-comments'; +import { create as createVueExtractFileService, createAddComponentToOptionEdit } from './plugins/vue-extract-file'; +import { create as createVueTemplateLanguageService } from './plugins/vue-template'; +import { create as createToggleVBindService } from './plugins/vue-toggle-v-bind-codeaction'; +import { create as createVueTqService } from './plugins/vue-twoslash-queries'; +import { create as createVisualizeHiddenCallbackParamService } from './plugins/vue-visualize-hidden-callback-param'; export interface Settings { - json?: Parameters[0]; + json?: Parameters[0]; } -export function resolveConfig( +export function resolveLanguages( + languages: Record, ts: typeof import('typescript/lib/tsserverlibrary'), - config: Config, - compilerOptions: ts.CompilerOptions = {}, - vueCompilerOptions: Partial = {}, + compilerOptions: ts.CompilerOptions, + vueCompilerOptions: VueCompilerOptions, codegenStack: boolean = false, -) { +): Record { - const resolvedVueCompilerOptions = resolveVueCompilerOptions(vueCompilerOptions); - const vueLanguageModules = createLanguages(ts, compilerOptions, resolvedVueCompilerOptions, codegenStack); + const vueLanguageModules = createLanguages(ts, compilerOptions, vueCompilerOptions, codegenStack); - config.languages = Object.assign({}, vueLanguageModules, config.languages); - config.services = resolvePlugins(config.services, resolvedVueCompilerOptions); - - return config; + return { + ...languages, + ...vueLanguageModules.reduce((obj, module, i) => { + obj['vue_' + i] = module; + return obj; + }, {} as Record), + }; } -function resolvePlugins( - services: Config['services'], - vueCompilerOptions: VueCompilerOptions, +export function resolveServices( + services: Record, + ts: typeof import('typescript/lib/tsserverlibrary'), + getVueOptions: (env: ServiceEnvironment) => VueCompilerOptions, ) { - const originalTsPlugin: Service = services?.typescript ?? TsService.create(); + const tsService: ServicePlugin = services?.typescript ?? createTsService(ts); services ??= {}; - services.typescript = (ctx: ServiceContext | undefined, modules): ReturnType => { - - const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(ctx, modules) : originalTsPlugin; - - if (!ctx || !modules?.typescript) - return base; - - const ts = modules.typescript; - - return { - ...base, - async provideCompletionItems(document, position, context, item) { - const result = await base.provideCompletionItems?.(document, position, context, item); - if (result) { - - // filter __VLS_ - result.items = result.items.filter(item => - item.label.indexOf('__VLS_') === -1 - && (!item.labelDetails?.description || item.labelDetails.description.indexOf('__VLS_') === -1) - ); - - // handle component auto-import patch - let casing: Awaited> | undefined; - - for (const [_, map] of ctx.documents.getMapsByVirtualFileUri(document.uri)) { - const virtualFile = ctx.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (virtualFile instanceof VueFile) { - const isAutoImport = !!map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly); - if (isAutoImport) { - const source = ctx.documents.getVirtualFileByUri(document.uri)[1]; - for (const item of result.items) { - item.data.__isComponentAutoImport = true; - } + services.typescript = { + ...tsService, + create(context) { + const base = tsService.create(context); + return { + ...base, + async provideCompletionItems(document, position, completeContext, item) { + const result = await base.provideCompletionItems?.(document, position, completeContext, item); + if (result) { + + // filter __VLS_ + result.items = result.items.filter(item => + item.label.indexOf('__VLS_') === -1 + && (!item.labelDetails?.description || item.labelDetails.description.indexOf('__VLS_') === -1) + ); + + // handle component auto-import patch + let casing: Awaited> | undefined; + + const [virtualFile, sourceFile] = context.language.files.getVirtualFile(context.env.uriToFileName(document.uri)); + + if (virtualFile && sourceFile) { + + for (const map of context.documents.getMaps(virtualFile)) { + + const sourceVirtualFile = context.language.files.getSourceFile(context.env.uriToFileName(map.sourceFileDocument.uri))?.virtualFile?.[0]; + + if (sourceVirtualFile instanceof VueFile) { + + const isAutoImport = !!map.getSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.onlyImport); + if (isAutoImport) { - // fix #2458 - if (source) { - casing ??= await getNameCasing(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions); - if (casing.tag === TagNameCasing.Kebab) { for (const item of result.items) { - item.filterText = hyphenateTag(item.filterText ?? item.label); + item.data.__isComponentAutoImport = true; + } + + // fix #2458 + casing ??= await getNameCasing(ts, context, sourceFile.fileName, getVueOptions(context.env)); + + if (casing.tag === TagNameCasing.Kebab) { + for (const item of result.items) { + item.filterText = hyphenateTag(item.filterText ?? item.label); + } } } } } } } - } - return result; - }, - async resolveCompletionItem(item, token) { + return result; + }, + async resolveCompletionItem(item, token) { - item = await base.resolveCompletionItem?.(item, token) ?? item; + item = await base.resolveCompletionItem?.(item, token) ?? item; - const itemData = item.data as { uri?: string; } | undefined; + const itemData = item.data as { uri?: string; } | undefined; - let newName: string | undefined; + let newName: string | undefined; - if (itemData?.uri && item.additionalTextEdits) { - patchAdditionalTextEdits(itemData.uri, item.additionalTextEdits); - } + if (itemData?.uri && item.additionalTextEdits) { + patchAdditionalTextEdits(itemData.uri, item.additionalTextEdits); + } - for (const ext of vueCompilerOptions.extensions) { - const suffix = capitalize(ext.substring('.'.length)); // .vue -> Vue - if ( - itemData?.uri - && item.textEdit?.newText.endsWith(suffix) - && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0 - && (await ctx.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true) - ) { - newName = item.textEdit.newText.slice(0, -suffix.length); - newName = newName[0].toUpperCase() + newName.substring(1); - if (newName === 'Index') { - const tsItem = (item.data as Data).originalItem; - if (tsItem.source) { - const dirs = tsItem.source.split('/'); - if (dirs.length >= 3) { - newName = dirs[dirs.length - 2]; - newName = newName[0].toUpperCase() + newName.substring(1); + for (const ext of getVueOptions(context.env).extensions) { + const suffix = capitalize(ext.substring('.'.length)); // .vue -> Vue + if ( + itemData?.uri + && item.textEdit?.newText.endsWith(suffix) + && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0 + && (await context.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true) + ) { + newName = item.textEdit.newText.slice(0, -suffix.length); + newName = newName[0].toUpperCase() + newName.substring(1); + if (newName === 'Index') { + const tsItem = (item.data as Data).originalItem; + if (tsItem.source) { + const dirs = tsItem.source.split('/'); + if (dirs.length >= 3) { + newName = dirs[dirs.length - 2]; + newName = newName[0].toUpperCase() + newName.substring(1); + } } } - } - item.additionalTextEdits[0].newText = item.additionalTextEdits[0].newText.replace( - 'import ' + item.textEdit.newText + ' from ', - 'import ' + newName + ' from ', - ); - item.textEdit.newText = newName; - const source = ctx.documents.getVirtualFileByUri(itemData.uri)[1]; - if (source) { - const casing = await getNameCasing(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions); - if (casing.tag === TagNameCasing.Kebab) { - item.textEdit.newText = hyphenateTag(item.textEdit.newText); + item.additionalTextEdits[0].newText = item.additionalTextEdits[0].newText.replace( + 'import ' + item.textEdit.newText + ' from ', + 'import ' + newName + ' from ', + ); + item.textEdit.newText = newName; + const [_, sourceFile] = context.language.files.getVirtualFile(context.env.uriToFileName(itemData.uri)); + if (sourceFile) { + const casing = await getNameCasing(ts, context, sourceFile.fileName, getVueOptions(context.env)); + if (casing.tag === TagNameCasing.Kebab) { + item.textEdit.newText = hyphenateTag(item.textEdit.newText); + } } } + else if (item.textEdit?.newText && new RegExp(`import \\w*${suffix}\\$1 from [\\S\\s]*`).test(item.textEdit.newText)) { + // https://github.com/vuejs/language-tools/issues/2286 + item.textEdit.newText = item.textEdit.newText.replace(`${suffix}$1`, '$1'); + } } - else if (item.textEdit?.newText && new RegExp(`import \\w*${suffix}\\$1 from [\\S\\s]*`).test(item.textEdit.newText)) { - // https://github.com/vuejs/language-tools/issues/2286 - item.textEdit.newText = item.textEdit.newText.replace(`${suffix}$1`, '$1'); - } - } - const data: Data = item.data; - if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit && itemData?.uri) { - const fileName = ctx.env.uriToFileName(itemData.uri); - const langaugeService = ctx.inject('typescript/languageService'); - const [virtualFile] = ctx.virtualFiles.getVirtualFile(fileName); - const ast = langaugeService.getProgram()?.getSourceFile(fileName); - const exportDefault = ast ? scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined; - if (virtualFile && ast && exportDefault) { - const componentName = newName ?? item.textEdit.newText; - const optionEdit = ExtractComponentService.createAddComponentToOptionEdit(ts, ast, componentName); - if (optionEdit) { - const textDoc = ctx.documents.getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName); - item.additionalTextEdits.push({ - range: { - start: textDoc.positionAt(optionEdit.range.start), - end: textDoc.positionAt(optionEdit.range.end), - }, - newText: optionEdit.newText, - }); + const data: Data = item.data; + if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit && itemData?.uri) { + const fileName = context.env.uriToFileName(itemData.uri); + const langaugeService = context.inject('typescript/languageService'); + const [virtualFile] = context.language.files.getVirtualFile(fileName); + const ast = langaugeService.getProgram()?.getSourceFile(fileName); + const exportDefault = ast ? scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined; + if (virtualFile && ast && exportDefault) { + const componentName = newName ?? item.textEdit.newText; + const optionEdit = createAddComponentToOptionEdit(ts, ast, componentName); + if (optionEdit) { + const textDoc = context.documents.get(context.env.fileNameToUri(virtualFile.fileName), virtualFile.languageId, virtualFile.snapshot); + item.additionalTextEdits.push({ + range: { + start: textDoc.positionAt(optionEdit.range.start), + end: textDoc.positionAt(optionEdit.range.end), + }, + newText: optionEdit.newText, + }); + } } } - } - return item; - }, - async provideCodeActions(document, range, context, token) { - const result = await base.provideCodeActions?.(document, range, context, token); - return result?.filter(codeAction => codeAction.title.indexOf('__VLS_') === -1); - }, - async resolveCodeAction(item, token) { + return item; + }, + async provideCodeActions(document, range, context, token) { + const result = await base.provideCodeActions?.(document, range, context, token); + return result?.filter(codeAction => codeAction.title.indexOf('__VLS_') === -1); + }, + async resolveCodeAction(item, token) { - const result = await base.resolveCodeAction?.(item, token) ?? item; + const result = await base.resolveCodeAction?.(item, token) ?? item; - if (result?.edit?.changes) { - for (const uri in result.edit.changes) { - const edits = result.edit.changes[uri]; - if (edits) { - patchAdditionalTextEdits(uri, edits); + if (result?.edit?.changes) { + for (const uri in result.edit.changes) { + const edits = result.edit.changes[uri]; + if (edits) { + patchAdditionalTextEdits(uri, edits); + } } } - } - if (result?.edit?.documentChanges) { - for (const documentChange of result.edit.documentChanges) { - if ('textDocument' in documentChange) { - patchAdditionalTextEdits(documentChange.textDocument.uri, documentChange.edits); + if (result?.edit?.documentChanges) { + for (const documentChange of result.edit.documentChanges) { + if ('textDocument' in documentChange) { + patchAdditionalTextEdits(documentChange.textDocument.uri, documentChange.edits); + } } } - } - return result; + return result; + }, + async provideSemanticDiagnostics(document, token) { + const result = await base.provideSemanticDiagnostics?.(document, token); + return result?.map(diagnostic => { + if ( + diagnostic.source === 'ts' + && diagnostic.code === 2578 /* Unused '@ts-expect-error' directive. */ + && document.getText(diagnostic.range) === '// @ts-expect-error __VLS_TS_EXPECT_ERROR' + ) { + diagnostic.source = 'vue'; + diagnostic.code = 'ts-2578'; + diagnostic.message = diagnostic.message.replace(/@ts-expect-error/g, '@vue-expect-error'); + } + return diagnostic; + }); + }, + }; + }, + }; + services.html ??= createVueTemplateLanguageService( + ts, + createHtmlService(), + getVueOptions, + { + getScanner: (htmlService, document): html.Scanner | undefined => { + return htmlService.provide['html/languageService']().createScanner(document.getText()); }, - async provideSemanticDiagnostics(document, token) { - const result = await base.provideSemanticDiagnostics?.(document, token); - return result?.map(diagnostic => { - if ( - diagnostic.source === 'ts' - && diagnostic.code === 2578 /* Unused '@ts-expect-error' directive. */ - && document.getText(diagnostic.range) === '// @ts-expect-error __VLS_TS_EXPECT_ERROR' - ) { - diagnostic.source = 'vue'; - diagnostic.code = 'ts-2578'; - diagnostic.message = diagnostic.message.replace(/@ts-expect-error/g, '@vue-expect-error'); - } - return diagnostic; - }); + updateCustomData(htmlService, extraData) { + htmlService.provide['html/updateCustomData'](extraData); }, - }; - }; - services.html ??= VueTemplateLanguageService.create({ - baseService: HtmlService.create(), - getScanner: (htmlService, document): html.Scanner | undefined => { - return htmlService.provide['html/languageService']().createScanner(document.getText()); - }, - updateCustomData(htmlService, extraData) { - htmlService.provide['html/updateCustomData'](extraData); - }, - isSupportedDocument: (document) => document.languageId === 'html', - vueCompilerOptions, - }); - services.pug ??= VueTemplateLanguageService.create({ - baseService: PugService.create(), - getScanner: (pugService, document): html.Scanner | undefined => { - const pugDocument = pugService.provide['pug/pugDocument'](document); - if (pugDocument) { - return pugService.provide['pug/languageService']().createScanner(pugDocument); - } - }, - updateCustomData(pugService, extraData) { - pugService.provide['pug/updateCustomData'](extraData); - }, - isSupportedDocument: (document) => document.languageId === 'jade', - vueCompilerOptions, - }); - services.vue ??= VueService.create(); - services.css ??= CssService.create(); - services['pug-beautify'] ??= PugFormatService.create(); - services.json ??= JsonService.create(); - services['typescript/twoslash-queries'] ??= TsTqService.create(); - services['vue/referencesCodeLens'] ??= ReferencesCodeLensService.create(); - services['vue/autoInsertDotValue'] ??= AutoDotValueService.create(); - services['vue/twoslash-queries'] ??= VueTqService.create(); - services['vue/autoInsertParentheses'] ??= AutoWrapParenthesesService.create(); - services['vue/autoInsertSpaces'] ??= AutoAddSpaceService.create(); - services['vue/visualizeHiddenCallbackParam'] ??= VisualizeHiddenCallbackParamService.create(); - services['vue/directiveComments'] ??= DirectiveCommentsService.create(); - services['vue/extractComponent'] ??= ExtractComponentService.create(); - services['vue/toggleVBind'] ??= ToggleVBindService.create(); - services.emmet ??= EmmetService.create(); + isSupportedDocument: (document) => document.languageId === 'html', + } + ); + services.pug ??= createVueTemplateLanguageService( + ts, + createPugService(), + getVueOptions, + { + getScanner: (pugService, document): html.Scanner | undefined => { + const pugDocument = pugService.provide['pug/pugDocument'](document); + if (pugDocument) { + return pugService.provide['pug/languageService']().createScanner(pugDocument); + } + }, + updateCustomData(pugService, extraData) { + pugService.provide['pug/updateCustomData'](extraData); + }, + isSupportedDocument: (document) => document.languageId === 'jade', + } + ); + services.vue ??= createVueService(); + services.css ??= createCssService(); + services['pug-beautify'] ??= createPugFormatService(); + services.json ??= createJsonService(); + services['typescript/twoslash-queries'] ??= createTsTqService(); + services['vue/referencesCodeLens'] ??= createReferencesCodeLensService(); + services['vue/documentDrop'] ??= createDocumentDropService(ts); + services['vue/autoInsertDotValue'] ??= createAutoDotValueService(ts); + services['vue/twoslash-queries'] ??= createVueTqService(ts); + services['vue/autoInsertParentheses'] ??= createAutoWrapParenthesesService(ts); + services['vue/autoInsertSpaces'] ??= createAutoAddSpaceService(); + services['vue/visualizeHiddenCallbackParam'] ??= createVisualizeHiddenCallbackParamService(); + services['vue/directiveComments'] ??= createDirectiveCommentsService(); + services['vue/extractComponent'] ??= createVueExtractFileService(ts); + services['vue/toggleVBind'] ??= createToggleVBindService(ts); + services.emmet ??= createEmmetService(); + + services.html.name += ' (html)'; + services.pug.name += ' (pug)'; return services; } diff --git a/packages/language-service/src/plugins/vue-autoinsert-dotvalue.ts b/packages/language-service/src/plugins/vue-autoinsert-dotvalue.ts index 336863dff1..b50b3a9097 100644 --- a/packages/language-service/src/plugins/vue-autoinsert-dotvalue.ts +++ b/packages/language-service/src/plugins/vue-autoinsert-dotvalue.ts @@ -1,83 +1,80 @@ -import { AutoInsertionContext, Service, ServiceContext } from '@volar/language-service'; +import { ServicePlugin, ServicePluginInstance } from '@volar/language-service'; import { hyphenateAttr } from '@vue/language-core'; import type * as ts from 'typescript/lib/tsserverlibrary'; +import { Provide } from 'volar-service-typescript'; import type * as vscode from 'vscode-languageserver-protocol'; import type { TextDocument } from 'vscode-languageserver-textdocument'; -const plugin: Service = (context: ServiceContext | undefined, modules) => { - - if (!modules?.typescript) - return {}; - - const ts = modules.typescript; - +export function create(ts: typeof import('typescript/lib/tsserverlibrary')): ServicePlugin { return { + name: 'vue-autoinsert-dotvalue', + create(context): ServicePluginInstance { + return { + async provideAutoInsertionEdit(document, position, lastChange) { - async provideAutoInsertionEdit(document, position, insertContext) { + if (!isTsDocument(document)) + return; - if (!isTsDocument(document)) - return; + if (!isCharacterTyping(document, lastChange)) + return; - if (!isCharacterTyping(document, insertContext)) - return; + const enabled = await context.env.getConfiguration?.('vue.autoInsert.dotValue') ?? true; + if (!enabled) + return; - const enabled = await context!.env.getConfiguration?.('vue.autoInsert.dotValue') ?? true; - if (!enabled) - return; + const program = context.inject('typescript/languageService').getProgram(); + if (!program) + return; - const program = context!.inject('typescript/languageService').getProgram(); - if (!program) - return; + const sourceFile = program.getSourceFile(context.env.uriToFileName(document.uri)); + if (!sourceFile) + return; - const sourceFile = program.getSourceFile(context!.env.uriToFileName(document.uri)); - if (!sourceFile) - return; + if (isBlacklistNode(ts, sourceFile, document.offsetAt(position), false)) + return; - if (isBlacklistNode(ts, sourceFile, document.offsetAt(position), false)) - return; + const node = findPositionIdentifier(sourceFile, sourceFile, document.offsetAt(position)); + if (!node) + return; - const node = findPositionIdentifier(sourceFile, sourceFile, document.offsetAt(position)); - if (!node) - return; + const token = context.inject('typescript/languageServiceHost').getCancellationToken?.(); + if (token) { + context.inject('typescript/languageService').getQuickInfoAtPosition(context.env.uriToFileName(document.uri), node.end); + if (token?.isCancellationRequested()) { + return; // check cancel here because type checker do not use cancel token + } + } - const token = context!.inject('typescript/languageServiceHost').getCancellationToken?.(); - if (token) { - context!.inject('typescript/languageService').getQuickInfoAtPosition(context!.env.uriToFileName(document.uri), node.end); - if (token?.isCancellationRequested()) { - return; // check cancel here because type checker do not use cancel token - } - } + const checker = program.getTypeChecker(); + const type = checker.getTypeAtLocation(node); + const props = type.getProperties(); - const checker = program.getTypeChecker(); - const type = checker.getTypeAtLocation(node); - const props = type.getProperties(); + if (props.some(prop => prop.name === 'value')) { + return '${1:.value}'; + } - if (props.some(prop => prop.name === 'value')) { - return '${1:.value}'; - } + function findPositionIdentifier(sourceFile: ts.SourceFile, node: ts.Node, offset: number) { - function findPositionIdentifier(sourceFile: ts.SourceFile, node: ts.Node, offset: number) { + let result: ts.Node | undefined; - let result: ts.Node | undefined; + node.forEachChild(child => { + if (!result) { + if (child.end === offset && ts.isIdentifier(child)) { + result = child; + } + else if (child.end >= offset && child.getStart(sourceFile) < offset) { + result = findPositionIdentifier(sourceFile, child, offset); + } + } + }); - node.forEachChild(child => { - if (!result) { - if (child.end === offset && ts.isIdentifier(child)) { - result = child; - } - else if (child.end >= offset && child.getStart(sourceFile) < offset) { - result = findPositionIdentifier(sourceFile, child, offset); - } + return result; } - }); - - return result; - } + }, + }; }, }; -}; - -export const create = () => plugin; +} function isTsDocument(document: TextDocument) { return document.languageId === 'javascript' || @@ -88,13 +85,13 @@ function isTsDocument(document: TextDocument) { const charReg = /\w/; -export function isCharacterTyping(document: TextDocument, options: AutoInsertionContext) { +export function isCharacterTyping(document: TextDocument, lastChange: { range: vscode.Range; text: string; }) { - const lastCharacter = options.lastChange.text[options.lastChange.text.length - 1]; - const rangeStart = options.lastChange.range.start; + const lastCharacter = lastChange.text[lastChange.text.length - 1]; + const rangeStart = lastChange.range.start; const position: vscode.Position = { line: rangeStart.line, - character: rangeStart.character + options.lastChange.text.length, + character: rangeStart.character + lastChange.text.length, }; const nextCharacter = document.getText({ start: position, @@ -104,7 +101,7 @@ export function isCharacterTyping(document: TextDocument, options: AutoInsertion if (lastCharacter === undefined) { // delete text return false; } - if (options.lastChange.text.indexOf('\n') >= 0) { // multi-line change + if (lastChange.text.indexOf('\n') >= 0) { // multi-line change return false; } diff --git a/packages/language-service/src/plugins/vue-autoinsert-parentheses.ts b/packages/language-service/src/plugins/vue-autoinsert-parentheses.ts index fba00b5f71..6cf494a34b 100644 --- a/packages/language-service/src/plugins/vue-autoinsert-parentheses.ts +++ b/packages/language-service/src/plugins/vue-autoinsert-parentheses.ts @@ -1,79 +1,72 @@ -import { Service } from '@volar/language-service'; +import { ServicePlugin, ServicePluginInstance } from '@volar/language-service'; import { isCharacterTyping } from './vue-autoinsert-dotvalue'; -const plugin: Service = (context, modules) => { - - if (!context) { - return {}; - } - - if (!modules?.typescript) { - return {}; - } - - const ts = modules.typescript; - +export function create(ts: typeof import('typescript/lib/tsserverlibrary')): ServicePlugin { return { + name: 'vue-autoinsert-parentheses', + create(context): ServicePluginInstance { + return { + async provideAutoInsertionEdit(document, position, lastChange) { - async provideAutoInsertionEdit(document, position, options_2) { - - const enabled = await context.env.getConfiguration?.('vue.autoInsert.parentheses') ?? false; - if (!enabled) - return; + const enabled = await context.env.getConfiguration?.('vue.autoInsert.parentheses') ?? false; + if (!enabled) + return; - if (!isCharacterTyping(document, options_2)) - return; + if (!isCharacterTyping(document, lastChange)) + return; - const [virtualFile] = context.documents.getVirtualFileByUri(document.uri); - if (!virtualFile?.fileName.endsWith('.template_format.ts')) - return; + const [virtualFile] = context.language.files.getVirtualFile(context.env.uriToFileName(document.uri)); + if (!virtualFile?.fileName.endsWith('.template_format.ts')) + return; - const offset = document.offsetAt(position); + const offset = document.offsetAt(position); - for (const mappedRange of virtualFile.mappings) { - if (mappedRange.generatedRange[1] === offset) { - const text = document.getText().substring(mappedRange.generatedRange[0], mappedRange.generatedRange[1]); - const ast = ts.createSourceFile(virtualFile.fileName, text, ts.ScriptTarget.Latest); - if (ast.statements.length === 1) { - const statement = ast.statements[0]; - if ( - ts.isExpressionStatement(statement) - && ( - ( - ts.isAsExpression(statement.expression) - && ts.isTypeReferenceNode(statement.expression.type) - && ts.isIdentifier(statement.expression.type.typeName) - && statement.expression.type.typeName.text - ) - || ( - ts.isBinaryExpression(statement.expression) - && statement.expression.right.getText(ast) - && statement.expression.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword - ) - || ( - ts.isTypeOfExpression(statement.expression) - && statement.expression.expression.getText(ast) - ) - ) - ) { - // https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar - const escapedText = text - .replaceAll('\\', '\\\\') - .replaceAll('$', '\\$') - .replaceAll('}', '\\}'); - return { - range: { - start: document.positionAt(mappedRange.generatedRange[0]), - end: document.positionAt(mappedRange.generatedRange[1]), - }, - newText: '(' + escapedText + '$0' + ')', - }; + for (const mappedRange of virtualFile.mappings) { + const generatedCodeEnd = mappedRange.generatedOffsets[mappedRange.generatedOffsets.length - 1] + + mappedRange.lengths[mappedRange.lengths.length - 1]; + if (generatedCodeEnd === offset) { + const text = document.getText().substring(mappedRange.generatedOffsets[0], generatedCodeEnd); + const ast = ts.createSourceFile(virtualFile.fileName, text, ts.ScriptTarget.Latest); + if (ast.statements.length === 1) { + const statement = ast.statements[0]; + if ( + ts.isExpressionStatement(statement) + && ( + ( + ts.isAsExpression(statement.expression) + && ts.isTypeReferenceNode(statement.expression.type) + && ts.isIdentifier(statement.expression.type.typeName) + && statement.expression.type.typeName.text + ) + || ( + ts.isBinaryExpression(statement.expression) + && statement.expression.right.getText(ast) + && statement.expression.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword + ) + || ( + ts.isTypeOfExpression(statement.expression) + && statement.expression.expression.getText(ast) + ) + ) + ) { + // https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar + const escapedText = text + .replaceAll('\\', '\\\\') + .replaceAll('$', '\\$') + .replaceAll('}', '\\}'); + return { + range: { + start: document.positionAt(mappedRange.generatedOffsets[0]), + end: document.positionAt(generatedCodeEnd), + }, + newText: '(' + escapedText + '$0' + ')', + }; + } + } } } - } - } + }, + }; }, }; -}; - -export const create = () => plugin; +} diff --git a/packages/language-service/src/plugins/vue-autoinsert-space.ts b/packages/language-service/src/plugins/vue-autoinsert-space.ts index 5b71baa6bc..2738fa8661 100644 --- a/packages/language-service/src/plugins/vue-autoinsert-space.ts +++ b/packages/language-service/src/plugins/vue-autoinsert-space.ts @@ -1,38 +1,36 @@ -import { Service } from '@volar/language-service'; - -const plugin: Service = (context): ReturnType => { - - if (!context) - return {}; +import { ServicePlugin, ServicePluginInstance } from '@volar/language-service'; +export function create(): ServicePlugin { return { - - async provideAutoInsertionEdit(document, _, { lastChange }) { - - if (document.languageId === 'html' || document.languageId === 'jade') { - - const enabled = await context.env.getConfiguration?.('vue.autoInsert.bracketSpacing') ?? true; - if (!enabled) - return; - - if ( - lastChange.text === '{}' - && document.getText({ - start: { line: lastChange.range.start.line, character: lastChange.range.start.character - 1 }, - end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 3 } - }) === '{{}}' - ) { - return { - newText: ` $0 `, - range: { - start: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 }, - end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 } - }, - }; - } - } + name: 'vue-autoinsert-space', + create(context): ServicePluginInstance { + return { + async provideAutoInsertionEdit(document, _, lastChange) { + + if (document.languageId === 'html' || document.languageId === 'jade') { + + const enabled = await context.env.getConfiguration?.('vue.autoInsert.bracketSpacing') ?? true; + if (!enabled) + return; + + if ( + lastChange.text === '{}' + && document.getText({ + start: { line: lastChange.range.start.line, character: lastChange.range.start.character - 1 }, + end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 3 } + }) === '{{}}' + ) { + return { + newText: ` $0 `, + range: { + start: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 }, + end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 } + }, + }; + } + } + }, + }; }, }; -}; - -export const create = () => plugin; +} diff --git a/packages/language-service/src/plugins/vue-codelens-references.ts b/packages/language-service/src/plugins/vue-codelens-references.ts index 4badb080b4..0a170e9374 100644 --- a/packages/language-service/src/plugins/vue-codelens-references.ts +++ b/packages/language-service/src/plugins/vue-codelens-references.ts @@ -1,70 +1,47 @@ -import { Service } from '@volar/language-service'; -import { VueFile } from '@vue/language-core'; +import { ServicePlugin, ServicePluginInstance } from '@volar/language-service'; +import { SourceFile, VirtualFile, VueCodeInformation, VueFile } from '@vue/language-core'; import type * as vscode from 'vscode-languageserver-protocol'; -export const create = function (): Service { +export function create(): ServicePlugin { + return { + name: 'vue-codelens-references', + create(context): ServicePluginInstance { + return { + provideReferencesCodeLensRanges(document) { - return (context): ReturnType => { + return worker(document.uri, async virtualFile => { - if (!context) - return {}; + const result: vscode.Range[] = []; - return { + for (const map of context.documents.getMaps(virtualFile) ?? []) { + for (const mapping of map.map.mappings) { - provideReferencesCodeLensRanges(document) { + if (!(mapping.data as VueCodeInformation).__referencesCodeLens) + continue; - return worker(document.uri, async () => { - - const result: vscode.Range[] = []; - - for (const [_, map] of context.documents.getMapsBySourceFileUri(document.uri)?.maps ?? []) { - for (const mapping of map.map.mappings) { - - if (!mapping.data.referencesCodeLens) - continue; - - result.push({ - start: document.positionAt(mapping.sourceRange[0]), - end: document.positionAt(mapping.sourceRange[1]), - }); + result.push({ + start: document.positionAt(mapping.generatedOffsets[0]), + end: document.positionAt( + mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + + mapping.lengths[mapping.lengths.length - 1] + ), + }); + } } - } - - return result; - }); - }, - - async resolveReferencesCodeLensLocations(document, range, references) { - - await worker(document.uri, async (vueFile) => { - - const document = context.documents.getDocumentByFileName(vueFile.snapshot, vueFile.fileName); - const offset = document.offsetAt(range.start); - const blocks = [ - vueFile.sfc.script, - vueFile.sfc.scriptSetup, - vueFile.sfc.template, - ...vueFile.sfc.styles, - ...vueFile.sfc.customBlocks, - ]; - const sourceBlock = blocks.find(block => block && offset >= block.startTagEnd && offset <= block.endTagStart); - references = references.filter(reference => - reference.uri !== document.uri // different file - || sourceBlock !== blocks.find(block => block && document.offsetAt(reference.range.start) >= block.startTagEnd && document.offsetAt(reference.range.end) <= block.endTagStart) // different block - ); - }); - return references; - }, - }; + return result; + }); + }, + }; - function worker(uri: string, callback: (vueSourceFile: VueFile) => T) { + function worker(uri: string, callback: (vueFile: VirtualFile, sourceFile: SourceFile) => T) { - const [virtualFile] = context!.documents.getVirtualFileByUri(uri); - if (!(virtualFile instanceof VueFile)) - return; + const [virtualFile, sourceFile] = context.language.files.getVirtualFile(context.env.uriToFileName(uri)); + if (!(sourceFile?.virtualFile?.[0] instanceof VueFile) || !sourceFile) + return; - return callback(virtualFile); - } + return callback(virtualFile, sourceFile); + } + }, }; -}; +} diff --git a/packages/language-service/src/plugins/vue-directive-comments.ts b/packages/language-service/src/plugins/vue-directive-comments.ts index ceda0d2028..1aa1a53745 100644 --- a/packages/language-service/src/plugins/vue-directive-comments.ts +++ b/packages/language-service/src/plugins/vue-directive-comments.ts @@ -1,4 +1,4 @@ -import { CompletionItem, Service } from '@volar/language-service'; +import { CompletionItem, ServicePlugin, ServicePluginInstance } from '@volar/language-service'; const cmds = [ 'vue-ignore', @@ -8,58 +8,58 @@ const cmds = [ const directiveCommentReg = //g; -const plugin: Service = (context: ServiceContext | undefined, modules) => { - - if (!context || !modules?.typescript) - return {}; - - const ts = modules.typescript; - +export function create(ts: typeof import('typescript/lib/tsserverlibrary')): ServicePlugin { return { + name: 'vue-twoslash-queries', + create(context): ServicePluginInstance { + return { + provideInlayHints(document, range) { + return worker(document.uri, (vueFile) => { - provideInlayHints(document, range) { - return worker(document.uri, (vueFile) => { + const hoverOffsets: [vscode.Position, number][] = []; + const inlayHints: vscode.InlayHint[] = []; + const languageService = context.inject('typescript/languageService'); - const hoverOffsets: [vscode.Position, number][] = []; - const inlayHints: vscode.InlayHint[] = []; - const languageService = context.inject('typescript/languageService'); - - for (const pointer of document.getText(range).matchAll(twoslashReg)) { - const offset = pointer.index! + pointer[0].indexOf('^?') + document.offsetAt(range.start); - const position = document.positionAt(offset); - hoverOffsets.push([position, document.offsetAt({ - line: position.line - 1, - character: position.character, - })]); - } + for (const pointer of document.getText(range).matchAll(twoslashReg)) { + const offset = pointer.index! + pointer[0].indexOf('^?') + document.offsetAt(range.start); + const position = document.positionAt(offset); + hoverOffsets.push([position, document.offsetAt({ + line: position.line - 1, + character: position.character, + })]); + } - forEachEmbeddedFile(vueFile, (embedded) => { - if (embedded.kind === FileKind.TypeScriptHostFile) { - for (const [_, map] of context.documents.getMapsByVirtualFileName(embedded.fileName)) { - for (const [pointerPosition, hoverOffset] of hoverOffsets) { - for (const [tsOffset, mapping] of map.map.toGeneratedOffsets(hoverOffset)) { - if (mapping.data.hover) { - const quickInfo = languageService.getQuickInfoAtPosition(embedded.fileName, tsOffset); - if (quickInfo) { - inlayHints.push({ - position: { line: pointerPosition.line, character: pointerPosition.character + 2 }, - label: ts.displayPartsToString(quickInfo.displayParts), - paddingLeft: true, - paddingRight: false, - }); + for (const virtualFile of forEachEmbeddedFile(vueFile)) { + if (virtualFile.typescript) { + for (const map of context.documents.getMaps(virtualFile)) { + for (const [pointerPosition, hoverOffset] of hoverOffsets) { + for (const [tsOffset, mapping] of map.map.getGeneratedOffsets(hoverOffset)) { + if (vue.isHoverEnabled(mapping.data)) { + const quickInfo = languageService.getQuickInfoAtPosition(virtualFile.fileName, tsOffset); + if (quickInfo) { + inlayHints.push({ + position: { line: pointerPosition.line, character: pointerPosition.character + 2 }, + label: ts.displayPartsToString(quickInfo.displayParts), + paddingLeft: true, + paddingRight: false, + }); + } + break; + } } - break; } } } } - } - }); - return inlayHints; - }); - }, - }; - - function worker(uri: string, callback: (vueSourceFile: vue.VueFile) => T) { + return inlayHints; + }); + }, + }; - const [virtualFile] = context!.documents.getVirtualFileByUri(uri); - if (!(virtualFile instanceof vue.VueFile)) - return; + function worker(uri: string, callback: (vueSourceFile: vue.VueFile) => T) { - return callback(virtualFile); - } -}; + const [virtualFile] = context.language.files.getVirtualFile(context.env.uriToFileName(uri)); + if (!(virtualFile instanceof vue.VueFile)) + return; -export const create = () => plugin; + return callback(virtualFile); + } + }, + }; +} diff --git a/packages/language-service/src/plugins/vue-visualize-hidden-callback-param.ts b/packages/language-service/src/plugins/vue-visualize-hidden-callback-param.ts index 1aca2da3e4..5379094061 100644 --- a/packages/language-service/src/plugins/vue-visualize-hidden-callback-param.ts +++ b/packages/language-service/src/plugins/vue-visualize-hidden-callback-param.ts @@ -1,59 +1,55 @@ -import { Service } from '@volar/language-service'; +import { ServicePluginInstance } from '@volar/language-service'; import type * as vscode from 'vscode-languageserver-protocol'; +import type { ServicePlugin, VueCodeInformation } from '../types'; -const plugin: Service = (context) => { - - if (!context) - return {}; - +export function create(): ServicePlugin { return { - - async provideInlayHints(document, range) { - - const settings: Record = {}; - const result: vscode.InlayHint[] = []; - const [file] = context.documents.getVirtualFileByUri(document.uri); - if (file) { - const start = document.offsetAt(range.start); - const end = document.offsetAt(range.end); - for (const mapping of file.mappings) { - - const hint: { - setting: string; - label: string; - tooltip: string; - paddingRight?: boolean; - paddingLeft?: boolean; - } | undefined = (mapping.data as any).__hint; - - if ( - mapping.generatedRange[0] >= start - && mapping.generatedRange[1] <= end - && hint - ) { - - settings[hint.setting] ??= await context.env.getConfiguration?.(hint.setting) ?? false; - - if (!settings[hint.setting]) - continue; - - result.push({ - label: hint.label, - paddingRight: hint.paddingRight, - paddingLeft: hint.paddingLeft, - position: document.positionAt(mapping.generatedRange[0]), - kind: 2 satisfies typeof vscode.InlayHintKind.Parameter, - tooltip: { - kind: 'markdown', - value: hint.tooltip, - }, - }); + name: 'vue-inlay-hints-hidden-callback-param', + create(context): ServicePluginInstance { + return { + async provideInlayHints(document, range) { + + const settings: Record = {}; + const result: vscode.InlayHint[] = []; + const [vitualFile] = context.language.files.getVirtualFile(context.env.uriToFileName(document.uri)); + + if (vitualFile) { + + const start = document.offsetAt(range.start); + const end = document.offsetAt(range.end); + + for (const mapping of vitualFile.mappings) { + + const hint = (mapping.data as VueCodeInformation).__hint; + + if ( + mapping.generatedOffsets[0] >= start + && mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + mapping.lengths[mapping.lengths.length - 1] <= end + && hint + ) { + + settings[hint.setting] ??= await context.env.getConfiguration?.(hint.setting) ?? false; + + if (!settings[hint.setting]) + continue; + + result.push({ + label: hint.label, + paddingRight: hint.paddingRight, + paddingLeft: hint.paddingLeft, + position: document.positionAt(mapping.generatedOffsets[0]), + kind: 2 satisfies typeof vscode.InlayHintKind.Parameter, + tooltip: { + kind: 'markdown', + value: hint.tooltip, + }, + }); + } + } } - } - } - return result; + return result; + }, + }; }, }; -}; - -export const create = () => plugin; +} diff --git a/packages/language-service/src/plugins/vue.ts b/packages/language-service/src/plugins/vue.ts index a9b14dd2c9..3ecd0faa81 100644 --- a/packages/language-service/src/plugins/vue.ts +++ b/packages/language-service/src/plugins/vue.ts @@ -1,9 +1,10 @@ -import type { Service, ServiceContext } from '@volar/language-service'; +import type { ServicePlugin, ServicePluginInstance } from '@volar/language-service'; +import * as vue from '@vue/language-core'; +import { create as createHtmlService } from 'volar-service-html'; +import type { Provide as TSProvide } from 'volar-service-typescript'; import * as html from 'vscode-html-languageservice'; import type * as vscode from 'vscode-languageserver-protocol'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import createHtmlPlugin from 'volar-service-html'; -import * as vue from '@vue/language-core'; import { loadLanguageBlocks } from './data'; let sfcDataProvider: html.IHTMLDataProvider | undefined; @@ -12,186 +13,196 @@ export interface Provide { 'vue/vueFile': (document: TextDocument) => vue.VueFile | undefined; } -export const create = (): Service => (context: ServiceContext | undefined, modules): ReturnType> => { +export function create(): ServicePlugin { + return { + name: 'vue-basic', + create(context): ServicePluginInstance { - const htmlPlugin = createHtmlPlugin({ languageId: 'vue', useCustomDataProviders: false })(context, modules); + const htmlPlugin = createHtmlService({ languageId: 'vue', useCustomDataProviders: false }).create(context); - if (!context) - return htmlPlugin as any; + if (!context) + return htmlPlugin as any; - sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en')); + sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en')); - htmlPlugin.provide['html/languageService']().setDataProviders(false, [sfcDataProvider]); + htmlPlugin.provide['html/languageService']().setDataProviders(false, [sfcDataProvider]); - return { + return { - ...htmlPlugin, + ...htmlPlugin, - provide: { - 'vue/vueFile': document => { - return worker(document, (vueFile) => { - return vueFile; - }); - }, - }, + provide: { + 'vue/vueFile': document => { + return worker(document, (vueFile) => { + return vueFile; + }); + }, + }, + + provideSemanticDiagnostics(document) { + return worker(document, (vueSourceFile) => { + + if (!vueSourceFile.mainTsFile) { + return; + } + + const result: vscode.Diagnostic[] = []; + const sfc = vueSourceFile.sfc; + const program = context.inject('typescript/languageService').getProgram(); + const tsFileName = vueSourceFile.mainTsFile.fileName; + + if (program && !program.getSourceFile(tsFileName)) { + for (const script of [sfc.script, sfc.scriptSetup]) { + + if (!script || script.content === '') + continue; + + const error: vscode.Diagnostic = { + range: { + start: document.positionAt(script.start), + end: document.positionAt(script.startTagEnd), + }, + message: `Virtual script ${JSON.stringify(tsFileName)} not found, may missing '; } - - if (ctx.options.host!.fileExists(fileName)) { - const fileContent = ctx.options.host!.readFile(fileName); - if (fileContent !== undefined) { - const script = { - projectVersion: ctx.projectVersion, - modifiedTime, - scriptSnapshot: ts.ScriptSnapshot.fromString(fileContent), - version: ctx.options.host!.createHash?.(fileContent) ?? fileContent, - }; - scripts.set(fileName, script); - return script; - } + return readFile(fileName); + }; + options.host!.writeFile = (fileName, ...args) => { + if (fileName.endsWith('__VLS_globalTypes.vue.d.ts')) { + return; } - } - } - else { - const ctx: ProgramContext = program.__vue; - ctx.options = options; - ctx.projectVersion++; - } - - const vueCompilerOptions = program.__vue.vueCompilerOptions; - if (vueCompilerOptions?.hooks) { - const index = (state.hook?.index ?? -1) + 1; - if (index < vueCompilerOptions.hooks.length) { - const hookPath = vueCompilerOptions.hooks[index]; - const hook: Hook = require(hookPath); - state.hook = { - program, - index, - worker: (async () => await hook(program))(), - }; - throw 'hook'; - } - } + return writeFile(fileName, ...args); + }; - for (const rootName of options.rootNames) { - // register file watchers - options.host.getSourceFile(rootName, ts.ScriptTarget.ESNext); + return fakeFileName; } - - return program; -} - -function toThrow(msg: string) { - console.error(msg); - return msg; } diff --git a/packages/tsc/src/shared.ts b/packages/tsc/src/shared.ts deleted file mode 100644 index d529192e32..0000000000 --- a/packages/tsc/src/shared.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { _Program } from './index'; - -export const state: { - hook?: { - program: _Program, - index: number, - worker: Promise, - }; -} = {}; diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index 2ffcb71db3..fc4a327ec7 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -33,7 +33,11 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v default?(data: { foo: number; }): any; - }>; + }> & { + default?(data: { + foo: number; + }): any; + }; }, __VLS_expose?: (exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>) => void, __VLS_setup?: Promise<{ @@ -49,7 +53,11 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v default?(data: { foo: number; }): any; - }>; + }> & { + default?(data: { + foo: number; + }): any; + }; emit: (e: 'bar', data: number) => void; }>) => import("vue").VNode Input: generic/component.vue, Output: generic/component.v default?(data: { foo: number; }): any; - }>; + }> & { + default?(data: { + foo: number; + }): any; + }; emit: (e: 'bar', data: number) => void; }; }; export default _default; +type __VLS_OmitKeepDiscriminatedUnion = T extends any ? Pick> : never; +type __VLS_Prettify = { + [K in keyof T]: T[K]; +} & {}; " `; @@ -224,10 +240,10 @@ export default _default; exports[`vue-tsc-dts > Input: reference-type-props/component.vue, Output: reference-type-props/component.vue.d.ts 1`] = ` "import { MyProps } from './my-props'; -declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps, { +declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption, { bar: number; baz: () => string[]; -}>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly, { +}>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly, { bar: number; baz: () => string[]; }>>>, { @@ -235,15 +251,6 @@ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_T baz: string[]; }, {}>; export default _default; -type __VLS_NonUndefinedable = T extends undefined ? never : T; -type __VLS_TypePropsToRuntimeProps = { - [K in keyof T]-?: {} extends Pick ? { - type: import('vue').PropType<__VLS_NonUndefinedable>; - } : { - type: import('vue').PropType; - required: true; - }; -}; type __VLS_WithDefaults = { [K in keyof Pick]: K extends keyof D ? __VLS_Prettify = { type __VLS_Prettify = { [K in keyof T]: T[K]; } & {}; +type __VLS_NonUndefinedable = T extends undefined ? never : T; +type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick ? { + type: import('vue').PropType<__VLS_NonUndefinedable>; + } : { + type: import('vue').PropType; + required: true; + }; +}; " `; @@ -564,6 +580,10 @@ exports[`vue-tsc-dts > Input: ts-component/PropDefinitions.ts, Output: ts-compon " `; +exports[`vue-tsc-dts > Input: ts-component/component.ts, Output: ts-component/component.d.ts 1`] = `undefined`; + +exports[`vue-tsc-dts > Input: ts-component/component.tsx, Output: ts-component/component.d.ts 1`] = `undefined`; + exports[`vue-tsc-dts > Input: ts-named-export/component.ts, Output: ts-named-export/component.d.ts 1`] = ` "export declare const Foo: (props: { foo: string; diff --git a/packages/tsc/tests/dts.spec.ts b/packages/tsc/tests/dts.spec.ts index 3079066844..8e486452ab 100644 --- a/packages/tsc/tests/dts.spec.ts +++ b/packages/tsc/tests/dts.spec.ts @@ -2,35 +2,68 @@ import * as path from 'path'; import * as fs from 'fs'; import * as ts from 'typescript'; import { describe, expect, it } from 'vitest'; -import { createProgram } from '../out'; +import { proxyCreateProgram } from '@volar/typescript'; +import * as vue from '@vue/language-core'; +import { createFakeGlobalTypesHolder } from '../out'; const workspace = path.resolve(__dirname, '../../../test-workspace/component-meta'); -const testFiles = readFilesRecursive(workspace); -const ensureTs = (filename: string) => filename.endsWith('.ts') || filename.endsWith('.tsx') ? filename : filename + '.ts'; const normalizePath = (filename: string) => filename.replace(/\\/g, '/'); const normalizeNewline = (text: string) => text.replace(/\r\n/g, '\n'); +const windowsPathReg = /\\/g; describe('vue-tsc-dts', () => { const compilerOptions: ts.CompilerOptions = { rootDir: workspace, declaration: true, emitDeclarationOnly: true, + allowNonTsExtensions: true, }; const host = ts.createCompilerHost(compilerOptions); - const program = createProgram({ + const options: ts.CreateProgramOptions = { host, - rootNames: testFiles, + rootNames: readFilesRecursive(workspace), options: compilerOptions + }; + const fakeGlobalTypesHolder = createFakeGlobalTypesHolder(options); + const createProgram = proxyCreateProgram(ts, ts.createProgram, ['.vue'], (ts, options) => { + const { configFilePath } = options.options; + const vueOptions = typeof configFilePath === 'string' + ? vue.createParsedCommandLine(ts, ts.sys, configFilePath.replace(windowsPathReg, '/')).vueOptions + : {}; + return vue.createLanguages( + ts, + options.options, + vueOptions, + false, + fakeGlobalTypesHolder?.replace(windowsPathReg, '/'), + ); }); - const service = program.__vue.languageService; - - for (const file of testFiles) { - const output = service.getEmitOutput(ensureTs(file), true); - for (const outputFile of output.outputFiles) { - it(`Input: ${shortenPath(file)}, Output: ${shortenPath(outputFile.name)}`, () => { - expect(normalizeNewline(outputFile.text)).toMatchSnapshot(); - }); - } + const program = createProgram(options); + + for (const intputFile of options.rootNames) { + + if (intputFile === fakeGlobalTypesHolder) + continue; + + const expectedOutputFile = intputFile.endsWith('.ts') + ? intputFile.slice(0, -'.ts'.length) + '.d.ts' + : intputFile.endsWith('.tsx') + ? intputFile.slice(0, -'.tsx'.length) + '.d.ts' + : intputFile + '.d.ts'; + it(`Input: ${shortenPath(intputFile)}, Output: ${shortenPath(expectedOutputFile)}`, () => { + let outputText: string | undefined; + const sourceFile = program.getSourceFile(intputFile); + program.emit( + sourceFile, + (outputFile, text) => { + expect(outputFile.replace(windowsPathReg, '/')).toBe(expectedOutputFile.replace(windowsPathReg, '/')); + outputText = text; + }, + undefined, + true, + ); + expect(outputText ? normalizeNewline(outputText) : undefined).toMatchSnapshot(); + }); } }); diff --git a/packages/typescript-plugin/package.json b/packages/typescript-plugin/package.json index 82d951888c..7a65088642 100644 --- a/packages/typescript-plugin/package.json +++ b/packages/typescript-plugin/package.json @@ -13,7 +13,7 @@ "directory": "packages/typescript-plugin" }, "dependencies": { - "@volar/typescript": "~1.11.1", + "@volar/typescript": "2.0.0-alpha.12", "@vue/language-core": "1.8.26" } } diff --git a/packages/typescript-plugin/src/index.ts b/packages/typescript-plugin/src/index.ts index 8ff8b22683..b5e88417d9 100644 --- a/packages/typescript-plugin/src/index.ts +++ b/packages/typescript-plugin/src/index.ts @@ -1,77 +1,39 @@ -import { decorateLanguageService, decorateLanguageServiceHost, searchExternalFiles } from '@volar/typescript'; +import { createTSServerPlugin } from '@volar/typescript/lib/quickstart/createTSServerPlugin'; import * as vue from '@vue/language-core'; +// @ts-expect-error import type * as ts from 'typescript/lib/tsserverlibrary'; -const externalFiles = new WeakMap(); const windowsPathReg = /\\/g; -const init: ts.server.PluginModuleFactory = (modules) => { - const { typescript: ts } = modules; - const pluginModule: ts.server.PluginModule = { - create(info) { - - const virtualFiles = vue.createVirtualFiles( - vue.createLanguages( - ts, - info.languageServiceHost.getCompilationSettings(), - getVueCompilerOptions(), - ), - ); - - decorateLanguageService(virtualFiles, info.languageService, true); - decorateLanguageServiceHost(virtualFiles, info.languageServiceHost, ts, ['.vue']); - - const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition.bind(info.languageService); - - info.languageService.getCompletionsAtPosition = (fileName, position, options) => { - const result = getCompletionsAtPosition(fileName, position, options); - if (result) { - result.entries = result.entries.filter(entry => entry.name.indexOf('__VLS_') === -1); - } - return result; - }; - - return info.languageService; +export = createTSServerPlugin((ts, info) => { + const vueOptions = vue.resolveVueCompilerOptions(getVueCompilerOptions()); + const languagePlugins = vue.createLanguages( + ts, + info.languageServiceHost.getCompilationSettings(), + vueOptions, + ); + const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition; + + info.languageService.getCompletionsAtPosition = (fileName, position, options) => { + const result = getCompletionsAtPosition(fileName, position, options); + if (result) { + result.entries = result.entries.filter(entry => entry.name.indexOf('__VLS_') === -1); + } + return result; + }; - function getVueCompilerOptions() { - if (info.project.projectKind === ts.server.ProjectKind.Configured) { - const tsconfig = info.project.getProjectName(); - return vue.createParsedCommandLine(ts, ts.sys, tsconfig.replace(windowsPathReg, '/')).vueOptions; - } - else { - return vue.createParsedCommandLineByJson(ts, ts.sys, info.languageServiceHost.getCurrentDirectory(), {}).vueOptions; - } - } - }, - getExternalFiles(project, updateLevel = 0) { - if ( - updateLevel >= (1 satisfies ts.ProgramUpdateLevel.RootNamesAndUpdate) - || !externalFiles.has(project) - ) { - const oldFiles = externalFiles.get(project); - const newFiles = searchExternalFiles(ts, project, ['.vue']); - externalFiles.set(project, newFiles); - if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) { - project.refreshDiagnostics(); - } - } - return externalFiles.get(project)!; - }, + return { + languagePlugins, + extensions: vueOptions.extensions, }; - return pluginModule; -}; -function arrayItemsEqual(a: string[], b: string[]) { - if (a.length !== b.length) { - return false; - } - const set = new Set(a); - for (const file of b) { - if (!set.has(file)) { - return false; + function getVueCompilerOptions() { + if (info.project.projectKind === ts.server.ProjectKind.Configured) { + const tsconfig = info.project.getProjectName(); + return vue.createParsedCommandLine(ts, ts.sys, tsconfig.replace(windowsPathReg, '/')).vueOptions; + } + else { + return vue.createParsedCommandLineByJson(ts, ts.sys, info.languageServiceHost.getCurrentDirectory(), {}).vueOptions; } } - return true; -} - -export = init; +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5585894930..a02c957c41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: latest version: 3.1.0(typescript@5.3.3) '@volar/language-service': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 typescript: specifier: latest version: 5.3.3 @@ -36,8 +36,8 @@ importers: specifier: ^1.82.0 version: 1.85.0 '@volar/vscode': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/language-core': specifier: 1.8.26 version: link:../../packages/language-core @@ -56,30 +56,21 @@ importers: semver: specifier: ^7.5.4 version: 7.5.4 - vsce: - specifier: latest - version: 2.15.0 - vscode-languageclient: - specifier: ^9.0.1 - version: 9.0.1 - - extensions/vscode-typescript-plugin: - devDependencies: - esbuild: - specifier: latest - version: 0.19.10 typescript-vue-plugin: specifier: 1.8.26 version: link:../../packages/typescript-plugin vsce: specifier: latest version: 2.15.0 + vscode-languageclient: + specifier: ^9.0.1 + version: 9.0.1 packages/component-meta: dependencies: '@volar/typescript': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/language-core': specifier: 1.8.26 version: link:../language-core @@ -105,11 +96,8 @@ importers: packages/language-core: dependencies: '@volar/language-core': - specifier: ~1.11.1 - version: 1.11.1 - '@volar/source-map': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/compiler-dom': specifier: ^3.3.0 version: 3.3.13 @@ -122,9 +110,6 @@ importers: minimatch: specifier: ^9.0.3 version: 9.0.3 - muggle-string: - specifier: ^0.3.1 - version: 0.3.1 path-browserify: specifier: ^1.0.1 version: 1.0.1 @@ -151,11 +136,11 @@ importers: packages/language-plugin-pug: dependencies: '@volar/source-map': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 volar-service-pug: - specifier: 0.0.17 - version: 0.0.17 + specifier: 0.0.25 + version: 0.0.25 devDependencies: '@types/node': specifier: latest @@ -167,14 +152,14 @@ importers: packages/language-server: dependencies: '@volar/language-core': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@volar/language-server': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@volar/typescript': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/language-core': specifier: 1.8.26 version: link:../language-core @@ -191,14 +176,14 @@ importers: packages/language-service: dependencies: '@volar/language-core': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@volar/language-service': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@volar/typescript': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/compiler-dom': specifier: ^3.3.0 version: 3.3.13 @@ -215,29 +200,29 @@ importers: specifier: ^1.0.1 version: 1.0.1 volar-service-css: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) volar-service-emmet: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) volar-service-html: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) volar-service-json: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) volar-service-pug: - specifier: 0.0.17 - version: 0.0.17 + specifier: 0.0.25 + version: 0.0.25 volar-service-pug-beautify: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) volar-service-typescript: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1)(@volar/typescript@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12)(@volar/typescript@2.0.0-alpha.12) volar-service-typescript-twoslash-queries: - specifier: 0.0.17 - version: 0.0.17(@volar/language-service@1.11.1) + specifier: 0.0.25 + version: 0.0.25(@volar/language-service@2.0.0-alpha.12) vscode-html-languageservice: specifier: ^5.1.0 version: 5.1.1 @@ -252,8 +237,8 @@ importers: specifier: latest version: 1.0.2 '@volar/kit': - specifier: ~1.11.1 - version: 1.11.1(typescript@5.3.3) + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12(typescript@5.3.3) vscode-languageserver-protocol: specifier: ^3.17.5 version: 3.17.5 @@ -264,8 +249,8 @@ importers: packages/tsc: dependencies: '@volar/typescript': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/language-core': specifier: 1.8.26 version: link:../language-core @@ -280,27 +265,11 @@ importers: specifier: latest version: 20.10.5 - packages/tsc-eslint-hook: - dependencies: - eslint: - specifier: '*' - version: 8.56.0 - vscode-languageserver-textdocument: - specifier: ^1.0.11 - version: 1.0.11 - devDependencies: - '@types/eslint': - specifier: latest - version: 8.56.0 - vue-tsc: - specifier: 1.8.26 - version: link:../tsc - packages/typescript-plugin: dependencies: '@volar/typescript': - specifier: ~1.11.1 - version: 1.11.1 + specifier: 2.0.0-alpha.12 + version: 2.0.0-alpha.12 '@vue/language-core': specifier: 1.8.26 version: link:../language-core @@ -319,11 +288,6 @@ importers: packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - dev: false - /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} @@ -587,63 +551,6 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.56.0 - eslint-visitor-keys: 3.4.3 - dev: false - - /@eslint-community/regexpp@4.10.0: - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: false - - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.0 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: false - - /@humanwhocodes/config-array@0.11.13: - resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: false - - /@humanwhocodes/object-schema@2.0.1: - resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} - dev: false - /@hutson/parse-repository-url@5.0.0: resolution: {integrity: sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==} engines: {node: '>=10.13.0'} @@ -872,10 +779,12 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 + dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} + dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -883,6 +792,7 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.16.0 + dev: true /@npmcli/agent@2.2.0: resolution: {integrity: sha512-2yThA1Es98orMkpSLVqlDZAMPK3jHJhifP2gnNUdk1754uZ8yI5c+ulCoVG+WlntQA6MzhrURMXjSd9Z7dJ2/Q==} @@ -1320,21 +1230,6 @@ packages: minimatch: 9.0.3 dev: true - /@types/eslint@8.56.0: - resolution: {integrity: sha512-FlsN0p4FhuYRjIxpbdXovvHQhtlG05O1GG/RNWvdAxTboR438IOTwmrY/vLA+Xfgg06BTkP045M3vpFwTMv1dg==} - dependencies: - '@types/estree': 1.0.5 - '@types/json-schema': 7.0.15 - dev: true - - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true - - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true - /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: true @@ -1361,10 +1256,6 @@ packages: resolution: {integrity: sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g==} dev: true - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: false - /@vitest/expect@1.1.0: resolution: {integrity: sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w==} dependencies: @@ -1403,29 +1294,31 @@ packages: pretty-format: 29.7.0 dev: true - /@volar/kit@1.11.1(typescript@5.3.3): - resolution: {integrity: sha512-nqO+Hl9f1ygOK/3M7Hpnw0lhKvuMFhh823nilStpkTmm5WfrUnE+4WaQkb3dC6LM3TZq74j2m88yxRC+Z3sZZw==} + /@volar/kit@2.0.0-alpha.12(typescript@5.3.3): + resolution: {integrity: sha512-buJv/MXh7zKfFxAEb5BvWM9oLui9Kuhyeb2zvun1hIDZIPHorOiRqtBorycMciH4mnRRjOAmf5jK7sggZYnHcQ==} peerDependencies: typescript: '*' dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 + '@volar/typescript': 2.0.0-alpha.12 typesafe-path: 0.2.2 typescript: 5.3.3 vscode-languageserver-textdocument: 1.0.11 vscode-uri: 3.0.8 dev: true - /@volar/language-core@1.11.1: - resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + /@volar/language-core@2.0.0-alpha.12: + resolution: {integrity: sha512-BJdwE3SICKfjJcfrVFVUH38padp+TBf6GrcmFIz94cyFswi3PCdiBOQWMXscrKTuc52tdRPu0jP+W2o+wSrhVw==} dependencies: - '@volar/source-map': 1.11.1 + '@volar/source-map': 2.0.0-alpha.12 - /@volar/language-server@1.11.1: - resolution: {integrity: sha512-XYG4HcML2qimQV9UouQ7c1GuuqQw1NXoNDxAOAcfyYlz43P+HgzGQx4QEou+QMGHJeYIN86foDvkTN3fcopw9A==} + /@volar/language-server@2.0.0-alpha.12: + resolution: {integrity: sha512-7yYRvqS2uGEVE1V3OBAejkldGjsJsTUNQSvKVx07byf6VBBnaknP/qavgYZ5mAsj8ZcdxK8f+nViptkPGCT7GA==} dependencies: - '@volar/language-core': 1.11.1 - '@volar/language-service': 1.11.1 - '@volar/typescript': 1.11.1 + '@volar/language-core': 2.0.0-alpha.12 + '@volar/language-service': 2.0.0-alpha.12 + '@volar/snapshot-document': 2.0.0-alpha.12 + '@volar/typescript': 2.0.0-alpha.12 '@vscode/l10n': 0.0.16 path-browserify: 1.0.1 request-light: 0.7.0 @@ -1434,30 +1327,35 @@ packages: vscode-languageserver-textdocument: 1.0.11 vscode-uri: 3.0.8 - /@volar/language-service@1.11.1: - resolution: {integrity: sha512-dKo8z1UzQRPHnlXxwfONGrasS1wEWXMoLQiohZ8KgWqZALbekZCwdGImLZD4DeFGNjk3HTTdfeCzo3KjwohjEQ==} + /@volar/language-service@2.0.0-alpha.12: + resolution: {integrity: sha512-dvHXy57wipg5bmaJuBA6F7gUDMrsR27ezVQAGZ67mUKQXAPx/LWtygKftYPZVIzgeNGb75WvfcF0suI5TaOadw==} dependencies: - '@volar/language-core': 1.11.1 - '@volar/source-map': 1.11.1 + '@volar/language-core': 2.0.0-alpha.12 vscode-languageserver-protocol: 3.17.5 vscode-languageserver-textdocument: 1.0.11 vscode-uri: 3.0.8 - /@volar/source-map@1.11.1: - resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + /@volar/snapshot-document@2.0.0-alpha.12: + resolution: {integrity: sha512-2QW6yBS31y/d2Cp6u0JeBn6y2e0joK7hXcWDHVufJ4n+7HWhS3o46rD4tbliwOrR8leUOxnVvReK7loMnUZgjQ==} + dependencies: + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.11 + + /@volar/source-map@2.0.0-alpha.12: + resolution: {integrity: sha512-+I1jtm4xrGhYQW+c2DjBpBnV9zgQR5bVI49HmoIJaVfuz7RF9PCGaxq6a2tZ433nCmEX8tXRBBhWgh/4i2OZrw==} dependencies: - muggle-string: 0.3.1 + muggle-string: 0.4.1 - /@volar/typescript@1.11.1: - resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + /@volar/typescript@2.0.0-alpha.12: + resolution: {integrity: sha512-aIksyYHaOMNynB03d5ab6gO6spYYdkS5rarl4trccOLoTtxGjpW5dsOs1ln7WErgLa6VkxmsxSPp3GtmOaLQtA==} dependencies: - '@volar/language-core': 1.11.1 + '@volar/language-core': 2.0.0-alpha.12 path-browserify: 1.0.1 - /@volar/vscode@1.11.1: - resolution: {integrity: sha512-5NC6wuy6uZtEwrIYREOVIIkmdBkTuZAS2GaDGhfz2JlL2bD48zdQloeQiiSDNEKLRjoghO2yLXKgepIBsWyfcA==} + /@volar/vscode@2.0.0-alpha.12: + resolution: {integrity: sha512-WXrLLvr1Tb0pAnjjuhlEiuq2fEfgLdWUhELY/w/XzmZNS5/f7+taTEp3VnwlzGwcT9SpIAtd9JDb0eY+ip2BaQ==} dependencies: - '@volar/language-server': 1.11.1 + '@volar/language-server': 2.0.0-alpha.12 path-browserify: 1.0.1 vscode-languageclient: 9.0.1 vscode-nls: 5.2.0 @@ -1584,14 +1482,6 @@ packages: event-target-shim: 5.0.1 dev: true - /acorn-jsx@5.3.2(acorn@8.11.2): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.11.2 - dev: false - /acorn-walk@8.3.1: resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==} engines: {node: '>=0.4.0'} @@ -1607,6 +1497,7 @@ packages: resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /add-stream@1.0.0: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} @@ -1629,15 +1520,6 @@ packages: indent-string: 4.0.0 dev: true - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: false - /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1648,6 +1530,7 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -1666,6 +1549,7 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 + dev: true /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -1699,6 +1583,7 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true /array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -1763,6 +1648,7 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + dev: true /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1838,6 +1724,7 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + dev: true /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} @@ -1867,6 +1754,7 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + dev: true /chalk@5.3.0: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} @@ -2008,6 +1896,7 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -2015,6 +1904,7 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} @@ -2051,6 +1941,7 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true /config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -2156,6 +2047,7 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dev: true /css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} @@ -2206,6 +2098,7 @@ packages: optional: true dependencies: ms: 2.1.2 + dev: true /decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} @@ -2235,10 +2128,6 @@ packages: engines: {node: '>=4.0.0'} dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: false - /deepmerge-ts@5.1.0: resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} engines: {node: '>=16.0.0'} @@ -2293,13 +2182,6 @@ packages: path-type: 4.0.0 dev: true - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: false - /dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -2463,112 +2345,14 @@ packages: engines: {node: '>=0.8.0'} dev: true - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: false - /escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} dev: true - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: false - - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: false - - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 - '@humanwhocodes/config-array': 0.11.13 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: false - - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.11.2 - acorn-jsx: 5.3.2(acorn@8.11.2) - eslint-visitor-keys: 3.4.3 - dev: false - - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: false - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: false - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: false - /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: false - /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -2616,10 +2400,6 @@ packages: tmp: 0.0.33 dev: true - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: false - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -2631,18 +2411,11 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: false - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: false - /fastq@1.16.0: resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} dependencies: reusify: 1.0.4 + dev: true /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -2666,13 +2439,6 @@ packages: is-unicode-supported: 1.3.0 dev: true - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.2.0 - dev: false - /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -2688,14 +2454,6 @@ packages: path-exists: 4.0.0 dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: false - /find-up@6.3.0: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2704,19 +2462,6 @@ packages: path-exists: 5.0.0 dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.9 - keyv: 4.5.4 - rimraf: 3.0.2 - dev: false - - /flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} - dev: false - /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -2770,6 +2515,7 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -2866,6 +2612,7 @@ packages: engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 + dev: true /glob@10.3.10: resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} @@ -2888,13 +2635,7 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: false + dev: true /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -2929,10 +2670,6 @@ packages: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: false - /handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -2954,6 +2691,7 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has-property-descriptors@1.0.1: resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} @@ -3072,6 +2810,7 @@ packages: /ignore@5.3.0: resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} engines: {node: '>= 4'} + dev: true /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -3079,6 +2818,7 @@ packages: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 + dev: true /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} @@ -3092,6 +2832,7 @@ packages: /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + dev: true /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} @@ -3103,9 +2844,11 @@ packages: dependencies: once: 1.4.0 wrappy: 1.0.2 + dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} @@ -3176,6 +2919,7 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3187,6 +2931,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 + dev: true /is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} @@ -3207,11 +2952,6 @@ packages: engines: {node: '>=8'} dev: true - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: false - /is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -3273,6 +3013,7 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true /isexe@3.1.1: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} @@ -3302,10 +3043,7 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: false + dev: true /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -3316,14 +3054,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: false - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: false - /json-stringify-nice@1.1.4: resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} dev: true @@ -3374,12 +3104,6 @@ packages: prebuild-install: 7.1.1 dev: true - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: false - /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -3390,14 +3114,6 @@ packages: engines: {node: '>=6'} dev: true - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: false - /libnpmaccess@8.0.2: resolution: {integrity: sha512-4K+nsg3OYt4rjryP/3D5zGWluLbZaKozwj6YdtvAyxNhLhUrjCoyxHVoL5AkTJcAnjsd6/ATei52QPVvpSX9Ug==} engines: {node: ^16.14.0 || >=18.0.0} @@ -3459,13 +3175,6 @@ packages: p-locate: 4.1.0 dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: false - /locate-path@7.2.0: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3473,10 +3182,6 @@ packages: p-locate: 6.0.0 dev: true - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: false - /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true @@ -3601,6 +3306,7 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 + dev: true /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} @@ -3711,9 +3417,10 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true - /muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + /muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} /mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -3734,10 +3441,6 @@ packages: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: true - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: false - /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -3918,6 +3621,7 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 + dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -3942,18 +3646,6 @@ packages: is-wsl: 2.2.0 dev: true - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} - engines: {node: '>= 0.8.0'} - dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: false - /ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -3981,13 +3673,6 @@ packages: p-try: 2.2.0 dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: false - /p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4009,13 +3694,6 @@ packages: p-limit: 2.3.0 dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: false - /p-locate@6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4096,6 +3774,7 @@ packages: engines: {node: '>=6'} dependencies: callsites: 3.1.0 + dev: true /parse-conflict-json@3.0.1: resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==} @@ -4164,6 +3843,7 @@ packages: /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + dev: true /path-exists@5.0.0: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} @@ -4173,10 +3853,12 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + dev: true /path-key@4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} @@ -4278,11 +3960,6 @@ packages: tunnel-agent: 0.6.0 dev: true - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: false - /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4361,11 +4038,6 @@ packages: once: 1.4.0 dev: true - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - dev: false - /qs@6.11.2: resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} engines: {node: '>=0.6'} @@ -4375,6 +4047,7 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} @@ -4484,6 +4157,7 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + dev: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -4506,12 +4180,14 @@ packages: /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 + dev: true /rollup@4.9.1: resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==} @@ -4543,6 +4219,7 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + dev: true /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -4599,10 +4276,12 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + dev: true /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -4770,6 +4449,7 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -4788,11 +4468,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: false - /strip-literal@1.3.0: resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: @@ -4821,6 +4496,7 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + dev: true /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} @@ -4864,10 +4540,6 @@ packages: engines: {node: '>=8'} dev: true - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: false - /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true @@ -4946,23 +4618,11 @@ packages: engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} dev: true - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: false - /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: false - /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -5064,12 +4724,6 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.1 - dev: false - /url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} dev: true @@ -5209,105 +4863,103 @@ packages: - terser dev: true - /volar-service-css@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-bEDJykygMzn2+a9ud6KwZZLli9eqarxApAXZuf2CqJJh6Trw1elmbBCo9SlPfqMrIhpFnwV0Sa+Xoc9x5WPeGw==} + /volar-service-css@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-Gk4DHGtoWTOxfXbOec8Orikr5viUrlEY6G7pz5wCaOVf4k6cDdadSl9fXZY3MFCZtZUo6RmfhTCyLZq9NeaFMg==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 vscode-css-languageservice: 6.2.11 vscode-uri: 3.0.8 dev: false - /volar-service-emmet@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-C6hVnuQL52MqaydkrblYUbzIo5ZmIGo1hR8wmpcCjs5uNcjqn8aPqZRfznhLiUSaPHpFC+zQxJwFcZI9/u2iKQ==} + /volar-service-emmet@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-NT0Z+FfQfz7M3AxorBem0nbKBMqL18yyskIpONBxUvC4QaVZ67pW7lPZGzX0IqhLqxKUUotNffh7ooQ9KW73Dg==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 '@vscode/emmet-helper': 2.9.2 - volar-service-html: 0.0.17(@volar/language-service@1.11.1) + volar-service-html: 0.0.25(@volar/language-service@2.0.0-alpha.12) dev: false - /volar-service-html@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-OGkP+ZTo13j/+enafGe+esXvda/W4eU78YNLbbHxtV3rnX4odVrewenLJmXiECg6wdQz/PG8rLijZqQnDUYkfw==} + /volar-service-html@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-ZosolIcq3ise5bz44gkFeUpTknbGGv8rG+FpnmcnAhqrgOktj0+XwLBbOOpeCfFVKw6N0FsUOAt8zGJs8Bpg9w==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 vscode-html-languageservice: 5.1.1 vscode-uri: 3.0.8 dev: false - /volar-service-json@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-28pXc5l5xSDtTDZ73zg0Jklr1KOsqL60wa8SXTJptK7e+PX8PF/cxQ/2FBHQOybdJsk+fn8jx8wF/ky8cuVHag==} + /volar-service-json@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-LilQg0xrYoaOl7BkI1CH8PBTl0/Apc3qCKuE0AB1mzbtltMxzU7jzojGPpH+ZNIdZS3lGf+6CKrjZFCV7oLJjA==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 vscode-json-languageservice: 5.3.7 vscode-uri: 3.0.8 dev: false - /volar-service-pug-beautify@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-r+18HvciIOFbwi9dNTok1r1oboFyPaRPW1F5K7Yw1Ypynt2PI6ocGjBJ0V+Z5oONTXq1wHA4vzlmmvz5vzVh9g==} + /volar-service-pug-beautify@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-ik2CFTKa9Hy7tfvIqX/83SHItDFLQQIn5gZeDMtMyLf+pbgFjJjtEHzaQHpAYyOlunztglPBRqJriaWzfmZ1AA==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: '@johnsoncodehk/pug-beautify': 0.2.2 - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 dev: false - /volar-service-pug@0.0.17: - resolution: {integrity: sha512-p6oDgH+ufWyS6r6Jv5h76cWGtjLOG/vhd5NH4Sk0pMCdh8zANHJsKsxsrIOiYsK6J9uNU1H6qRRHGg95xj34eQ==} + /volar-service-pug@0.0.25: + resolution: {integrity: sha512-pwknc5UcZoKIAODOHz5tCaA/u1JgxeMiSvTpMj8/Lp7fqlH7i3iQ+I9mDg8wymxGhJ6seBCQZ/HU2ySo/vFIHw==} dependencies: - '@volar/language-service': 1.11.1 - '@volar/source-map': 1.11.1 - muggle-string: 0.3.1 + '@volar/language-service': 2.0.0-alpha.12 pug-lexer: 5.0.1 pug-parser: 6.0.0 - volar-service-html: 0.0.17(@volar/language-service@1.11.1) + volar-service-html: 0.0.25(@volar/language-service@2.0.0-alpha.12) vscode-html-languageservice: 5.1.1 vscode-languageserver-textdocument: 1.0.11 dev: false - /volar-service-typescript-twoslash-queries@0.0.17(@volar/language-service@1.11.1): - resolution: {integrity: sha512-6FHXK5AWeFzCL6uGmEcbkZmQsaQ0m9IjbeLdgOIQ4KGvauqT2aA1BhdfDJu6vRAFIfXe7xjEJ85keIlHl72tSA==} + /volar-service-typescript-twoslash-queries@0.0.25(@volar/language-service@2.0.0-alpha.12): + resolution: {integrity: sha512-HOfVV3qRJkQDKJgcUD/n3pOeKTaTmD9ycW5TMWgwZrygMgbGD9mWtG2W7fEh8YUeTCV5y/NArUCUzFb1Uq4qKg==} peerDependencies: - '@volar/language-service': ~1.11.0 + '@volar/language-service': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 dev: false - /volar-service-typescript@0.0.17(@volar/language-service@1.11.1)(@volar/typescript@1.11.1): - resolution: {integrity: sha512-Krs8pOIo2yoBVoJ91hKT1czhWt9ek7EbuK3MxxgvDYdd4HYHOtHi1eOlb7bFnZMNgFcwsL48yQI9vbPm160s9A==} + /volar-service-typescript@0.0.25(@volar/language-service@2.0.0-alpha.12)(@volar/typescript@2.0.0-alpha.12): + resolution: {integrity: sha512-HapraB6P6S+v2o5nH+dzu4l5r4iIyY8EwYQjgbYTAhtFdpJEscIt9HtPOUJSY0d9xXbblFeLrhUFzAicNrSeaA==} peerDependencies: - '@volar/language-service': ~1.11.0 - '@volar/typescript': ~1.11.0 + '@volar/language-service': next + '@volar/typescript': next peerDependenciesMeta: '@volar/language-service': optional: true dependencies: - '@volar/language-service': 1.11.1 - '@volar/typescript': 1.11.1 + '@volar/language-service': 2.0.0-alpha.12 + '@volar/typescript': 2.0.0-alpha.12 path-browserify: 1.0.1 semver: 7.5.4 typescript-auto-import-cache: 0.3.1 @@ -5464,6 +5116,7 @@ packages: hasBin: true dependencies: isexe: 2.0.0 + dev: true /which@4.0.0: resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} @@ -5521,6 +5174,7 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true /write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} @@ -5612,11 +5266,6 @@ packages: buffer-crc32: 0.2.13 dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: false - /yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} diff --git a/test-workspace/language-service/reference/slot-named/entry.vue b/test-workspace/language-service/reference/slot-named/entry.vue index ba43324499..b4f17d8bc7 100644 --- a/test-workspace/language-service/reference/slot-named/entry.vue +++ b/test-workspace/language-service/reference/slot-named/entry.vue @@ -1,4 +1,4 @@ diff --git a/test-workspace/tsc/petite-vue/main.html b/test-workspace/tsc/petite-vue/main.html index e379d02710..e52731ffb0 100644 --- a/test-workspace/tsc/petite-vue/main.html +++ b/test-workspace/tsc/petite-vue/main.html @@ -1,5 +1,6 @@ + + diff --git a/test-workspace/tsc/vue3/#3779/named.vue b/test-workspace/tsc/vue3/#3779/named.vue new file mode 100644 index 0000000000..0a6a3484d8 --- /dev/null +++ b/test-workspace/tsc/vue3/#3779/named.vue @@ -0,0 +1,11 @@ + + + diff --git a/test-workspace/tsc/vue3/#3820/main.vue b/test-workspace/tsc/vue3/#3820/main.vue new file mode 100644 index 0000000000..beafbfa6bc --- /dev/null +++ b/test-workspace/tsc/vue3/#3820/main.vue @@ -0,0 +1,18 @@ + + diff --git a/test-workspace/tsc/vue3/components/main.vue b/test-workspace/tsc/vue3/components/main.vue index c95d0a01c3..9c93ed2794 100644 --- a/test-workspace/tsc/vue3/components/main.vue +++ b/test-workspace/tsc/vue3/components/main.vue @@ -75,7 +75,7 @@ declare const ScriptSetupGenericExact: ( foo: T; } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, attrs: any, - slots: Readonly<{ default?(data: T): any; }>, + slots: Readonly<{ default?(data: T): any; }> & { default?(data: T): any; }, emit: { (e: 'bar', data: T): void; }, expose(_exposed: import('vue').ShallowUnwrapRef<{ baz: T; buz: import('vue').Ref<1>; }>): void, }> diff --git a/tsconfig.json b/tsconfig.json index e1dc6131f5..a273185343 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,11 +6,9 @@ "include": [ "packages/*/tests" ], "references": [ { "path": "./extensions/vscode/tsconfig.json" }, - { "path": "./extensions/vscode-typescript-plugin/tsconfig.json" }, { "path": "./packages/language-plugin-pug/tsconfig.json" }, { "path": "./packages/typescript-plugin/tsconfig.json" }, { "path": "./packages/tsc/tsconfig.json" }, - { "path": "./packages/tsc-eslint-hook/tsconfig.json" }, { "path": "./packages/component-meta/tsconfig.json" }, ], } \ No newline at end of file