From dd95dbc92246bbdd78cdf6f5b8713000fb1c2e28 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 04:31:33 +0800 Subject: [PATCH 1/9] refactor: generate single typescript script for vue file --- README.md | 4 - packages/vue-code-gen/LICENSE | 21 - packages/vue-code-gen/package.json | 25 - .../vue-code-gen/src/generators/script.ts | 743 --------------- packages/vue-code-gen/src/index.ts | 102 --- packages/vue-code-gen/src/types.ts | 40 - packages/vue-code-gen/tsconfig.build.json | 25 - packages/vue-language-core/package.json | 6 +- .../vue-language-core/src/documentRegistry.ts | 2 +- .../src/generators/script.ts | 852 ++++++++++++++++++ .../src/generators/template.ts | 2 +- packages/vue-language-core/src/index.ts | 5 + .../src/parsers/refSugarRanges.ts | 0 .../src/parsers/scriptRanges.ts | 0 .../src/parsers/scriptSetupConvertRanges.ts | 0 .../src/parsers/scriptSetupRanges.ts | 0 .../src/plugins/vue-typescript-scripts.ts | 137 +-- .../src/plugins/vue-typescript-template.ts | 279 ------ packages/vue-language-core/src/sourceFile.ts | 42 +- packages/vue-language-core/src/types.ts | 42 +- .../src/utils/compileSFCTemplate.ts | 116 +++ .../vue-language-core/src/utils/localTypes.ts | 2 +- .../vue-language-core/src/utils/shared.ts | 7 + .../vue-language-core/src/utils/sourceMaps.ts | 2 +- .../src/utils}/transform.ts | 0 .../src/utils}/vue2TemplateCompiler.ts | 0 .../vue-language-core/tsconfig.build.json | 3 - packages/vue-language-service/package.json | 1 - .../src/languageFeatures/definition.ts | 2 +- .../src/plugins/vue-convert-refsugar.ts | 6 +- .../src/plugins/vue-convert-scriptsetup.ts | 10 +- .../src/plugins/vue-template.ts | 10 +- .../vue-language-service/src/plugins/vue.ts | 2 +- .../vue-language-service/src/vueDocuments.ts | 4 +- .../vue-language-service/tsconfig.build.json | 3 - .../rename/component-tag/input/component.vue | 2 + .../rename/component-tag/input/entry.vue | 2 +- .../rename/component-tag/output/component.vue | 2 + .../rename/component-tag/output/entry.vue | 2 +- .../rename/regular-component/input/entry.vue | 18 + .../rename/regular-component/output/entry.vue | 18 + .../reference-type-in-template/main.vue | 10 + pnpm-lock.yaml | 282 +++--- 43 files changed, 1316 insertions(+), 1515 deletions(-) delete mode 100644 packages/vue-code-gen/LICENSE delete mode 100644 packages/vue-code-gen/package.json delete mode 100644 packages/vue-code-gen/src/generators/script.ts delete mode 100644 packages/vue-code-gen/src/index.ts delete mode 100644 packages/vue-code-gen/src/types.ts delete mode 100644 packages/vue-code-gen/tsconfig.build.json create mode 100644 packages/vue-language-core/src/generators/script.ts rename packages/{vue-code-gen => vue-language-core}/src/generators/template.ts (99%) rename packages/{vue-code-gen => vue-language-core}/src/parsers/refSugarRanges.ts (100%) rename packages/{vue-code-gen => vue-language-core}/src/parsers/scriptRanges.ts (100%) rename packages/{vue-code-gen => vue-language-core}/src/parsers/scriptSetupConvertRanges.ts (100%) rename packages/{vue-code-gen => vue-language-core}/src/parsers/scriptSetupRanges.ts (100%) delete mode 100644 packages/vue-language-core/src/plugins/vue-typescript-template.ts create mode 100644 packages/vue-language-core/src/utils/compileSFCTemplate.ts create mode 100644 packages/vue-language-core/src/utils/shared.ts rename packages/{vue-code-gen/src => vue-language-core/src/utils}/transform.ts (100%) rename packages/{vue-code-gen/src => vue-language-core/src/utils}/vue2TemplateCompiler.ts (100%) create mode 100644 packages/vue-test-workspace/rename/component-tag/input/component.vue create mode 100644 packages/vue-test-workspace/rename/component-tag/output/component.vue create mode 100644 packages/vue-test-workspace/rename/regular-component/input/entry.vue create mode 100644 packages/vue-test-workspace/rename/regular-component/output/entry.vue create mode 100644 packages/vue-test-workspace/vue-tsc/reference-type-in-template/main.vue diff --git a/README.md b/README.md index ce931282f..2b7fc39b4 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,6 @@ flowchart LR VOLAR_VUE_SERVER["@volar/vue-language-server"] VOLAR_VUE_TS["@volar/vue-typescript"] VOLAR_VUE_CORE["@volar/vue-language-core"] - VOLAR_VUE_CG["@volar/vue-code-gen"] VOLAR_VUE_SERVICE["@volar/vue-language-service"] VOLAR_PUG_SERVICE["@volar/pug-language-service"] VOLAR_TS_SERVICE["@volar/typescript-language-service"] @@ -140,7 +139,6 @@ flowchart LR click VOLAR_VUE_SERVER "https://github.com/johnsoncodehk/volar/tree/master/packages/vue-language-server" click VOLAR_VUE_TS "https://github.com/johnsoncodehk/volar/tree/master/packages/vue-typescript" click VOLAR_VUE_CORE "https://github.com/johnsoncodehk/volar/tree/master/packages/vue-language-core" - click VOLAR_VUE_CG "https://github.com/johnsoncodehk/volar/tree/master/packages/vue-code-gen" click VOLAR_VUE_SERVICE "https://github.com/johnsoncodehk/volar/tree/master/packages/vue-language-service" click VOLAR_PUG_SERVICE "https://github.com/johnsoncodehk/volar/tree/master/packages/pug-language-service" click VOLAR_TS_SERVICE "https://github.com/johnsoncodehk/volar/tree/master/packages/typescript-language-service" @@ -239,8 +237,6 @@ flowchart LR VOLAR_VUE_SERVICE --> HTML_SERVICE VOLAR_VUE_SERVICE --> CSS_SERVICE VOLAR_VUE_SERVICE --> JSON_SERVICE - - VOLAR_VUE_CORE --> VOLAR_VUE_CG ``` ## Sponsors diff --git a/packages/vue-code-gen/LICENSE b/packages/vue-code-gen/LICENSE deleted file mode 100644 index b55e47a7e..000000000 --- a/packages/vue-code-gen/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/packages/vue-code-gen/package.json b/packages/vue-code-gen/package.json deleted file mode 100644 index c7f16e16a..000000000 --- a/packages/vue-code-gen/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@volar/vue-code-gen", - "version": "0.39.5", - "main": "out/index.js", - "license": "MIT", - "files": [ - "out/**/*.js", - "out/**/*.d.ts" - ], - "repository": { - "type": "git", - "url": "https://github.com/johnsoncodehk/volar.git", - "directory": "packages/vue-code-gen" - }, - "devDependencies": { - "typescript": "latest" - }, - "dependencies": { - "@volar/code-gen": "0.39.5", - "@volar/source-map": "0.39.5", - "@vue/compiler-core": "^3.2.37", - "@vue/compiler-dom": "^3.2.37", - "@vue/shared": "^3.2.37" - } -} diff --git a/packages/vue-code-gen/src/generators/script.ts b/packages/vue-code-gen/src/generators/script.ts deleted file mode 100644 index f216ba1a4..000000000 --- a/packages/vue-code-gen/src/generators/script.ts +++ /dev/null @@ -1,743 +0,0 @@ -import { CodeGen } from '@volar/code-gen'; -import * as SourceMaps from '@volar/source-map'; -import { hyphenate } from '@vue/shared'; -import { posix as path } from 'path'; -import type * as templateGen from '../generators/template'; -import type { ScriptRanges } from '../parsers/scriptRanges'; -import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import type { TeleportMappingData, EmbeddedFileMappingData } from '../types'; - -export function getSlotsPropertyName(vueVersion: number) { - return vueVersion < 3 ? '$scopedSlots' : '$slots'; -} - -export function getVueLibraryName(vueVersion: number) { - return vueVersion < 2.7 ? '@vue/runtime-dom' : 'vue'; -} - -export function generate( - lsType: 'template' | 'script', - fileName: string, - script: undefined | { - src?: string, - content: string, - }, - scriptSetup: undefined | { - content: string, - }, - scriptRanges: ScriptRanges | undefined, - scriptSetupRanges: ScriptSetupRanges | undefined, - getHtmlGen: () => ReturnType | undefined, - getStyleBindTexts: () => string[], - shimComponentOptions: 'defineComponent' | 'Vue.extend' | false, - downgradePropsAndEmitsToSetupReturnOnScriptSetup: boolean, - vueVersion: number, - codeGen = new CodeGen(), - teleports: SourceMaps.Mapping[] = [], -) { - - const vueLibName = getVueLibraryName(vueVersion); - const usedTypes = { - DefinePropsToOptions: false, - mergePropDefaults: false, - ConstructorOverloads: false, - }; - - let exportdefaultStart: number | undefined; - let exportdefaultEnd: number | undefined; - - if (lsType === 'template') { - codeGen.addText('// @ts-nocheck\n'); - } - - writeScriptSrc(); - writeScriptSetupImports(); - writeScriptBeforeExportDefault(); - writeScriptSetup(); - writeScriptSetupTypes(); - writeScriptAfterExportDefault(); - - if (lsType === 'script' && !script && !scriptSetup) { - codeGen.addCode( - 'export default {} as any', - { - start: 0, - end: 0, - }, - SourceMaps.Mode.Expand, - { - vueTag: undefined, - capabilities: {}, - }, - ); - } - - if (lsType === 'template') { - writeExportOptions(); - writeConstNameOption(); - writeExportTypes(); - } - - if (lsType === 'script' && scriptSetup) { - // for code action edits - codeGen.addCode( - '', - { - start: scriptSetup.content.length, - end: scriptSetup.content.length, - }, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: {}, - }, - ); - } - - // fix https://github.com/johnsoncodehk/volar/issues/1048 - // fix https://github.com/johnsoncodehk/volar/issues/435 - codeGen.addMapping2({ - data: { - vueTag: undefined, - capabilities: {}, - }, - mode: SourceMaps.Mode.Expand, - mappedRange: { - start: 0, - end: codeGen.getText().length, - }, - sourceRange: { - start: 0, - end: 0, - }, - }); - - // fix https://github.com/johnsoncodehk/volar/issues/1127 - if (scriptSetup && exportdefaultStart !== undefined && exportdefaultEnd !== undefined) { - codeGen.addMapping2({ - data: { - vueTag: 'scriptSetup', - capabilities: { - diagnostic: lsType === 'script', - }, - }, - mode: SourceMaps.Mode.Totally, - mappedRange: { - start: exportdefaultStart, - end: exportdefaultEnd, - }, - sourceRange: { - start: 0, - end: scriptSetup.content.length, - }, - }); - } - - return { - codeGen, - teleports, - }; - - function writeScriptSetupTypes() { - if (usedTypes.DefinePropsToOptions) { - codeGen.addText(`type __VLS_NonUndefinedable = T extends undefined ? never : T;\n`); - codeGen.addText(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType<__VLS_NonUndefinedable> } : { type: import('${vueLibName}').PropType, required: true } };\n`); - } - if (usedTypes.mergePropDefaults) { - codeGen.addText(`type __VLS_WithDefaults = { - // use 'keyof Pick' instead of 'keyof P' to keep props jsdoc - [K in keyof Pick]: K extends keyof D ? P[K] & { - default: D[K] - } : P[K] - };\n`); - } - if (usedTypes.ConstructorOverloads) { - // fix https://github.com/johnsoncodehk/volar/issues/926 - codeGen.addText('type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;\n'); - if (scriptSetupRanges && scriptSetupRanges.emitsTypeNums !== -1) { - codeGen.addText(genConstructorOverloads('__VLS_ConstructorOverloads', scriptSetupRanges.emitsTypeNums)); - } - else { - codeGen.addText(genConstructorOverloads('__VLS_ConstructorOverloads')); - } - } - } - function writeScriptSrc() { - 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('.js') && !src.endsWith('.jsx')) src = src + '.js'; - - codeGen.addText(`export * from `); - codeGen.addCode( - `'${src}'`, - { start: -1, end: -1 }, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSrc', - capabilities: { - basic: lsType === 'script', - references: true, - definitions: lsType === 'script', - rename: true, - diagnostic: lsType === 'script', - completion: lsType === 'script', - semanticTokens: lsType === 'script', - }, - } - ); - codeGen.addText(`;\n`); - codeGen.addText(`export { default } from '${src}';\n`); - } - function writeScriptBeforeExportDefault() { - if (!script) - return; - - if (!!scriptSetup && scriptRanges?.exportDefault) { - addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); - exportdefaultStart = codeGen.getText().length - (scriptRanges.exportDefault.expression.start - scriptRanges.exportDefault.start); - } - else { - let isExportRawObject = false; - if (scriptRanges?.exportDefault) { - isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); - } - if (isExportRawObject && shimComponentOptions && scriptRanges?.exportDefault) { - addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); - if (shimComponentOptions === 'defineComponent') { - codeGen.addText(`(await import('${vueLibName}')).defineComponent(`); - } - else { - codeGen.addText(`(await import('vue')).default.extend(`); - } - addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end); - codeGen.addText(`)`); - addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length); - } - else { - addVirtualCode('script', 0, script.content.length); - } - } - } - function writeScriptAfterExportDefault() { - if (!script) - return; - - if (!!scriptSetup && scriptRanges?.exportDefault) { - addVirtualCode('script', scriptRanges.exportDefault.end, script.content.length); - } - } - function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { - codeGen.addCode( - (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end), - { start, end }, - SourceMaps.Mode.Offset, - { - vueTag: vueTag, - capabilities: { - basic: lsType === 'script', - references: true, - definitions: lsType === 'script', - rename: true, - diagnostic: true, // also working for setup() returns unused in template checking - completion: lsType === 'script', - semanticTokens: lsType === 'script', - }, - } - ); - } - function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { - codeGen.addCode( - (vueTag === 'scriptSetup' ? scriptSetup : script)!.content.substring(start, end), - { start, end }, - SourceMaps.Mode.Offset, - { - vueTag, - capabilities: { - references: true, - definitions: true, - rename: true, - }, - }, - ); - } - function writeScriptSetupImports() { - - if (!scriptSetup) - return; - - if (!scriptSetupRanges) - return; - - codeGen.addCode( - scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset), - { - start: 0, - end: scriptSetupRanges.importSectionEndOffset, - }, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: { - basic: lsType === 'script', - references: true, - definitions: lsType === 'script', - diagnostic: lsType === 'script', - rename: true, - completion: lsType === 'script', - semanticTokens: lsType === 'script', - }, - }, - ); - } - function writeScriptSetup() { - - if (!scriptSetup) - return; - - if (!scriptSetupRanges) - return; - - if (scriptRanges?.exportDefault) { - codeGen.addText('await (async () => {\n'); - } - else { - exportdefaultStart = codeGen.getText().length; - codeGen.addText('export default await (async () => {\n'); - } - - codeGen.addText('const __VLS_setup = async () => {\n'); - - codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.importSectionEndOffset), - { - start: scriptSetupRanges.importSectionEndOffset, - end: scriptSetup.content.length, - }, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: { - basic: lsType === 'script', - references: true, - definitions: lsType === 'script', - diagnostic: lsType === 'script', - rename: true, - completion: lsType === 'script', - semanticTokens: lsType === 'script', - }, - }, - ); - - if (scriptSetupRanges.propsTypeArg && scriptSetupRanges?.withDefaultsArg) { - // fix https://github.com/johnsoncodehk/volar/issues/1187 - codeGen.addText(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); - codeGen.addText(`);\n`); - } - - if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { - // use defineComponent() from user space code if it exist - codeGen.addText(`const __VLS_Component = `); - addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start); - codeGen.addText(`{\n`); - } - else { - codeGen.addText(`const __VLS_Component = (await import('${vueLibName}')).defineComponent({\n`); - } - - if (scriptSetup && scriptSetupRanges) { - if (!downgradePropsAndEmitsToSetupReturnOnScriptSetup) { - if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { - codeGen.addText(`props: (`); - if (scriptSetupRanges.propsTypeArg) { - - usedTypes.DefinePropsToOptions = true; - codeGen.addText(`{} as `); - - if (scriptSetupRanges.withDefaultsArg) { - usedTypes.mergePropDefaults = true; - codeGen.addText(`__VLS_WithDefaults<`); - } - - codeGen.addText(`__VLS_TypePropsToRuntimeProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.addText(`>`); - - if (scriptSetupRanges.withDefaultsArg) { - codeGen.addText(`, typeof __VLS_withDefaultsArg`); - codeGen.addText(`>`); - } - } - else if (scriptSetupRanges.propsRuntimeArg) { - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - } - codeGen.addText(`),\n`); - } - if (scriptSetupRanges.emitsTypeArg) { - usedTypes.ConstructorOverloads = true; - codeGen.addText(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.addText(`>>),\n`); - } - else if (scriptSetupRanges.emitsRuntimeArg) { - codeGen.addText(`emits: (`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); - codeGen.addText(`),\n`); - } - } - const bindingsArr: { - bindings: { start: number, end: number; }[], - content: string, - vueTag: 'script' | 'scriptSetup', - }[] = []; - if (scriptSetupRanges) { - bindingsArr.push({ - bindings: scriptSetupRanges.bindings, - content: scriptSetup.content, - vueTag: 'scriptSetup', - }); - } - if (scriptRanges && script) { - bindingsArr.push({ - bindings: scriptRanges.bindings, - content: script.content, - vueTag: 'script', - }); - } - - codeGen.addText(`setup() {\n`); - - if (lsType === 'script') { - codeGen.addText(`() => {\n`); - for (const bindText of getStyleBindTexts()) { - codeGen.addText('// @ts-ignore\n'); - codeGen.addText(bindText + ';\n'); - } - writeTemplate(); - codeGen.addText(`};\n`); - } - - codeGen.addText(`return {\n`); - - if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { - // fill $props - if (scriptSetupRanges.propsTypeArg) { - // NOTE: defineProps is inaccurate for $props - codeGen.addText(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.addText(`>()),\n`); - } - else if (scriptSetupRanges.propsRuntimeArg) { - // NOTE: defineProps is inaccurate for $props - codeGen.addText(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - codeGen.addText(`)),\n`); - } - // fill $emit - if (scriptSetupRanges.emitsAssignName) { - codeGen.addText(`$emit: ${scriptSetupRanges.emitsAssignName},\n`); - } - else if (scriptSetupRanges.emitsTypeArg) { - codeGen.addText(`$emit: defineEmits<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.addText(`>(),\n`); - } - else if (scriptSetupRanges.emitsRuntimeArg) { - codeGen.addText(`$emit: defineEmits(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); - codeGen.addText(`),\n`); - } - } - - if (lsType === 'script') { - if (scriptSetupRanges.exposeTypeArg) { - codeGen.addText(`...({} as `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeTypeArg.start, scriptSetupRanges.exposeTypeArg.end); - codeGen.addText(`),\n`); - } - else if (scriptSetupRanges.exposeRuntimeArg) { - codeGen.addText(`...(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); - codeGen.addText(`),\n`); - } - } - if (lsType === 'template') { - // fill ctx from props - if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { - if (scriptSetupRanges.propsAssignName) { - codeGen.addText(`...${scriptSetupRanges.propsAssignName},\n`); - } - else if (scriptSetupRanges.withDefaultsArg && scriptSetupRanges.propsTypeArg) { - codeGen.addText(`...withDefaults(defineProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.addText(`>(), `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); - codeGen.addText(`),\n`); - } - else if (scriptSetupRanges.propsRuntimeArg) { - codeGen.addText(`...defineProps(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - codeGen.addText(`),\n`); - } - } - // bindings - for (const { bindings, content } of bindingsArr) { - for (const expose of bindings) { - const varName = content.substring(expose.start, expose.end); - const templateSideRange = codeGen.addText(varName); - codeGen.addText(`: `); - const scriptSideRange = codeGen.addText(varName); - codeGen.addText(',\n'); - - teleports.push({ - sourceRange: scriptSideRange, - mappedRange: templateSideRange, - mode: SourceMaps.Mode.Offset, - data: { - toSource: { - capabilities: { - definitions: true, - references: true, - rename: true, - }, - }, - toTarget: { - capabilities: { - definitions: true, - references: true, - rename: true, - }, - }, - }, - }); - } - } - } - codeGen.addText(`};\n`); - - codeGen.addText(`},\n`); - } - - if (script && scriptRanges?.exportDefault?.args) { - addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); - } - - codeGen.addText(`});\n`); - if (lsType === 'template') { - codeGen.addText(`return __VLS_Component;\n`); - } - else { - codeGen.addText(`const __VLS_slots = (await import('./${path.basename(fileName)}.__VLS_template.jsx')).default;\n`); - codeGen.addText(`return {} as typeof __VLS_Component & (new () => { ${getSlotsPropertyName(vueVersion)}: typeof __VLS_slots });\n`); - } - codeGen.addText(`};\n`); - codeGen.addText(`return await __VLS_setup();\n`); - codeGen.addText(`})();`); - exportdefaultEnd = codeGen.getText().length; - - codeGen.addText(`\n`); - } - function writeExportOptions() { - codeGen.addText(`\n`); - codeGen.addText(`export const __VLS_options = {\n`); - if (script && scriptRanges?.exportDefault?.args) { - const args = scriptRanges.exportDefault.args; - codeGen.addText(`...(`); - codeGen.addCode( - script.content.substring(args.start, args.end), - args, - SourceMaps.Mode.Offset, - { - vueTag: 'script', - capabilities: { - references: true, - rename: true, - }, - }, - ); - codeGen.addText(`),\n`); - } - if (scriptSetupRanges?.propsRuntimeArg && scriptSetup) { - codeGen.addText(`props: (`); - codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end), - scriptSetupRanges.propsRuntimeArg, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: { - references: true, - definitions: true, - rename: true, - }, - }, - ); - codeGen.addText(`),\n`); - } - else if (scriptSetupRanges?.propsTypeArg && scriptSetup) { - codeGen.addText(`props: ({} as `); - codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end), - scriptSetupRanges.propsTypeArg, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: { - references: true, - definitions: true, - rename: true, - }, - }, - ); - codeGen.addText(`),\n`); - } - if (scriptSetupRanges?.emitsRuntimeArg && scriptSetup) { - codeGen.addText(`emits: (`); - codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end), - scriptSetupRanges.emitsRuntimeArg, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: { - references: true, - definitions: true, - rename: true, - }, - }, - ); - codeGen.addText(`),\n`); - } - else if (scriptSetupRanges?.emitsTypeArg && scriptSetup) { - codeGen.addText(`emits: ({} as `); - codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end), - scriptSetupRanges.emitsTypeArg, - SourceMaps.Mode.Offset, - { - vueTag: 'scriptSetup', - capabilities: {}, - }, - ); - codeGen.addText(`),\n`); - } - codeGen.addText(`};\n`); - } - function writeExportTypes() { - - const bindingsArr: { - typeBindings: { start: number, end: number; }[], - content: string, - }[] = []; - - if (scriptSetupRanges && scriptSetup) { - bindingsArr.push({ - typeBindings: scriptSetupRanges.typeBindings, - content: scriptSetup.content, - }); - } - // if (scriptRanges && script) { - // bindingsArr.push({ - // typeBindings: scriptRanges.typeBindings, - // content: script.content, - // }); - // } - - codeGen.addText('export {\n'); - for (const bindings of bindingsArr) { - for (const typeBinding of bindings.typeBindings) { - const text = bindings.content.substring(typeBinding.start, typeBinding.end); - codeGen.addText(`${text} as __VLS_types_${text},\n`); - } - } - codeGen.addText('};\n'); - } - function writeConstNameOption() { - codeGen.addText(`\n`); - if (script && scriptRanges?.exportDefault?.args) { - const args = scriptRanges.exportDefault.args; - codeGen.addText(`export const __VLS_name = (await import('./__VLS_types.js')).getNameOption(`); - codeGen.addText(`${script.content.substring(args.start, args.end)} as const`); - codeGen.addText(`);\n`); - } - else if (scriptSetup) { - codeGen.addText(`export declare const __VLS_name: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); - } - else { - codeGen.addText(`export const __VLS_name = undefined;\n`); - } - } - function writeTemplate() { - - const htmlGen = getHtmlGen(); - if (!htmlGen) - return; - - let bindingNames: string[] = []; - - if (scriptSetupRanges) { - bindingNames = bindingNames.concat(scriptSetupRanges.bindings.map(range => scriptSetup?.content.substring(range.start, range.end) ?? '')); - } - if (scriptRanges) { - bindingNames = bindingNames.concat(scriptRanges.bindings.map(range => script?.content.substring(range.start, range.end) ?? '')); - } - - // fix import components unused report - codeGen.addText('// @ts-ignore\n'); - codeGen.addText('['); - for (const varName of bindingNames) { - if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenate(varName)]) { - codeGen.addText(varName + ', '); - } - } - for (const tag of Object.keys(htmlGen.tagNames)) { - if (tag.indexOf('.') >= 0) { - codeGen.addText(tag + ', '); - } - } - codeGen.addText('];\n'); - - codeGen.addText('// @ts-ignore\n'); - codeGen.addText('['); - codeGen.addText([...htmlGen.identifiers].join(', ')); - codeGen.addText('];\n'); - } -} - -// TODO: not working for overloads > n (n = 8) -// see: https://github.com/johnsoncodehk/volar/issues/60 -export function genConstructorOverloads(name = 'ConstructorOverloads', nums?: number) { - let code = `type ${name} =\n`; - if (nums === undefined) { - for (let i = 8; i >= 1; i--) { - gen(i); - } - } - else { - gen(nums); - } - code += `// 0\n`; - code += `{};\n`; - return code; - - function gen(i: number) { - code += `// ${i}\n`; - code += `T extends {\n`; - for (let j = 1; j <= i; j++) { - code += `(event: infer E${j}, ...payload: infer P${j}): void;\n`; - } - code += `} ? (\n`; - for (let j = 1; j <= i; j++) { - if (j > 1) code += '& '; - code += `(E${j} extends string ? { [K${j} in E${j}]: (...payload: P${j}) => void } : {})\n`; - } - code += `) :\n`; - } -} diff --git a/packages/vue-code-gen/src/index.ts b/packages/vue-code-gen/src/index.ts deleted file mode 100644 index 66c637555..000000000 --- a/packages/vue-code-gen/src/index.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { generate as generateScript, getSlotsPropertyName, getVueLibraryName } from './generators/script'; -import { - isIntrinsicElement, - walkElementNodes, -} from './generators/template'; -import { parseScriptRanges } from './parsers/scriptRanges'; -import { parseScriptSetupRanges } from './parsers/scriptSetupRanges'; -import * as CompilerDOM from '@vue/compiler-dom'; -import * as CompilerVue2 from './vue2TemplateCompiler'; - -export * from './types'; -export * from '@vue/compiler-dom'; -export { isIntrinsicElement, getSlotsPropertyName, getVueLibraryName, walkElementNodes }; - -/** - * @param templateAst Use `require('@vue/compiler-dom').compile` or `require('@volar/vue-code-gen').compileTemplate`, provide to resolve variables unused in script setup - * @param cssVars Use `require('@vue/compiler-sfc').parseCssVars`, provide to resolve variables unused in script setup - * @param vueLibName Where should `defineComponent` and `PropType` import from? (For example: `vue`, `@vue/runtime-dom`, `@vue/composition-api`) - */ -export function generateSFCScriptTypeCheckCode( - ts: typeof import('typescript/lib/tsserverlibrary'), - scriptLang: 'js' | 'jsx' | 'ts' | 'tsx', - scriptCode: string | undefined, - scriptSetupCode: string | undefined, - shimComponentOptions: 'defineComponent' | 'Vue.extend' | false, - downgradePropsAndEmitsToSetupReturnOnScriptSetup: boolean, - vueVersion: number, - templateAst?: CompilerDOM.RootNode, - cssVars?: string[], -) { - - const generated = generateScript( - 'script', - '', - scriptCode !== undefined ? { content: scriptCode } : undefined, - scriptSetupCode !== undefined ? { content: scriptSetupCode } : undefined, - scriptCode !== undefined ? parseScriptRanges( - ts, - ts.createSourceFile('dummy.' + scriptLang, scriptCode, ts.ScriptTarget.ESNext), - scriptSetupCode !== undefined, - false, - false, - ) : undefined, - scriptSetupCode !== undefined ? parseScriptSetupRanges( - ts, - ts.createSourceFile('dummy.' + scriptLang, scriptSetupCode, ts.ScriptTarget.ESNext) - ) : undefined, - () => undefined, // TODO - // () => templateAst ? generateTemplateScript(templateAst) : undefined, - () => cssVars ?? [], - shimComponentOptions, - downgradePropsAndEmitsToSetupReturnOnScriptSetup, - vueVersion, - ); - - return { - code: generated.codeGen.getText(), - scriptMappings: getScriptMappings('script'), - scriptSetupMappings: getScriptMappings('scriptSetup'), - }; - - function getScriptMappings(vueTag: 'script' | 'scriptSetup') { - return generated.codeGen.getMappings() - .filter(mapping => - mapping.data.vueTag === vueTag - && mapping.data.capabilities.diagnostic - ) - .map(mapping => ({ - sourceTextRange: mapping.sourceRange, - generatedTextRange: mapping.mappedRange, - })); - } -} - -/** - * A wrapper function of `require('@vue/compiler-dom').compile` - */ -export function compileSFCTemplate(htmlCode: string, options: CompilerDOM.CompilerOptions = {}, vueVersion: number) { - - const errors: CompilerDOM.CompilerError[] = []; - const warnings: CompilerDOM.CompilerError[] = []; - let ast: CompilerDOM.RootNode | undefined; - - try { - ast = (vueVersion < 3 ? CompilerVue2 : CompilerDOM).compile(htmlCode, { - onError: (err: CompilerDOM.CompilerError) => errors.push(err), - onWarn: (err: CompilerDOM.CompilerError) => warnings.push(err), - expressionPlugins: ['typescript'], - ...options, - }).ast; - } - catch (e) { - const err = e as CompilerDOM.CompilerError; - errors.push(err); - } - - return { - errors, - warnings, - ast, - }; -} diff --git a/packages/vue-code-gen/src/types.ts b/packages/vue-code-gen/src/types.ts deleted file mode 100644 index efe52d7d4..000000000 --- a/packages/vue-code-gen/src/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -export interface EmbeddedFileMappingData { - vueTag: 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined, - vueTagIndex?: number, - normalizeNewName?: (newName: string) => string, - applyNewName?: (oldName: string, newName: string) => string, - capabilities: { - basic?: boolean, - references?: boolean, - definitions?: boolean, - diagnostic?: boolean, - rename?: boolean | { - in: boolean, - out: boolean, - }, - completion?: boolean, - semanticTokens?: boolean, - referencesCodeLens?: boolean, - displayWithLink?: boolean, - }, -} - -export interface TeleportSideData { - transformNewName?: (newName: string) => string, - capabilities: { - references?: boolean, - definitions?: boolean, - rename?: boolean, - }, -} - -export interface TeleportMappingData { - isAdditionalReference?: boolean; - toSource: TeleportSideData, - toTarget: TeleportSideData, -} - -export interface TextRange { - start: number, - end: number, -} diff --git a/packages/vue-code-gen/tsconfig.build.json b/packages/vue-code-gen/tsconfig.build.json deleted file mode 100644 index 4bd966efb..000000000 --- a/packages/vue-code-gen/tsconfig.build.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "out", - "rootDir": "src", - }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", - ".vscode-test" - ], - "references": [ - { - "path": "../shared/tsconfig.build.json" - }, - { - "path": "../source-map/tsconfig.build.json" - }, - { - "path": "../code-gen/tsconfig.build.json" - }, - ], -} \ No newline at end of file diff --git a/packages/vue-language-core/package.json b/packages/vue-language-core/package.json index d7bc3c36a..b88ae2b01 100644 --- a/packages/vue-language-core/package.json +++ b/packages/vue-language-core/package.json @@ -19,9 +19,11 @@ "dependencies": { "@volar/code-gen": "0.39.5", "@volar/source-map": "0.39.5", - "@volar/vue-code-gen": "0.39.5", + "@vue/compiler-core": "^3.2.37", + "@vue/compiler-dom": "^3.2.37", "@vue/compiler-sfc": "^3.2.37", - "@vue/reactivity": "^3.2.37" + "@vue/reactivity": "^3.2.37", + "@vue/shared": "^3.2.37" }, "browser": { "./out/plugins/vue-template-pug.js": "./out/plugins/empty.js" diff --git a/packages/vue-language-core/src/documentRegistry.ts b/packages/vue-language-core/src/documentRegistry.ts index f2f5207ce..ee336f9d5 100644 --- a/packages/vue-language-core/src/documentRegistry.ts +++ b/packages/vue-language-core/src/documentRegistry.ts @@ -1,4 +1,4 @@ -import type { EmbeddedFileMappingData } from '@volar/vue-code-gen'; +import type { EmbeddedFileMappingData } from './types'; import { computed, shallowReactive } from '@vue/reactivity'; import { posix as path } from 'path'; import * as localTypes from './utils/localTypes'; diff --git a/packages/vue-language-core/src/generators/script.ts b/packages/vue-language-core/src/generators/script.ts new file mode 100644 index 000000000..1b3729563 --- /dev/null +++ b/packages/vue-language-core/src/generators/script.ts @@ -0,0 +1,852 @@ +import { CodeGen, mergeCodeGen } from '@volar/code-gen'; +import * as SourceMaps from '@volar/source-map'; +import { hyphenate } from '@vue/shared'; +import { posix as path } from 'path'; +import type * as templateGen from '../generators/template'; +import type { ScriptRanges } from '../parsers/scriptRanges'; +import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; +import { useCssVars, useStyleCssClasses } from '../sourceFile'; +import type { EmbeddedFileMappingData, TeleportMappingData } from '../types'; +import { TextRange, VueCompilerOptions } from '../types'; +import { getSlotsPropertyName, getVueLibraryName } from '../utils/shared'; +import { SearchTexts } from '../utils/string'; +import { walkInterpolationFragment } from '../utils/transform'; + +/** + * TODO: rewrite this + */ +export function generate( + ts: typeof import('typescript/lib/tsserverlibrary'), + fileName: string, + lang: string, + script: undefined | { + src?: string, + content: string, + }, + scriptSetup: undefined | { + content: string, + }, + scriptRanges: ScriptRanges | undefined, + scriptSetupRanges: ScriptSetupRanges | undefined, + cssVars: ReturnType['value'], + cssModuleClasses: ReturnType['value'], + cssScopedClasses: ReturnType['value'], + htmlGen: ReturnType | undefined, + compilerOptions: VueCompilerOptions, + codeGen = new CodeGen(), + teleports: SourceMaps.Mapping[] = [], +) { + + const downgradePropsAndEmitsToSetupReturnOnScriptSetup = (compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup ?? 'onlyJs') === 'onlyJs' + ? lang === 'js' || lang === 'jsx' + : !!compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup; + const vueVersion = compilerOptions.target ?? 3; + const vueLibName = getVueLibraryName(vueVersion); + const usedTypes = { + DefinePropsToOptions: false, + mergePropDefaults: false, + ConstructorOverloads: false, + }; + + let exportdefaultStart: number | undefined; + let exportdefaultEnd: number | undefined; + + writeScriptSrc(); + writeScriptSetupImportsSegment(); + writeScriptContentBeforeExportDefault(); + writeScriptSetupAndTemplate(); + writeScriptSetupTypes(); + writeScriptContentAfterExportDefault(); + writeTemplateIfNoScriptSetup(); + + if (!script && !scriptSetup) { + codeGen.addCode( + 'export default {} as any', + { + start: 0, + end: 0, + }, + SourceMaps.Mode.Expand, + { + vueTag: undefined, + capabilities: {}, + }, + ); + } + + if (scriptSetup) { + // for code action edits + codeGen.addCode( + '', + { + start: scriptSetup.content.length, + end: scriptSetup.content.length, + }, + SourceMaps.Mode.Offset, + { + vueTag: 'scriptSetup', + capabilities: {}, + }, + ); + } + + // fix https://github.com/johnsoncodehk/volar/issues/1048 + // fix https://github.com/johnsoncodehk/volar/issues/435 + codeGen.addMapping2({ + data: { + vueTag: undefined, + capabilities: {}, + }, + mode: SourceMaps.Mode.Expand, + mappedRange: { + start: 0, + end: codeGen.getText().length, + }, + sourceRange: { + start: 0, + end: 0, + }, + }); + + // fix https://github.com/johnsoncodehk/volar/issues/1127 + if (scriptSetup && exportdefaultStart !== undefined && exportdefaultEnd !== undefined) { + codeGen.addMapping2({ + data: { + vueTag: 'scriptSetup', + capabilities: { + diagnostic: true, + }, + }, + mode: SourceMaps.Mode.Totally, + mappedRange: { + start: exportdefaultStart, + end: exportdefaultEnd, + }, + sourceRange: { + start: 0, + end: scriptSetup.content.length, + }, + }); + } + + return { + codeGen: codeGen, + teleports, + }; + + function writeScriptSetupTypes() { + codeGen.addText(`// __VLS_dont_organize_me\n`); + codeGen.addText(`import * as __VLS_types from './__VLS_types.js';\n`); + if (usedTypes.DefinePropsToOptions) { + codeGen.addText(`type __VLS_NonUndefinedable = T extends undefined ? never : T;\n`); + codeGen.addText(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType<__VLS_NonUndefinedable> } : { type: import('${vueLibName}').PropType, required: true } };\n`); + } + if (usedTypes.mergePropDefaults) { + codeGen.addText(`type __VLS_WithDefaults = { + // use 'keyof Pick' instead of 'keyof P' to keep props jsdoc + [K in keyof Pick]: K extends keyof D ? P[K] & { + default: D[K] + } : P[K] + };\n`); + } + if (usedTypes.ConstructorOverloads) { + // fix https://github.com/johnsoncodehk/volar/issues/926 + codeGen.addText('type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;\n'); + if (scriptSetupRanges && scriptSetupRanges.emitsTypeNums !== -1) { + codeGen.addText(genConstructorOverloads('__VLS_ConstructorOverloads', scriptSetupRanges.emitsTypeNums)); + } + else { + codeGen.addText(genConstructorOverloads('__VLS_ConstructorOverloads')); + } + } + } + function writeScriptSrc() { + 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('.js') && !src.endsWith('.jsx')) src = src + '.js'; + + codeGen.addText(`export * from `); + codeGen.addCode( + `'${src}'`, + { start: -1, end: -1 }, + SourceMaps.Mode.Offset, + { + vueTag: 'scriptSrc', + capabilities: { + basic: true, + references: true, + definitions: true, + rename: true, + diagnostic: true, + completion: true, + semanticTokens: true, + }, + } + ); + codeGen.addText(`;\n`); + codeGen.addText(`export { default } from '${src}';\n`); + } + function writeScriptContentBeforeExportDefault() { + if (!script) + return; + + if (!!scriptSetup && scriptRanges?.exportDefault) { + addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); + exportdefaultStart = codeGen.getText().length - (scriptRanges.exportDefault.expression.start - scriptRanges.exportDefault.start); + } + else { + let isExportRawObject = false; + if (scriptRanges?.exportDefault) { + isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); + } + const wrapMode = getImplicitWrapComponentOptionsMode(lang); + if (isExportRawObject && wrapMode && scriptRanges?.exportDefault) { + addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); + if (wrapMode === 'defineComponent') { + codeGen.addText(`(await import('${vueLibName}')).defineComponent(`); + } + else { + codeGen.addText(`(await import('vue')).default.extend(`); + } + addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end); + codeGen.addText(`)`); + addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length); + } + else { + addVirtualCode('script', 0, script.content.length); + } + } + } + function writeScriptContentAfterExportDefault() { + if (!script) + return; + + if (!!scriptSetup && scriptRanges?.exportDefault) { + addVirtualCode('script', scriptRanges.exportDefault.end, script.content.length); + } + } + function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { + codeGen.addCode( + (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end), + { start, end }, + SourceMaps.Mode.Offset, + { + vueTag: vueTag, + capabilities: { + basic: true, + references: true, + definitions: true, + rename: true, + diagnostic: true, // also working for setup() returns unused in template checking + completion: true, + semanticTokens: true, + }, + } + ); + } + function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { + codeGen.addCode( + (vueTag === 'scriptSetup' ? scriptSetup : script)!.content.substring(start, end), + { start, end }, + SourceMaps.Mode.Offset, + { + vueTag, + capabilities: { + references: true, + definitions: true, + rename: true, + }, + }, + ); + } + function writeScriptSetupImportsSegment() { + + if (!scriptSetup) + return; + + if (!scriptSetupRanges) + return; + + codeGen.addCode( + scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset), + { + start: 0, + end: scriptSetupRanges.importSectionEndOffset, + }, + SourceMaps.Mode.Offset, + { + vueTag: 'scriptSetup', + capabilities: { + basic: true, + references: true, + definitions: true, + diagnostic: true, + rename: true, + completion: true, + semanticTokens: true, + }, + }, + ); + } + function writeTemplateIfNoScriptSetup() { + + if (!scriptSetup) { + writeTemplate(); + } + } + function writeScriptSetupAndTemplate() { + + if (scriptSetup && scriptSetupRanges) { + + if (scriptRanges?.exportDefault) { + codeGen.addText('await (async () => {\n'); + } + else { + exportdefaultStart = codeGen.getText().length; + codeGen.addText('export default await (async () => {\n'); + } + + codeGen.addText('const __VLS_setup = async () => {\n'); + + codeGen.addCode( + scriptSetup.content.substring(scriptSetupRanges.importSectionEndOffset), + { + start: scriptSetupRanges.importSectionEndOffset, + end: scriptSetup.content.length, + }, + SourceMaps.Mode.Offset, + { + vueTag: 'scriptSetup', + capabilities: { + basic: true, + references: true, + definitions: true, + diagnostic: true, + rename: true, + completion: true, + semanticTokens: true, + }, + }, + ); + + if (scriptSetupRanges.propsTypeArg && scriptSetupRanges.withDefaultsArg) { + // fix https://github.com/johnsoncodehk/volar/issues/1187 + codeGen.addText(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); + codeGen.addText(`);\n`); + } + + if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { + // use defineComponent() from user space code if it exist + codeGen.addText(`const __VLS_Component = `); + addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start); + codeGen.addText(`{\n`); + } + else { + codeGen.addText(`const __VLS_Component = (await import('${vueLibName}')).defineComponent({\n`); + } + + if (!downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { + codeGen.addText(`props: (`); + if (scriptSetupRanges.propsTypeArg) { + + usedTypes.DefinePropsToOptions = true; + codeGen.addText(`{} as `); + + if (scriptSetupRanges.withDefaultsArg) { + usedTypes.mergePropDefaults = true; + codeGen.addText(`__VLS_WithDefaults<`); + } + + codeGen.addText(`__VLS_TypePropsToRuntimeProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codeGen.addText(`>`); + + if (scriptSetupRanges.withDefaultsArg) { + codeGen.addText(`, typeof __VLS_withDefaultsArg`); + codeGen.addText(`>`); + } + } + else if (scriptSetupRanges.propsRuntimeArg) { + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + } + codeGen.addText(`),\n`); + } + if (scriptSetupRanges.emitsTypeArg) { + usedTypes.ConstructorOverloads = true; + codeGen.addText(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codeGen.addText(`>>),\n`); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codeGen.addText(`emits: (`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codeGen.addText(`),\n`); + } + } + + codeGen.addText(`setup() {\n`); + codeGen.addText(`return {\n`); + + if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + // fill $props + if (scriptSetupRanges.propsTypeArg) { + // NOTE: defineProps is inaccurate for $props + codeGen.addText(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codeGen.addText(`>()),\n`); + } + else if (scriptSetupRanges.propsRuntimeArg) { + // NOTE: defineProps is inaccurate for $props + codeGen.addText(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + codeGen.addText(`)),\n`); + } + // fill $emit + if (scriptSetupRanges.emitsAssignName) { + codeGen.addText(`$emit: ${scriptSetupRanges.emitsAssignName},\n`); + } + else if (scriptSetupRanges.emitsTypeArg) { + codeGen.addText(`$emit: defineEmits<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codeGen.addText(`>(),\n`); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codeGen.addText(`$emit: defineEmits(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codeGen.addText(`),\n`); + } + } + + if (scriptSetupRanges.exposeTypeArg) { + codeGen.addText(`...({} as `); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeTypeArg.start, scriptSetupRanges.exposeTypeArg.end); + codeGen.addText(`),\n`); + } + else if (scriptSetupRanges.exposeRuntimeArg) { + codeGen.addText(`...(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); + codeGen.addText(`),\n`); + } + + codeGen.addText(`};\n`); + codeGen.addText(`},\n`); + + if (script && scriptRanges?.exportDefault?.args) { + addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); + } + + codeGen.addText(`});\n`); + + writeTemplate(); + + codeGen.addText(`return {} as typeof __VLS_Component & (new () => { ${getSlotsPropertyName(vueVersion)}: ReturnType });\n`); + + codeGen.addText(`};\n`); + codeGen.addText(`return await __VLS_setup();\n`); + codeGen.addText(`})();`); + exportdefaultEnd = codeGen.getText().length; + + codeGen.addText(`\n`); + } + } + function writeTemplate() { + + if (lang === 'jsx' || lang === 'tsx') { + + codeGen.addText(`function __VLS_template() {\n`); + writeExportOptions(); + writeConstNameOption(); + const templateGened = writeTemplateContext(); + codeGen.addText(`}\n`); + + writeComponentForTemplateUsage(templateGened.cssIds); + } + else { + codeGen.addText(`const __VLS_template = () => ({});\n`); + } + } + function writeComponentForTemplateUsage(cssIds: Set) { + + if (scriptSetup && scriptSetupRanges) { + + codeGen.addText(`const __VLS_component = (await import('${vueLibName}')).defineComponent({\n`); + codeGen.addText(`setup() {\n`); + codeGen.addText(`return {\n`); + // fill ctx from props + if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + if (scriptSetupRanges.propsAssignName) { + codeGen.addText(`...${scriptSetupRanges.propsAssignName},\n`); + } + else if (scriptSetupRanges.withDefaultsArg && scriptSetupRanges.propsTypeArg) { + codeGen.addText(`...withDefaults(defineProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codeGen.addText(`>(), `); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); + codeGen.addText(`),\n`); + } + else if (scriptSetupRanges.propsRuntimeArg) { + codeGen.addText(`...defineProps(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + codeGen.addText(`),\n`); + } + } + // bindings + const bindingsArr: { + bindings: { start: number, end: number; }[], + content: string, + vueTag: 'script' | 'scriptSetup', + }[] = []; + bindingsArr.push({ + bindings: scriptSetupRanges.bindings, + content: scriptSetup.content, + vueTag: 'scriptSetup', + }); + if (scriptRanges && script) { + bindingsArr.push({ + bindings: scriptRanges.bindings, + content: script.content, + vueTag: 'script', + }); + } + const templateUsageVars = getTemplateUsageVars(); + for (const { bindings, content } of bindingsArr) { + for (const expose of bindings) { + const varName = content.substring(expose.start, expose.end); + if (!templateUsageVars.has(varName) && !cssIds.has(varName)) { + continue; + } + const templateSideRange = codeGen.addText(varName); + codeGen.addText(`: `); + const scriptSideRange = codeGen.addText(varName); + codeGen.addText(',\n'); + + teleports.push({ + sourceRange: scriptSideRange, + mappedRange: templateSideRange, + mode: SourceMaps.Mode.Offset, + data: { + toSource: { + capabilities: { + definitions: true, + references: true, + rename: true, + }, + }, + toTarget: { + capabilities: { + definitions: true, + references: true, + rename: true, + }, + }, + }, + }); + } + } + codeGen.addText(`};\n`); // return { + codeGen.addText(`},\n`); // setup() { + codeGen.addText(`});\n`); // defineComponent({ + } + else { + codeGen.addText(`let __VLS_component!: typeof import('./${path.basename(fileName)}')['default'];`); + } + } + function writeExportOptions() { + codeGen.addText(`\n`); + codeGen.addText(`const __VLS_options = {\n`); + if (script && scriptRanges?.exportDefault?.args) { + const args = scriptRanges.exportDefault.args; + codeGen.addText(`...(`); + codeGen.addCode( + script.content.substring(args.start, args.end), + args, + SourceMaps.Mode.Offset, + { + vueTag: 'script', + capabilities: { + references: true, + rename: true, + }, + }, + ); + codeGen.addText(`),\n`); + } + codeGen.addText(`};\n`); + } + function writeConstNameOption() { + codeGen.addText(`\n`); + if (script && scriptRanges?.exportDefault?.args) { + const args = scriptRanges.exportDefault.args; + codeGen.addText(`const __VLS_name = (await import('./__VLS_types.js')).getNameOption(`); + codeGen.addText(`${script.content.substring(args.start, args.end)} as const`); + codeGen.addText(`);\n`); + } + else if (scriptSetup) { + codeGen.addText(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); + } + else { + codeGen.addText(`const __VLS_name = undefined;\n`); + } + } + function writeTemplateContext() { + + const useGlobalThisTypeInCtx = fileName.endsWith('.html'); + + codeGen.addText(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); + if (scriptSetup) { + codeGen.addText(`InstanceType<__VLS_types.PickNotAny {}>> & `); + } + codeGen.addText(`InstanceType<__VLS_types.PickNotAny {}>> & {\n`); + + /* CSS Module */ + for (const cssModule of cssModuleClasses) { + codeGen.addText(`${cssModule.style.module}: Record`); + for (const classNameRange of cssModule.classNameRanges) { + writeCssClassProperty( + cssModule.index, + cssModule.style.content.substring(classNameRange.start + 1, classNameRange.end), + classNameRange, + 'string', + false, + ); + } + codeGen.addText(';\n'); + } + codeGen.addText(`};\n`); + + codeGen.addText(`let __VLS_vmUnwrap!: typeof __VLS_options & { components: { } };\n`); + + /* Components */ + codeGen.addText('/* Components */\n'); + codeGen.addText('let __VLS_otherComponents!: NonNullable & __VLS_types.GlobalComponents & typeof __VLS_vmUnwrap.components & __VLS_types.PickComponents;\n'); + codeGen.addText(`let __VLS_selfComponent!: __VLS_types.SelfComponent { ${getSlotsPropertyName(compilerOptions.target ?? 3)}: typeof __VLS_slots })>;\n`); + codeGen.addText('let __VLS_components!: typeof __VLS_otherComponents & Omit;\n'); + + codeGen.addText(`__VLS_components.${SearchTexts.Components};\n`); + codeGen.addText(`({} as __VLS_types.GlobalAttrs).${SearchTexts.GlobalAttrs};\n`); + + /* Style Scoped */ + codeGen.addText('/* Style Scoped */\n'); + codeGen.addText('type __VLS_StyleScopedClasses = {}'); + for (const scopedCss of cssScopedClasses) { + for (const classNameRange of scopedCss.classNameRanges) { + writeCssClassProperty( + scopedCss.index, + scopedCss.style.content.substring(classNameRange.start + 1, classNameRange.end), + classNameRange, + 'boolean', + true, + ); + } + } + codeGen.addText(';\n'); + codeGen.addText('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); + + codeGen.addText(`/* CSS variable injection */\n`); + const cssIds = writeCssVars(); + + if (htmlGen) { + mergeCodeGen(codeGen, htmlGen.codeGen); + } + + if (!htmlGen) { + codeGen.addText(`const __VLS_slots = {};\n`); + } + + codeGen.addText(`return __VLS_slots;\n`); + + return { cssIds }; + + function writeCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean) { + codeGen.addText(`\n & { `); + codeGen.addMapping2({ + mappedRange: { + start: codeGen.getText().length, + end: codeGen.getText().length + className.length + 2, + }, + sourceRange: classRange, + mode: SourceMaps.Mode.Totally, + additional: [{ + mappedRange: { + start: codeGen.getText().length + 1, // + ' + end: codeGen.getText().length + 1 + className.length, + }, + sourceRange: classRange, + mode: SourceMaps.Mode.Offset, + }], + data: { + vueTag: 'style', + vueTagIndex: styleIndex, + capabilities: { + references: true, + rename: true, + referencesCodeLens: true, + }, + normalizeNewName: beforeCssRename, + applyNewName: doCssRename, + }, + }); + codeGen.addText(`'${className}'${optional ? '?' : ''}: ${propertyType}`); + codeGen.addText(` }`); + } + function writeCssVars() { + + const emptyLocalVars: Record = {}; + const identifiers = new Set(); + + for (const cssVar of cssVars) { + for (const cssBind of cssVar.ranges) { + walkInterpolationFragment( + ts, + cssVar.style.content.substring(cssBind.start, cssBind.end), + (frag, fragOffset, isJustForErrorMapping) => { + if (fragOffset === undefined) { + codeGen.addText(frag); + } + else { + codeGen.addCode( + frag, + { + start: cssBind.start + fragOffset, + end: cssBind.start + fragOffset + frag.length, + }, + SourceMaps.Mode.Offset, + { + vueTag: 'style', + vueTagIndex: cssVar.styleIndex, + capabilities: isJustForErrorMapping ? { + diagnostic: true, + } : { + basic: true, + references: true, + definitions: true, + diagnostic: true, + rename: true, + completion: true, + semanticTokens: true, + }, + }, + ); + } + }, + emptyLocalVars, + identifiers, + ); + codeGen.addText(';\n'); + } + } + + return identifiers; + } + } + function getTemplateUsageVars() { + + const usageVars = new Set(); + + if (htmlGen) { + + let bindingNames: string[] = []; + + if (scriptSetupRanges) { + bindingNames = bindingNames.concat(scriptSetupRanges.bindings.map(range => scriptSetup?.content.substring(range.start, range.end) ?? '')); + } + if (scriptRanges) { + bindingNames = bindingNames.concat(scriptRanges.bindings.map(range => script?.content.substring(range.start, range.end) ?? '')); + } + + // fix import components unused report + for (const varName of bindingNames) { + if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenate(varName)]) { + usageVars.add(varName); + } + } + for (const tag of Object.keys(htmlGen.tagNames)) { + if (tag.indexOf('.') >= 0) { + usageVars.add(tag); + } + } + for (const _id of htmlGen.identifiers) { + usageVars.add(_id); + } + } + + return usageVars; + } + function getImplicitWrapComponentOptionsMode(lang: string) { + + let shimComponentOptionsMode: 'defineComponent' | 'Vue.extend' | false = false; + + if ( + (compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend ?? 'onlyJs') === 'onlyJs' + ? lang === 'js' || lang === 'jsx' + : !!compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend + ) { + shimComponentOptionsMode = 'Vue.extend'; + } + if ( + (compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent ?? 'onlyJs') === 'onlyJs' + ? lang === 'js' || lang === 'jsx' + : !!compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent + ) { + shimComponentOptionsMode = 'defineComponent'; + } + + // true override 'onlyJs' + if (compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend === true) { + shimComponentOptionsMode = 'Vue.extend'; + } + if (compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent === true) { + shimComponentOptionsMode = 'defineComponent'; + } + + return shimComponentOptionsMode; + } +} + +// TODO: not working for overloads > n (n = 8) +// see: https://github.com/johnsoncodehk/volar/issues/60 +function genConstructorOverloads(name = 'ConstructorOverloads', nums?: number) { + let code = `type ${name} =\n`; + if (nums === undefined) { + for (let i = 8; i >= 1; i--) { + gen(i); + } + } + else { + gen(nums); + } + code += `// 0\n`; + code += `{};\n`; + return code; + + function gen(i: number) { + code += `// ${i}\n`; + code += `T extends {\n`; + for (let j = 1; j <= i; j++) { + code += `(event: infer E${j}, ...payload: infer P${j}): void;\n`; + } + code += `} ? (\n`; + for (let j = 1; j <= i; j++) { + if (j > 1) code += '& '; + code += `(E${j} extends string ? { [K${j} in E${j}]: (...payload: P${j}) => void } : {})\n`; + } + code += `) :\n`; + } +} + +function beforeCssRename(newName: string) { + return newName.startsWith('.') ? newName.slice(1) : newName; +} + +function doCssRename(oldName: string, newName: string) { + return '.' + newName; +} diff --git a/packages/vue-code-gen/src/generators/template.ts b/packages/vue-language-core/src/generators/template.ts similarity index 99% rename from packages/vue-code-gen/src/generators/template.ts rename to packages/vue-language-core/src/generators/template.ts index fcbb5ca0c..7a35a0ba1 100644 --- a/packages/vue-code-gen/src/generators/template.ts +++ b/packages/vue-language-core/src/generators/template.ts @@ -4,7 +4,7 @@ import { camelize, hyphenate, capitalize, isHTMLTag, isSVGTag } from '@vue/share import * as CompilerDOM from '@vue/compiler-dom'; import * as CompilerCore from '@vue/compiler-core'; import { EmbeddedFileMappingData } from '../types'; -import { colletVars, walkInterpolationFragment } from '../transform'; +import { colletVars, walkInterpolationFragment } from '../utils/transform'; import { parseBindingRanges } from '../parsers/scriptSetupRanges'; const capabilitiesSet = { diff --git a/packages/vue-language-core/src/index.ts b/packages/vue-language-core/src/index.ts index 44fb3005b..de156c9f0 100644 --- a/packages/vue-language-core/src/index.ts +++ b/packages/vue-language-core/src/index.ts @@ -4,5 +4,10 @@ export * from './sourceFile'; export * from './documentRegistry'; export * from './types'; export * from './lsContext'; +export * as refSugarRanges from './parsers/refSugarRanges'; +export * as scriptRanges from './parsers/scriptRanges'; +export * as scriptSetupConvertRanges from './parsers/scriptSetupConvertRanges'; +export * as scriptSetupRanges from './parsers/scriptSetupRanges'; export * as localTypes from './utils/localTypes'; export * as tsShared from './utils/ts'; +export * from './generators/template'; diff --git a/packages/vue-code-gen/src/parsers/refSugarRanges.ts b/packages/vue-language-core/src/parsers/refSugarRanges.ts similarity index 100% rename from packages/vue-code-gen/src/parsers/refSugarRanges.ts rename to packages/vue-language-core/src/parsers/refSugarRanges.ts diff --git a/packages/vue-code-gen/src/parsers/scriptRanges.ts b/packages/vue-language-core/src/parsers/scriptRanges.ts similarity index 100% rename from packages/vue-code-gen/src/parsers/scriptRanges.ts rename to packages/vue-language-core/src/parsers/scriptRanges.ts diff --git a/packages/vue-code-gen/src/parsers/scriptSetupConvertRanges.ts b/packages/vue-language-core/src/parsers/scriptSetupConvertRanges.ts similarity index 100% rename from packages/vue-code-gen/src/parsers/scriptSetupConvertRanges.ts rename to packages/vue-language-core/src/parsers/scriptSetupConvertRanges.ts diff --git a/packages/vue-code-gen/src/parsers/scriptSetupRanges.ts b/packages/vue-language-core/src/parsers/scriptSetupRanges.ts similarity index 100% rename from packages/vue-code-gen/src/parsers/scriptSetupRanges.ts rename to packages/vue-language-core/src/parsers/scriptSetupRanges.ts diff --git a/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts b/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts index 53752324b..7b69e7803 100644 --- a/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts +++ b/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts @@ -1,105 +1,106 @@ -import { generate as genScript } from '@volar/vue-code-gen/out/generators/script'; -import type * as templateGen from '@volar/vue-code-gen/out/generators/template'; -import { parseScriptRanges } from '@volar/vue-code-gen/out/parsers/scriptRanges'; -import { parseScriptSetupRanges } from '@volar/vue-code-gen/out/parsers/scriptSetupRanges'; +import { generate as genScript } from '../generators/script'; +import type * as templateGen from '../generators/template'; +import { parseScriptRanges } from '../parsers/scriptRanges'; +import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; import { Ref } from '@vue/reactivity'; +import { useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; import { VueCompilerOptions } from '../types'; -import { VueLanguagePlugin } from '../sourceFile'; export default function ( - lang: Ref, + ts: typeof import('typescript/lib/tsserverlibrary'), scriptRanges: Ref | undefined>, scriptSetupRanges: Ref | undefined>, htmlGen: Ref | undefined>, compilerOptions: VueCompilerOptions, - cssVars: Ref, + cssVars: ReturnType, + cssModuleClasses: ReturnType, + cssScopedClasses: ReturnType, + disableTemplateScript: boolean, ): VueLanguagePlugin { return { getEmbeddedFileNames(fileName, sfc) { + + const fileNames: string[] = []; + if (!fileName.endsWith('.html')) { - return [ - fileName + '.' + lang.value, - fileName + '.__VLS_script.' + lang.value, - ]; + let lang = !sfc.script && !sfc.scriptSetup ? 'ts' + : sfc.scriptSetup && sfc.scriptSetup.lang !== 'js' ? sfc.scriptSetup.lang + : sfc.script && sfc.script.lang !== 'js' ? sfc.script.lang + : 'js'; + if (!disableTemplateScript) { + if (lang === 'js') { + lang = 'jsx'; + } + else if (lang === 'ts') { + lang = 'tsx'; + } + } + fileNames.push(fileName + '.' + lang); + } + if (sfc.template) { + fileNames.push(fileName + '.__VLS_template_format.tsx'); + fileNames.push(fileName + '.__VLS_template.css'); } - return []; + + return fileNames; }, resolveEmbeddedFile(fileName, sfc, embeddedFile) { - const match = embeddedFile.fileName.replace(fileName, '').match(/^(\.__VLS_script)?\.(js|ts)x?$/); + const suffix = embeddedFile.fileName.replace(fileName, ''); + const match = suffix.match(/^\.(js|ts|jsx|tsx)?$/); if (match) { + const lang = match[1]; embeddedFile.isTsHostFile = true; - embeddedFile.capabilities = !match[1] ? { - diagnostics: !sfc.script?.src, - foldingRanges: false, - formatting: false, - documentSymbol: false, - codeActions: !sfc.script?.src, - inlayHints: !sfc.script?.src, - } : { - diagnostics: false, + embeddedFile.capabilities = { + diagnostics: true, foldingRanges: false, formatting: false, documentSymbol: false, - codeActions: false, - inlayHints: false, + codeActions: true, + inlayHints: true, }; genScript( - !match[1] ? 'script' : 'template', + ts, fileName, + lang, sfc.script ?? undefined, sfc.scriptSetup ?? undefined, scriptRanges.value, scriptSetupRanges.value, - () => htmlGen.value, - () => { - const bindTexts: string[] = []; - for (const cssVar of cssVars.value) { - bindTexts.push(cssVar); - } - return bindTexts; - }, - getShimComponentOptionsMode(), - (compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup ?? 'onlyJs') === 'onlyJs' - ? lang.value === 'js' || lang.value === 'jsx' - : !!compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup, - compilerOptions.target ?? 3, + cssVars.value, + cssModuleClasses.value, + cssScopedClasses.value, + htmlGen.value, + compilerOptions, embeddedFile.codeGen, embeddedFile.teleportMappings, ); } - }, - }; + else if (suffix.match(/^\.__VLS_template_format\.tsx$/)) { - function getShimComponentOptionsMode() { - - let shimComponentOptionsMode: 'defineComponent' | 'Vue.extend' | false = false; - - if ( - (compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend ?? 'onlyJs') === 'onlyJs' - ? lang.value === 'js' || lang.value === 'jsx' - : !!compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend - ) { - shimComponentOptionsMode = 'Vue.extend'; - } - if ( - (compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent ?? 'onlyJs') === 'onlyJs' - ? lang.value === 'js' || lang.value === 'jsx' - : !!compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent - ) { - shimComponentOptionsMode = 'defineComponent'; - } - - // true override 'onlyJs' - if (compilerOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend === true) { - shimComponentOptionsMode = 'Vue.extend'; - } - if (compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent === true) { - shimComponentOptionsMode = 'defineComponent'; - } + embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; + embeddedFile.capabilities = { + diagnostics: false, + foldingRanges: false, + formatting: true, + documentSymbol: true, + codeActions: false, + inlayHints: false, + }; + embeddedFile.isTsHostFile = false; + if (htmlGen.value) { + embeddedFile.codeGen = htmlGen.value.formatCodeGen; + } + } + else if (suffix.match(/^\.__VLS_template\.css$/)) { - return shimComponentOptionsMode; - } + embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; + if (htmlGen.value) { + embeddedFile.codeGen = htmlGen.value.cssCodeGen; + } + } + }, + }; } diff --git a/packages/vue-language-core/src/plugins/vue-typescript-template.ts b/packages/vue-language-core/src/plugins/vue-typescript-template.ts deleted file mode 100644 index 87074edb2..000000000 --- a/packages/vue-language-core/src/plugins/vue-typescript-template.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { mergeCodeGen } from '@volar/code-gen'; -import * as SourceMaps from '@volar/source-map'; -import { getSlotsPropertyName, getVueLibraryName, TextRange } from '@volar/vue-code-gen'; -import * as templateGen from '@volar/vue-code-gen/out/generators/template'; -import type { parseScriptSetupRanges } from '@volar/vue-code-gen/out/parsers/scriptSetupRanges'; -import { walkInterpolationFragment } from '@volar/vue-code-gen/out/transform'; -import { ComputedRef } from '@vue/reactivity'; -import { posix as path } from 'path'; -import { useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; -import { VueCompilerOptions } from '../types'; -import { SearchTexts } from '../utils/string'; - -export default function ( - ts: typeof import('typescript/lib/tsserverlibrary'), - cssModuleClasses: ReturnType, - cssScopedClasses: ReturnType, - templateCodeGens: ComputedRef | undefined>, - cssVars: ReturnType, - scriptSetupRanges: ComputedRef | undefined>, - scriptLang: ComputedRef, - compilerOptions: VueCompilerOptions, - disableTemplateScript: boolean, - useGlobalThisTypeInCtx: boolean, -): VueLanguagePlugin { - - return { - - getEmbeddedFileNames(fileName, sfc) { - const fileNames: string[] = []; - const lang = scriptLang.value === 'js' ? 'jsx' : scriptLang.value === 'ts' ? 'tsx' : scriptLang.value; - if (!disableTemplateScript) { - fileNames.push(fileName + '.__VLS_template.' + lang); - } - if (templateCodeGens.value) { - fileNames.push(fileName + '.__VLS_template_format.' + lang); - fileNames.push(fileName + '.__VLS_template.css'); - } - return fileNames; - }, - - resolveEmbeddedFile(fileName, sfc, embeddedFile) { - const suffix = embeddedFile.fileName.replace(fileName, ''); - - if (suffix.match(/^\.__VLS_template\.(jsx|tsx)$/)) { - - const baseFileName = path.basename(fileName); - embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; - embeddedFile.capabilities = { - diagnostics: true, - foldingRanges: false, - formatting: false, - documentSymbol: false, - codeActions: false, - inlayHints: true, - }; - embeddedFile.isTsHostFile = true; - - let scriptLeadingComments: string[] = []; - if (compilerOptions.experimentalUseScriptLeadingCommentInTemplate ?? true) { - for (const _script of [sfc.script, sfc.scriptSetup]) { - if (_script) { - const commentRanges = ts.getLeadingCommentRanges(_script.content, 0); - if (commentRanges) { - scriptLeadingComments = commentRanges.map(range => _script!.content.substring(range.pos, range.end)); - } - } - } - } - const scriptLeadingComment = scriptLeadingComments.join('\n'); - const tsxCodeGen = embeddedFile.codeGen; - - tsxCodeGen.addText(scriptLeadingComment + '\n'); - tsxCodeGen.addText(`import * as __VLS_types from './__VLS_types.js';\n`); - - if (sfc.script || sfc.scriptSetup) { - tsxCodeGen.addText(`import { __VLS_options, __VLS_name } from './${baseFileName}.__VLS_script.js';\n`); - tsxCodeGen.addText(`import __VLS_component from './${baseFileName}.__VLS_script.js';\n`); - } - else { - tsxCodeGen.addText(`var __VLS_name = undefined;\n`); - tsxCodeGen.addText(`var __VLS_options = {};\n`); - tsxCodeGen.addText(`var __VLS_component = (await import('${getVueLibraryName(compilerOptions.target ?? 3)}')).defineComponent({});\n`); - } - - writeImportTypes(); - - tsxCodeGen.addText(`declare var __VLS_ctx: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''} InstanceType<__VLS_types.PickNotAny {}>> & {\n`); - /* CSS Module */ - for (const cssModule of cssModuleClasses.value) { - tsxCodeGen.addText(`${cssModule.style.module}: Record`); - for (const classNameRange of cssModule.classNameRanges) { - writeCssClassProperty( - cssModule.index, - cssModule.style.content.substring(classNameRange.start + 1, classNameRange.end), - classNameRange, - 'string', - false, - ); - } - tsxCodeGen.addText(';\n'); - } - tsxCodeGen.addText(`};\n`); - - tsxCodeGen.addText(`declare var __VLS_vmUnwrap: typeof __VLS_options & { components: { } };\n`); - - /* Components */ - tsxCodeGen.addText('/* Components */\n'); - tsxCodeGen.addText('declare var __VLS_otherComponents: NonNullable & __VLS_types.GlobalComponents & typeof __VLS_vmUnwrap.components & __VLS_types.PickComponents;\n'); - tsxCodeGen.addText(`declare var __VLS_selfComponent: __VLS_types.SelfComponent { ${getSlotsPropertyName(compilerOptions.target ?? 3)}: typeof __VLS_slots })>;\n`); - tsxCodeGen.addText('declare var __VLS_components: typeof __VLS_otherComponents & Omit;\n'); - - tsxCodeGen.addText(`__VLS_components.${SearchTexts.Components};\n`); - tsxCodeGen.addText(`({} as __VLS_types.GlobalAttrs).${SearchTexts.GlobalAttrs};\n`); - - /* Style Scoped */ - tsxCodeGen.addText('/* Style Scoped */\n'); - tsxCodeGen.addText('type __VLS_StyleScopedClasses = {}'); - for (const scopedCss of cssScopedClasses.value) { - for (const classNameRange of scopedCss.classNameRanges) { - writeCssClassProperty( - scopedCss.index, - scopedCss.style.content.substring(classNameRange.start + 1, classNameRange.end), - classNameRange, - 'boolean', - true, - ); - } - } - tsxCodeGen.addText(';\n'); - tsxCodeGen.addText('declare var __VLS_styleScopedClasses: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); - - tsxCodeGen.addText(`/* CSS variable injection */\n`); - writeCssVars(); - - if (templateCodeGens.value) { - mergeCodeGen(tsxCodeGen, templateCodeGens.value.codeGen); - } - - tsxCodeGen.addText(`export default __VLS_slots;\n`); - - function writeImportTypes() { - - const bindingsArr: { - typeBindings: { start: number, end: number; }[], - content: string, - }[] = []; - - if (scriptSetupRanges.value && sfc.scriptSetup) { - bindingsArr.push({ - typeBindings: scriptSetupRanges.value.typeBindings, - content: sfc.scriptSetup.content, - }); - } - // if (scriptRanges.value && script.value) { - // bindingsArr.push({ - // typeBindings: scriptRanges.value.typeBindings, - // content: script.value.content, - // }); - // } - - tsxCodeGen.addText('import {\n'); - for (const bindings of bindingsArr) { - for (const typeBinding of bindings.typeBindings) { - const text = bindings.content.substring(typeBinding.start, typeBinding.end); - tsxCodeGen.addText(`__VLS_types_${text} as ${text},\n`); - } - } - tsxCodeGen.addText(`} from './${baseFileName}.__VLS_script.js';\n`); - } - function writeCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean) { - tsxCodeGen.addText(`\n & { `); - tsxCodeGen.addMapping2({ - mappedRange: { - start: tsxCodeGen.getText().length, - end: tsxCodeGen.getText().length + className.length + 2, - }, - sourceRange: classRange, - mode: SourceMaps.Mode.Totally, - additional: [{ - mappedRange: { - start: tsxCodeGen.getText().length + 1, // + ' - end: tsxCodeGen.getText().length + 1 + className.length, - }, - sourceRange: classRange, - mode: SourceMaps.Mode.Offset, - }], - data: { - vueTag: 'style', - vueTagIndex: styleIndex, - capabilities: { - references: true, - rename: true, - referencesCodeLens: true, - }, - normalizeNewName: beforeCssRename, - applyNewName: doCssRename, - }, - }); - tsxCodeGen.addText(`'${className}'${optional ? '?' : ''}: ${propertyType}`); - tsxCodeGen.addText(` }`); - } - function writeCssVars() { - - const emptyLocalVars: Record = {}; - const identifiers = new Set(); - - for (const cssVar of cssVars.value) { - for (const cssBind of cssVar.ranges) { - walkInterpolationFragment( - ts, - cssVar.style.content.substring(cssBind.start, cssBind.end), - (frag, fragOffset, isJustForErrorMapping) => { - if (fragOffset === undefined) { - tsxCodeGen.addText(frag); - } - else { - tsxCodeGen.addCode( - frag, - { - start: cssBind.start + fragOffset, - end: cssBind.start + fragOffset + frag.length, - }, - SourceMaps.Mode.Offset, - { - vueTag: 'style', - vueTagIndex: cssVar.styleIndex, - capabilities: isJustForErrorMapping ? { - diagnostic: true, - } : { - basic: true, - references: true, - definitions: true, - diagnostic: true, - rename: true, - completion: true, - semanticTokens: true, - }, - }, - ); - } - }, - emptyLocalVars, - identifiers, - ); - tsxCodeGen.addText(';\n'); - } - } - } - } - else if (suffix.match(/^\.__VLS_template_format\.[^\.]+$/)) { - - embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; - embeddedFile.capabilities = { - diagnostics: false, - foldingRanges: false, - formatting: true, - documentSymbol: true, - codeActions: false, - inlayHints: false, - }; - embeddedFile.isTsHostFile = false; - embeddedFile.codeGen = templateCodeGens.value!.formatCodeGen; - } - else if (suffix.match(/^\.__VLS_template\.css$/)) { - - embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; - embeddedFile.codeGen = templateCodeGens.value!.cssCodeGen; - } - }, - }; -} - -function beforeCssRename(newName: string) { - return newName.startsWith('.') ? newName.slice(1) : newName; -} - -function doCssRename(oldName: string, newName: string) { - return '.' + newName; -} diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index 5d7d1eca4..757922032 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -1,13 +1,13 @@ -import { compileSFCTemplate, EmbeddedFileMappingData, TeleportMappingData, TextRange } from '@volar/vue-code-gen'; -import { parseRefSugarCallRanges, parseRefSugarDeclarationRanges } from '@volar/vue-code-gen/out/parsers/refSugarRanges'; -import { parseScriptRanges } from '@volar/vue-code-gen/out/parsers/scriptRanges'; -import { parseScriptSetupRanges } from '@volar/vue-code-gen/out/parsers/scriptSetupRanges'; +import { EmbeddedFileMappingData, TeleportMappingData, TextRange } from './types'; +import { parseRefSugarCallRanges, parseRefSugarDeclarationRanges } from './parsers/refSugarRanges'; +import { parseScriptRanges } from './parsers/scriptRanges'; +import { parseScriptSetupRanges } from './parsers/scriptSetupRanges'; import { SFCBlock, SFCParseResult, SFCScriptBlock, SFCStyleBlock, SFCTemplateBlock } from '@vue/compiler-sfc'; import { computed, ComputedRef, reactive, ref, unref } from '@vue/reactivity'; import { VueCompilerOptions } from './types'; import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; import { SearchTexts } from './utils/string'; -import * as templateGen from '@volar/vue-code-gen/out/generators/template'; +import * as templateGen from './generators/template'; import { parseCssClassNames } from './utils/parseCssClassNames'; import { parseCssVars } from './utils/parseCssVars'; @@ -21,11 +21,11 @@ import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; import useVueSfcTemplate from './plugins/vue-sfc-template'; import useVueTsScripts from './plugins/vue-typescript-scripts'; -import useVueTsTemplate from './plugins/vue-typescript-template'; import type * as _0 from 'typescript/lib/tsserverlibrary'; // fix TS2742 import { Mapping, MappingBase } from '@volar/source-map'; import { CodeGen } from '@volar/code-gen'; +import { compileSFCTemplate } from './utils/compileSFCTemplate'; export interface VueLanguagePlugin { @@ -176,15 +176,6 @@ export function createSourceFile( ); }); const cssVars = useCssVars(sfc); - const cssVarTexts = computed(() => { - const result: string[] = []; - for (const { style, ranges } of cssVars.value) { - for (const range of ranges) { - result.push(style.content.substring(range.start, range.end)); - } - } - return result; - }); const scriptAst = computed(() => { if (sfc.script) { return ts.createSourceFile(fileName + '.' + sfc.script.lang, sfc.script.content, ts.ScriptTarget.Latest); @@ -205,12 +196,6 @@ export function createSourceFile( ? parseScriptSetupRanges(ts, scriptSetupAst.value) : undefined ); - const scriptLang = computed(() => { - return !sfc.script && !sfc.scriptSetup ? 'ts' - : sfc.scriptSetup && sfc.scriptSetup.lang !== 'js' ? sfc.scriptSetup.lang - : sfc.script && sfc.script.lang !== 'js' ? sfc.script.lang - : 'js'; - }); const sfcRefSugarRanges = computed(() => (scriptSetupAst.value ? { refs: parseRefSugarDeclarationRanges(ts, scriptSetupAst.value, ['$ref', '$computed', '$shallowRef', '$fromRefs']), raws: parseRefSugarCallRanges(ts, scriptSetupAst.value, ['$raw', '$fromRefs']), @@ -228,24 +213,15 @@ export function createSourceFile( useVueSfcScriptsFormat(), useVueSfcTemplate(), useVueTsScripts( - scriptLang, + ts, scriptRanges, scriptSetupRanges, templateCodeGens, vueCompilerOptions, - cssVarTexts, - ), - useVueTsTemplate( - ts, + cssVars, cssModuleClasses, cssScopedClasses, - templateCodeGens, - cssVars, - scriptSetupRanges, - scriptLang, - vueCompilerOptions, !!vueCompilerOptions.experimentalDisableTemplateSupport || compilerOptions.jsx !== ts.JsxEmit.Preserve, - fileName.endsWith('.html') // petite-vue ), ]; @@ -421,7 +397,7 @@ export function createSourceFile( }, getSfcTemplateLanguageCompiled: () => computedHtmlTemplate.value, getSfcVueTemplateCompiled: () => templateAstCompiled.value, - getScriptFileName: () => fileName.endsWith('.html') ? fileName + '.__VLS_script.' + scriptLang.value : fileName + '.' + scriptLang.value, + getScriptFileName: () => allEmbeddeds.value.find(e => e.file.fileName.replace(fileName, '').match(/^\.(js|ts)x?$/))?.file.fileName, getDescriptor: () => unref(sfc), getScriptAst: () => scriptAst.value, getScriptSetupAst: () => scriptSetupAst.value, diff --git a/packages/vue-language-core/src/types.ts b/packages/vue-language-core/src/types.ts index d25a8e410..94a035104 100644 --- a/packages/vue-language-core/src/types.ts +++ b/packages/vue-language-core/src/types.ts @@ -21,5 +21,45 @@ export interface VueCompilerOptions { experimentalDisableTemplateSupport?: boolean; experimentalResolveStyleCssClasses?: 'scoped' | 'always' | 'never'; experimentalAllowTypeNarrowingInInlineHandlers?: boolean; - experimentalUseScriptLeadingCommentInTemplate?: boolean; +} + +export interface EmbeddedFileMappingData { + vueTag: 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined, + vueTagIndex?: number, + normalizeNewName?: (newName: string) => string, + applyNewName?: (oldName: string, newName: string) => string, + capabilities: { + basic?: boolean, + references?: boolean, + definitions?: boolean, + diagnostic?: boolean, + rename?: boolean | { + in: boolean, + out: boolean, + }, + completion?: boolean, + semanticTokens?: boolean, + referencesCodeLens?: boolean, + displayWithLink?: boolean, + }, +} + +export interface TeleportSideData { + transformNewName?: (newName: string) => string, + capabilities: { + references?: boolean, + definitions?: boolean, + rename?: boolean, + }, +} + +export interface TeleportMappingData { + isAdditionalReference?: boolean; + toSource: TeleportSideData, + toTarget: TeleportSideData, +} + +export interface TextRange { + start: number, + end: number, } diff --git a/packages/vue-language-core/src/utils/compileSFCTemplate.ts b/packages/vue-language-core/src/utils/compileSFCTemplate.ts new file mode 100644 index 000000000..993028b76 --- /dev/null +++ b/packages/vue-language-core/src/utils/compileSFCTemplate.ts @@ -0,0 +1,116 @@ +import * as CompilerDom from '@vue/compiler-dom'; +import * as CompilerCore from '@vue/compiler-core'; +import * as CompilerDOM from '@vue/compiler-dom'; +import * as CompilerVue2 from './vue2TemplateCompiler'; + +export function compileSFCTemplate(htmlCode: string, options: CompilerDOM.CompilerOptions = {}, vueVersion: number) { + + const errors: CompilerDOM.CompilerError[] = []; + const warnings: CompilerDOM.CompilerError[] = []; + let ast: CompilerDOM.RootNode | undefined; + + try { + ast = (vueVersion < 3 ? CompilerVue2 : CompilerDOM).compile(htmlCode, { + onError: (err: CompilerDOM.CompilerError) => errors.push(err), + onWarn: (err: CompilerDOM.CompilerError) => warnings.push(err), + expressionPlugins: ['typescript'], + ...options, + }).ast; + } + catch (e) { + const err = e as CompilerDOM.CompilerError; + errors.push(err); + } + + return { + errors, + warnings, + ast, + }; +} + +export function compile( + template: string, + options: CompilerDom.CompilerOptions = {} +): CompilerDom.CodegenResult { + + const onError = options.onError; + options.onError = (error) => { + if ( + error.code === CompilerCore.ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT // :key binding allowed in v-for template child in vue 2 + || error.code === CompilerCore.ErrorCodes.X_V_IF_SAME_KEY // fix https://github.com/johnsoncodehk/volar/issues/1638 + ) { + return; + } + if (onError) { + onError(error); + } + else { + throw error; + } + }; + + return baseCompile( + template, + Object.assign({}, CompilerDom.parserOptions, options, { + nodeTransforms: [ + ...CompilerDom.DOMNodeTransforms, + ...(options.nodeTransforms || []) + ], + directiveTransforms: Object.assign( + {}, + CompilerDom.DOMDirectiveTransforms, + options.directiveTransforms || {} + ), + }) + ); +} + +export function baseCompile( + template: string, + options: CompilerCore.CompilerOptions = {} +): CompilerCore.CodegenResult { + + const onError = options.onError || ((error) => { throw error; }); + const isModuleMode = options.mode === 'module'; + + const prefixIdentifiers = options.prefixIdentifiers === true || isModuleMode; + if (!prefixIdentifiers && options.cacheHandlers) { + onError(CompilerCore.createCompilerError(CompilerCore.ErrorCodes.X_CACHE_HANDLER_NOT_SUPPORTED)); + } + if (options.scopeId && !isModuleMode) { + onError(CompilerCore.createCompilerError(CompilerCore.ErrorCodes.X_SCOPE_ID_NOT_SUPPORTED)); + } + + const ast = CompilerCore.baseParse(template, options); + const [nodeTransforms, directiveTransforms] = CompilerCore.getBaseTransformPreset(prefixIdentifiers); + + // v-for > v-if in vue 2 + const transformIf = nodeTransforms[1]; + const transformFor = nodeTransforms[3]; + nodeTransforms[1] = transformFor; + nodeTransforms[3] = transformIf; + + CompilerCore.transform( + ast, + Object.assign({}, options, { + prefixIdentifiers, + nodeTransforms: [ + ...nodeTransforms, + ...(options.nodeTransforms || []) // user transforms + ], + directiveTransforms: Object.assign( + {}, + directiveTransforms, + options.directiveTransforms || {} // user transforms + ) + }) + ); + + return CompilerCore.generate( + ast, + Object.assign({}, options, { + prefixIdentifiers + }) + ); +} diff --git a/packages/vue-language-core/src/utils/localTypes.ts b/packages/vue-language-core/src/utils/localTypes.ts index 0af1d6684..c74e1d4e9 100644 --- a/packages/vue-language-core/src/utils/localTypes.ts +++ b/packages/vue-language-core/src/utils/localTypes.ts @@ -1,4 +1,4 @@ -import { getSlotsPropertyName, getVueLibraryName } from '@volar/vue-code-gen'; +import { getSlotsPropertyName, getVueLibraryName } from './shared'; export const typesFileName = '__VLS_types.ts'; diff --git a/packages/vue-language-core/src/utils/shared.ts b/packages/vue-language-core/src/utils/shared.ts new file mode 100644 index 000000000..626c8de25 --- /dev/null +++ b/packages/vue-language-core/src/utils/shared.ts @@ -0,0 +1,7 @@ +export function getSlotsPropertyName(vueVersion: number) { + return vueVersion < 3 ? '$scopedSlots' : '$slots'; +} + +export function getVueLibraryName(vueVersion: number) { + return vueVersion < 2.7 ? '@vue/runtime-dom' : 'vue'; +} diff --git a/packages/vue-language-core/src/utils/sourceMaps.ts b/packages/vue-language-core/src/utils/sourceMaps.ts index 04337a9d9..5f0f37d3e 100644 --- a/packages/vue-language-core/src/utils/sourceMaps.ts +++ b/packages/vue-language-core/src/utils/sourceMaps.ts @@ -1,5 +1,5 @@ import * as SourceMaps from '@volar/source-map'; -import { EmbeddedFileMappingData, TeleportMappingData, TeleportSideData } from '@volar/vue-code-gen/out/types'; +import { EmbeddedFileMappingData, TeleportMappingData, TeleportSideData } from '../types'; export class EmbeddedFileSourceMap extends SourceMaps.SourceMapBase { } diff --git a/packages/vue-code-gen/src/transform.ts b/packages/vue-language-core/src/utils/transform.ts similarity index 100% rename from packages/vue-code-gen/src/transform.ts rename to packages/vue-language-core/src/utils/transform.ts diff --git a/packages/vue-code-gen/src/vue2TemplateCompiler.ts b/packages/vue-language-core/src/utils/vue2TemplateCompiler.ts similarity index 100% rename from packages/vue-code-gen/src/vue2TemplateCompiler.ts rename to packages/vue-language-core/src/utils/vue2TemplateCompiler.ts diff --git a/packages/vue-language-core/tsconfig.build.json b/packages/vue-language-core/tsconfig.build.json index d40153ad6..d5cb8357a 100644 --- a/packages/vue-language-core/tsconfig.build.json +++ b/packages/vue-language-core/tsconfig.build.json @@ -30,8 +30,5 @@ { "path": "../typescript-language-service/tsconfig.build.json" }, - { - "path": "../vue-code-gen/tsconfig.build.json" - }, ], } \ No newline at end of file diff --git a/packages/vue-language-service/package.json b/packages/vue-language-service/package.json index 888c85680..28e34c46d 100644 --- a/packages/vue-language-service/package.json +++ b/packages/vue-language-service/package.json @@ -25,7 +25,6 @@ "@volar/transforms": "0.39.5", "@volar/typescript-faster": "0.39.5", "@volar/typescript-language-service": "0.39.5", - "@volar/vue-code-gen": "0.39.5", "@volar/vue-language-core": "0.39.5", "@volar/vue-language-service-types": "0.39.5", "@volar/vue-typescript": "0.39.5", diff --git a/packages/vue-language-service/src/languageFeatures/definition.ts b/packages/vue-language-service/src/languageFeatures/definition.ts index 9da136739..2bea788f5 100644 --- a/packages/vue-language-service/src/languageFeatures/definition.ts +++ b/packages/vue-language-service/src/languageFeatures/definition.ts @@ -4,7 +4,7 @@ import * as shared from '@volar/shared'; import { languageFeatureWorker } from '../utils/featureWorkers'; import * as dedupe from '../utils/dedupe'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { EmbeddedFileMappingData, TeleportSideData } from '@volar/vue-code-gen'; +import { EmbeddedFileMappingData, TeleportSideData } from '@volar/vue-language-core'; export function register( context: LanguageServiceRuntimeContext, diff --git a/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts b/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts index d924b499a..e29c17841 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts @@ -1,6 +1,6 @@ import * as shared from '@volar/shared'; import * as ts2 from '@volar/typescript-language-service'; -import { parseDeclarationRanges, parseDotValueRanges } from '@volar/vue-code-gen/out/parsers/refSugarRanges'; +import { refSugarRanges } from '@volar/vue-language-core'; import * as vscode from 'vscode-languageserver-protocol'; import { mergeWorkspaceEdits } from '../languageFeatures/rename'; import { EmbeddedLanguageServicePlugin, ExecuteCommandContext, useConfigurationHost } from '@volar/vue-language-service-types'; @@ -138,8 +138,8 @@ async function useRefSugar( _scriptSetupAst: NonNullable, ) { - const ranges = parseDeclarationRanges(ts, _scriptSetupAst); - const dotValueRanges = parseDotValueRanges(ts, _scriptSetupAst); + const ranges = refSugarRanges.parseDeclarationRanges(ts, _scriptSetupAst); + const dotValueRanges = refSugarRanges.parseDotValueRanges(ts, _scriptSetupAst); const document = _vueDocument.getDocument(); const edits: vscode.TextEdit[] = []; diff --git a/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts b/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts index 0555f8f27..ce12b7bf3 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts @@ -1,6 +1,6 @@ import * as shared from '@volar/shared'; -import { parseUnuseScriptSetupRanges, parseUseScriptSetupRanges } from '@volar/vue-code-gen/out/parsers/scriptSetupConvertRanges'; -import type { TextRange } from '@volar/vue-code-gen/out/types'; +import { scriptSetupConvertRanges } from '@volar/vue-language-core'; +import type { TextRange } from '@volar/vue-language-core'; import * as vscode from 'vscode-languageserver-protocol'; import { EmbeddedLanguageServicePlugin, ExecuteCommandContext, useConfigurationHost } from '@volar/vue-language-service-types'; import { VueDocument } from '../vueDocuments'; @@ -142,7 +142,7 @@ async function useSetupSugar( _scriptAst: NonNullable, ) { - const ranges = parseUseScriptSetupRanges(ts, _scriptAst); + const ranges = scriptSetupConvertRanges.parseUseScriptSetupRanges(ts, _scriptAst); const document = _vueDocument.getDocument(); const edits: vscode.TextEdit[] = []; @@ -337,8 +337,8 @@ async function unuseSetupSugar( _scriptSetupAst: NonNullable, ) { - const ranges = parseUnuseScriptSetupRanges(ts, _scriptSetupAst); - const scriptRanges = _scriptAst ? parseUseScriptSetupRanges(ts, _scriptAst) : undefined; + const ranges = scriptSetupConvertRanges.parseUnuseScriptSetupRanges(ts, _scriptSetupAst); + const scriptRanges = _scriptAst ? scriptSetupConvertRanges.parseUseScriptSetupRanges(ts, _scriptAst) : undefined; const document = _vueDocument.getDocument(); const edits: vscode.TextEdit[] = []; diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 9f97a73f5..a92f92a3a 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -1,7 +1,7 @@ import * as shared from '@volar/shared'; import type * as ts2 from '@volar/typescript-language-service'; -import { isIntrinsicElement } from '@volar/vue-code-gen'; -import { parseScriptRanges } from '@volar/vue-code-gen/out/parsers/scriptRanges'; +import { isIntrinsicElement } from '@volar/vue-language-core'; +import { scriptRanges } from '@volar/vue-language-core'; import * as vue from '@volar/vue-language-core'; import { EmbeddedLanguageServicePlugin, useConfigurationHost } from '@volar/vue-language-service-types'; import { camelize, capitalize, hyphenate } from '@vue/shared'; @@ -345,8 +345,8 @@ export default function useVueTemplateLanguagePlugin + diff --git a/packages/vue-test-workspace/rename/component-tag/input/entry.vue b/packages/vue-test-workspace/rename/component-tag/input/entry.vue index 6f6cee59c..fc092f4a4 100644 --- a/packages/vue-test-workspace/rename/component-tag/input/entry.vue +++ b/packages/vue-test-workspace/rename/component-tag/input/entry.vue @@ -4,6 +4,6 @@ \ No newline at end of file diff --git a/packages/vue-test-workspace/rename/component-tag/output/component.vue b/packages/vue-test-workspace/rename/component-tag/output/component.vue new file mode 100644 index 000000000..bc692573e --- /dev/null +++ b/packages/vue-test-workspace/rename/component-tag/output/component.vue @@ -0,0 +1,2 @@ + diff --git a/packages/vue-test-workspace/rename/component-tag/output/entry.vue b/packages/vue-test-workspace/rename/component-tag/output/entry.vue index 9853a156e..1d104e413 100644 --- a/packages/vue-test-workspace/rename/component-tag/output/entry.vue +++ b/packages/vue-test-workspace/rename/component-tag/output/entry.vue @@ -4,6 +4,6 @@ \ No newline at end of file diff --git a/packages/vue-test-workspace/rename/regular-component/input/entry.vue b/packages/vue-test-workspace/rename/regular-component/input/entry.vue new file mode 100644 index 000000000..8d1922a7c --- /dev/null +++ b/packages/vue-test-workspace/rename/regular-component/input/entry.vue @@ -0,0 +1,18 @@ +import { defineComponent } from 'vue'; + + + \ No newline at end of file diff --git a/packages/vue-test-workspace/rename/regular-component/output/entry.vue b/packages/vue-test-workspace/rename/regular-component/output/entry.vue new file mode 100644 index 000000000..8542ea25a --- /dev/null +++ b/packages/vue-test-workspace/rename/regular-component/output/entry.vue @@ -0,0 +1,18 @@ +import { defineComponent } from 'vue'; + + + \ No newline at end of file diff --git a/packages/vue-test-workspace/vue-tsc/reference-type-in-template/main.vue b/packages/vue-test-workspace/vue-tsc/reference-type-in-template/main.vue new file mode 100644 index 000000000..31f81a51a --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc/reference-type-in-template/main.vue @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eedfbdf7d..0aaeb85fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,13 @@ importers: vitest: latest vue: ^3.2.37 devDependencies: - '@lerna-lite/cli': 1.9.1 - '@types/node': 18.6.4 + '@lerna-lite/cli': 1.10.0 + '@types/node': 18.6.5 '@vscode/test-web': 0.0.29 typescript: 4.7.4 - vite: 3.0.4 + vite: 3.0.5 vitepress: 1.0.0-alpha.4 - vitest: 0.21.0 + vitest: 0.21.1 vue: 3.2.37 extensions/vscode-alpine-language-features: @@ -37,7 +37,7 @@ importers: '@types/vscode': 1.67.0 '@volar/alpine-language-server': link:../../packages/alpine-language-server '@volar/shared': link:../../packages/shared - esbuild: 0.14.53 + esbuild: 0.14.54 path-browserify: 1.0.1 punycode: 2.1.1 vsce: 2.10.0 @@ -53,7 +53,7 @@ importers: dependencies: typescript-vue-plugin-forward: file:extensions/vscode-typescript-vue-plugin/typescript-vue-plugin-forward devDependencies: - esbuild: 0.14.53 + esbuild: 0.14.54 typescript-vue-plugin: link:../../packages/typescript-vue-plugin vsce: 2.10.0 @@ -84,8 +84,8 @@ importers: '@volar/vue-language-server': link:../../packages/vue-language-server '@vue/compiler-sfc': 3.2.37 '@vue/reactivity': 3.2.37 - esbuild: 0.14.53 - esbuild-plugin-copy: 1.3.0_esbuild@0.14.53 + esbuild: 0.14.54 + esbuild-plugin-copy: 1.3.0_esbuild@0.14.54 path-browserify: 1.0.1 punycode: 2.1.1 semver: 7.3.7 @@ -236,23 +236,6 @@ importers: devDependencies: typescript: 4.7.4 - packages/vue-code-gen: - specifiers: - '@volar/code-gen': 0.39.5 - '@volar/source-map': 0.39.5 - '@vue/compiler-core': ^3.2.37 - '@vue/compiler-dom': ^3.2.37 - '@vue/shared': ^3.2.37 - typescript: latest - dependencies: - '@volar/code-gen': link:../code-gen - '@volar/source-map': link:../source-map - '@vue/compiler-core': 3.2.37 - '@vue/compiler-dom': 3.2.37 - '@vue/shared': 3.2.37 - devDependencies: - typescript: 4.7.4 - packages/vue-component-meta: specifiers: '@volar/vue-language-core': 0.39.5 @@ -264,16 +247,20 @@ importers: '@volar/code-gen': 0.39.5 '@volar/pug-language-service': 0.39.5 '@volar/source-map': 0.39.5 - '@volar/vue-code-gen': 0.39.5 + '@vue/compiler-core': ^3.2.37 + '@vue/compiler-dom': ^3.2.37 '@vue/compiler-sfc': ^3.2.37 '@vue/reactivity': ^3.2.37 + '@vue/shared': ^3.2.37 typescript: latest dependencies: '@volar/code-gen': link:../code-gen '@volar/source-map': link:../source-map - '@volar/vue-code-gen': link:../vue-code-gen + '@vue/compiler-core': 3.2.37 + '@vue/compiler-dom': 3.2.37 '@vue/compiler-sfc': 3.2.37 '@vue/reactivity': 3.2.37 + '@vue/shared': 3.2.37 devDependencies: '@volar/pug-language-service': link:../pug-language-service typescript: 4.7.4 @@ -315,7 +302,6 @@ importers: '@volar/transforms': 0.39.5 '@volar/typescript-faster': 0.39.5 '@volar/typescript-language-service': 0.39.5 - '@volar/vue-code-gen': 0.39.5 '@volar/vue-language-core': 0.39.5 '@volar/vue-language-service-types': 0.39.5 '@volar/vue-typescript': 0.39.5 @@ -340,7 +326,6 @@ importers: '@volar/transforms': link:../transforms '@volar/typescript-faster': link:../typescript-faster '@volar/typescript-language-service': link:../typescript-language-service - '@volar/vue-code-gen': link:../vue-code-gen '@volar/vue-language-core': link:../vue-language-core '@volar/vue-language-service-types': link:../vue-language-service-types '@volar/vue-typescript': link:../vue-typescript @@ -586,8 +571,8 @@ packages: resolution: {integrity: sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==} dev: false - /@esbuild/linux-loong64/0.14.53: - resolution: {integrity: sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==} + /@esbuild/linux-loong64/0.14.54: + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -637,33 +622,35 @@ packages: - supports-color dev: true - /@lerna-lite/cli/1.9.1: - resolution: {integrity: sha512-xJvqgsZ43+oTf+cue9PHpn9a5eQKi2VZn15UYhuq31VzriFqC199LsUZWJrIw158GbDslwdIe3RUW73iBYQ7MA==} + /@lerna-lite/cli/1.10.0: + resolution: {integrity: sha512-wz71kSs62B5Dr5qe8/eVtQRTt3yygpSDqxW+MAq2QmmQdOGr2I9ES5xtPQccdG3lvedZpfTzaJ8JFkFMSTD8xA==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} hasBin: true dependencies: - '@lerna-lite/core': 1.9.1 - '@lerna-lite/info': 1.9.1 - '@lerna-lite/init': 1.9.1 - '@lerna-lite/listable': 1.9.1 - '@lerna-lite/publish': 1.9.1 - '@lerna-lite/version': 1.9.1 + '@lerna-lite/core': 1.10.0 + '@lerna-lite/info': 1.10.0 + '@lerna-lite/init': 1.10.0 + '@lerna-lite/listable': 1.10.0 + '@lerna-lite/publish': 1.10.0 + '@lerna-lite/version': 1.10.0 dedent: 0.7.0 dotenv: 16.0.1 import-local: 3.1.0 + load-json-file: 6.2.0 npmlog: 6.0.2 + path: 0.12.7 yargs: 17.5.1 transitivePeerDependencies: - encoding - supports-color dev: true - /@lerna-lite/core/1.9.1: - resolution: {integrity: sha512-q0K1FrvKyb17snLbYC3ftNaPogYAcUyFR2lYCVqWnAMHZ7JLXQ45xwaQdcrhe246W2I/kwTflL3YC7Ht/Lpw4w==} + /@lerna-lite/core/1.10.0: + resolution: {integrity: sha512-e5HxFO0v/l3Kd6nGQXV0kGNu1jMQqIPvqAjch+63Czw82D0+xZN8g7j4yssD9fPKcVC11cJOZIXftEVUv6mE3g==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: os: 0.1.2 - '@npmcli/run-script': 4.1.7 + '@npmcli/run-script': 4.2.0 '@octokit/plugin-enterprise-rest': 6.0.1 '@octokit/rest': 19.0.3 async: 3.2.4 @@ -672,6 +659,8 @@ packages: config-chain: 1.1.13 conventional-changelog-angular: 5.0.13 conventional-changelog-core: 4.2.4 + conventional-changelog-writer: 5.0.1 + conventional-commits-parser: 3.2.4 conventional-recommended-bump: 6.1.0 cosmiconfig: 7.0.1 dedent: 0.7.0 @@ -720,11 +709,11 @@ packages: - supports-color dev: true - /@lerna-lite/info/1.9.1: - resolution: {integrity: sha512-kAPaaBCtFExREA9sjNbtsF8sTnQyBVBZr1r8/3TK2tTQMyOkZdSFI2L3Q1KaOitmwIqgfqSNFdFvrhD8tNhaHw==} + /@lerna-lite/info/1.10.0: + resolution: {integrity: sha512-Wi3s+tY+iZaNG7XuL+Tlz5x7yfvr1CMDLD4oZCnyhO8u8e/htPd1EbpzQQ7nGHW9e+GjIsTNhuWK6ubdQ0qGgQ==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: - '@lerna-lite/core': 1.9.1 + '@lerna-lite/core': 1.10.0 dedent: 0.7.0 envinfo: 7.8.1 yargs: 17.5.1 @@ -733,11 +722,11 @@ packages: - supports-color dev: true - /@lerna-lite/init/1.9.1: - resolution: {integrity: sha512-3fDZZ6A+4qrLTKEwB7L4Iaqkb7LMi/I+nJEEbfG3iBirHuZ4CHA/rIkLtMLKO6ETf0Sd9455ct8560dtPhoHUw==} + /@lerna-lite/init/1.10.0: + resolution: {integrity: sha512-nvBVD4NyDPCR6LsEY1LGvv1wuOV9U2hY6hn+DT2iSuzdgY53Qdc4et7ekFqyLUpPaXmr3eweNmSST4x/jiMRvA==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: - '@lerna-lite/core': 1.9.1 + '@lerna-lite/core': 1.10.0 fs-extra: 10.1.0 p-map: 4.0.0 write-json-file: 4.3.0 @@ -746,11 +735,11 @@ packages: - supports-color dev: true - /@lerna-lite/listable/1.9.1: - resolution: {integrity: sha512-ljnBgaGs0qTv4QU9EHGjrCxn5B6Aa2uvsAWMbY9DsTUN5XdxeoffhBKBsb4QrulwPUFzbVVtqT3im51P/0S6+A==} + /@lerna-lite/listable/1.10.0: + resolution: {integrity: sha512-ov8Jxfqwqd5rRjPQNuDCvpwZSsHocxc63RxWciJJV30xSJVmbRBFNO7NSBAtyj/gzfBCybqrd57Znm5W3zArEg==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: - '@lerna-lite/core': 1.9.1 + '@lerna-lite/core': 1.10.0 chalk: 4.1.2 columnify: 1.6.0 transitivePeerDependencies: @@ -758,13 +747,13 @@ packages: - supports-color dev: true - /@lerna-lite/publish/1.9.1: - resolution: {integrity: sha512-3Qly/tIT9YdMdRsGE8ZWiMkAsoKvQzQoNKvgLPDMx7cKIsiPrvsj8vPlJyW6RwLSyK4LflK/Kx/LsQJS5iBmLg==} + /@lerna-lite/publish/1.10.0: + resolution: {integrity: sha512-AH2A9bA+3jvHcuIhVE/3qe6CuYeNj/ohbvbOAdjkdtZAUtz5UdxnHQZ50EQ9dBTMZPPFI8eVf4J2qiL6Y9ogtA==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: os: 0.1.2 - '@lerna-lite/core': 1.9.1 - '@lerna-lite/version': 1.9.1 + '@lerna-lite/core': 1.10.0 + '@lerna-lite/version': 1.10.0 byte-size: 7.0.1 columnify: 1.6.0 fs-extra: 10.1.0 @@ -797,12 +786,12 @@ packages: - supports-color dev: true - /@lerna-lite/version/1.9.1: - resolution: {integrity: sha512-8pm69m+WI0SvK1jbVQxp2gTM6tmkepGUhqCjTtznvImM7Uhwuf+CLArHGHn4qTwUXA2zfyLuCAzucJg3K46Bfw==} + /@lerna-lite/version/1.10.0: + resolution: {integrity: sha512-5rN9s9EDIKJVLFemHYqMDEobmWijeOkpO9QICUMh63bL22uSjyQs4ahPjeEKmT2N03s5IS3wf0jkWLeSdBYmFw==} engines: {node: '>=14.15.0', npm: '>=8.0.0'} dependencies: os: 0.1.2 - '@lerna-lite/core': 1.9.1 + '@lerna-lite/core': 1.10.0 chalk: 4.1.2 dedent: 0.7.0 load-json-file: 6.2.0 @@ -894,8 +883,8 @@ packages: infer-owner: 1.0.4 dev: true - /@npmcli/run-script/4.1.7: - resolution: {integrity: sha512-WXr/MyM4tpKA4BotB81NccGAv8B48lNH0gRoILucbcAhTQXLCoi6HflMV3KdXubIqvP9SuLsFn68Z7r4jl+ppw==} + /@npmcli/run-script/4.2.0: + resolution: {integrity: sha512-e/QgLg7j2wSJp1/7JRl0GC8c7PMX+uYlA/1Tb+IDOLdSM4T7K1VQ9mm9IGU3WRtY5vEIObpqCLb3aCNCug18DA==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: '@npmcli/node-gyp': 2.0.0 @@ -1035,11 +1024,11 @@ packages: /@types/chai-subset/1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@types/chai': 4.3.1 + '@types/chai': 4.3.3 dev: true - /@types/chai/4.3.1: - resolution: {integrity: sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==} + /@types/chai/4.3.3: + resolution: {integrity: sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==} dev: true /@types/minimist/1.2.2: @@ -1050,8 +1039,8 @@ packages: resolution: {integrity: sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==} dev: true - /@types/node/18.6.4: - resolution: {integrity: sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==} + /@types/node/18.6.5: + resolution: {integrity: sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==} dev: true /@types/normalize-package-data/2.4.1: @@ -2244,8 +2233,8 @@ packages: is-arrayish: 0.2.1 dev: true - /esbuild-android-64/0.14.53: - resolution: {integrity: sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==} + /esbuild-android-64/0.14.54: + resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -2253,8 +2242,8 @@ packages: dev: true optional: true - /esbuild-android-arm64/0.14.53: - resolution: {integrity: sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==} + /esbuild-android-arm64/0.14.54: + resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -2262,8 +2251,8 @@ packages: dev: true optional: true - /esbuild-darwin-64/0.14.53: - resolution: {integrity: sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==} + /esbuild-darwin-64/0.14.54: + resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -2271,8 +2260,8 @@ packages: dev: true optional: true - /esbuild-darwin-arm64/0.14.53: - resolution: {integrity: sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==} + /esbuild-darwin-arm64/0.14.54: + resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -2280,8 +2269,8 @@ packages: dev: true optional: true - /esbuild-freebsd-64/0.14.53: - resolution: {integrity: sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==} + /esbuild-freebsd-64/0.14.54: + resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -2289,8 +2278,8 @@ packages: dev: true optional: true - /esbuild-freebsd-arm64/0.14.53: - resolution: {integrity: sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==} + /esbuild-freebsd-arm64/0.14.54: + resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -2298,8 +2287,8 @@ packages: dev: true optional: true - /esbuild-linux-32/0.14.53: - resolution: {integrity: sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==} + /esbuild-linux-32/0.14.54: + resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -2307,8 +2296,8 @@ packages: dev: true optional: true - /esbuild-linux-64/0.14.53: - resolution: {integrity: sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==} + /esbuild-linux-64/0.14.54: + resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -2316,8 +2305,8 @@ packages: dev: true optional: true - /esbuild-linux-arm/0.14.53: - resolution: {integrity: sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==} + /esbuild-linux-arm/0.14.54: + resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -2325,8 +2314,8 @@ packages: dev: true optional: true - /esbuild-linux-arm64/0.14.53: - resolution: {integrity: sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==} + /esbuild-linux-arm64/0.14.54: + resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -2334,8 +2323,8 @@ packages: dev: true optional: true - /esbuild-linux-mips64le/0.14.53: - resolution: {integrity: sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==} + /esbuild-linux-mips64le/0.14.54: + resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -2343,8 +2332,8 @@ packages: dev: true optional: true - /esbuild-linux-ppc64le/0.14.53: - resolution: {integrity: sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==} + /esbuild-linux-ppc64le/0.14.54: + resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -2352,8 +2341,8 @@ packages: dev: true optional: true - /esbuild-linux-riscv64/0.14.53: - resolution: {integrity: sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==} + /esbuild-linux-riscv64/0.14.54: + resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -2361,8 +2350,8 @@ packages: dev: true optional: true - /esbuild-linux-s390x/0.14.53: - resolution: {integrity: sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==} + /esbuild-linux-s390x/0.14.54: + resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -2370,8 +2359,8 @@ packages: dev: true optional: true - /esbuild-netbsd-64/0.14.53: - resolution: {integrity: sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==} + /esbuild-netbsd-64/0.14.54: + resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -2379,8 +2368,8 @@ packages: dev: true optional: true - /esbuild-openbsd-64/0.14.53: - resolution: {integrity: sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==} + /esbuild-openbsd-64/0.14.54: + resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -2388,19 +2377,19 @@ packages: dev: true optional: true - /esbuild-plugin-copy/1.3.0_esbuild@0.14.53: + /esbuild-plugin-copy/1.3.0_esbuild@0.14.54: resolution: {integrity: sha512-LOx1xJOlAaCFMRtokHjsJfEkrosy3RDRa8SUHmn7loo0gwrouBQQwLAmOyMECshf7gSR1cPSRtAHu3KF/kQsyw==} peerDependencies: esbuild: ^0.14.0 dependencies: chalk: 4.1.2 - esbuild: 0.14.53 + esbuild: 0.14.54 fs-extra: 10.1.0 globby: 11.1.0 dev: true - /esbuild-sunos-64/0.14.53: - resolution: {integrity: sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==} + /esbuild-sunos-64/0.14.54: + resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -2408,8 +2397,8 @@ packages: dev: true optional: true - /esbuild-windows-32/0.14.53: - resolution: {integrity: sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==} + /esbuild-windows-32/0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -2417,8 +2406,8 @@ packages: dev: true optional: true - /esbuild-windows-64/0.14.53: - resolution: {integrity: sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==} + /esbuild-windows-64/0.14.54: + resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -2426,8 +2415,8 @@ packages: dev: true optional: true - /esbuild-windows-arm64/0.14.53: - resolution: {integrity: sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==} + /esbuild-windows-arm64/0.14.54: + resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -2435,33 +2424,33 @@ packages: dev: true optional: true - /esbuild/0.14.53: - resolution: {integrity: sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==} + /esbuild/0.14.54: + resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/linux-loong64': 0.14.53 - esbuild-android-64: 0.14.53 - esbuild-android-arm64: 0.14.53 - esbuild-darwin-64: 0.14.53 - esbuild-darwin-arm64: 0.14.53 - esbuild-freebsd-64: 0.14.53 - esbuild-freebsd-arm64: 0.14.53 - esbuild-linux-32: 0.14.53 - esbuild-linux-64: 0.14.53 - esbuild-linux-arm: 0.14.53 - esbuild-linux-arm64: 0.14.53 - esbuild-linux-mips64le: 0.14.53 - esbuild-linux-ppc64le: 0.14.53 - esbuild-linux-riscv64: 0.14.53 - esbuild-linux-s390x: 0.14.53 - esbuild-netbsd-64: 0.14.53 - esbuild-openbsd-64: 0.14.53 - esbuild-sunos-64: 0.14.53 - esbuild-windows-32: 0.14.53 - esbuild-windows-64: 0.14.53 - esbuild-windows-arm64: 0.14.53 + '@esbuild/linux-loong64': 0.14.54 + esbuild-android-64: 0.14.54 + esbuild-android-arm64: 0.14.54 + esbuild-darwin-64: 0.14.54 + esbuild-darwin-arm64: 0.14.54 + esbuild-freebsd-64: 0.14.54 + esbuild-freebsd-arm64: 0.14.54 + esbuild-linux-32: 0.14.54 + esbuild-linux-64: 0.14.54 + esbuild-linux-arm: 0.14.54 + esbuild-linux-arm64: 0.14.54 + esbuild-linux-mips64le: 0.14.54 + esbuild-linux-ppc64le: 0.14.54 + esbuild-linux-riscv64: 0.14.54 + esbuild-linux-s390x: 0.14.54 + esbuild-netbsd-64: 0.14.54 + esbuild-openbsd-64: 0.14.54 + esbuild-sunos-64: 0.14.54 + esbuild-windows-32: 0.14.54 + esbuild-windows-64: 0.14.54 + esbuild-windows-arm64: 0.14.54 dev: true /escalade/3.1.1: @@ -4064,7 +4053,7 @@ packages: '@npmcli/git': 3.0.1 '@npmcli/installed-package-contents': 1.0.7 '@npmcli/promise-spawn': 3.0.0 - '@npmcli/run-script': 4.1.7 + '@npmcli/run-script': 4.2.0 cacache: 16.1.1 chownr: 2.0.0 fs-minipass: 2.1.0 @@ -4278,6 +4267,15 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss/8.4.16: + resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /preact/10.10.0: resolution: {integrity: sha512-fszkg1iJJjq68I4lI8ZsmBiaoQiQHbxf1lNq+72EmC/mZOsFF5zn3k1yv9QGoFgIXzgsdSKtYymLJsrJPoamjQ==} dev: true @@ -5271,16 +5269,16 @@ packages: stylus: optional: true dependencies: - esbuild: 0.14.53 - postcss: 8.4.14 + esbuild: 0.14.54 + postcss: 8.4.16 resolve: 1.22.1 rollup: 2.77.0 optionalDependencies: fsevents: 2.3.2 dev: true - /vite/3.0.4: - resolution: {integrity: sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==} + /vite/3.0.5: + resolution: {integrity: sha512-bRvrt9Tw8EGW4jj64aYFTnVg134E8hgDxyl/eEHnxiGqYk7/pTPss6CWlurqPOUzqvEoZkZ58Ws+Iu8MB87iMA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -5298,8 +5296,8 @@ packages: terser: optional: true dependencies: - esbuild: 0.14.53 - postcss: 8.4.14 + esbuild: 0.14.54 + postcss: 8.4.16 resolve: 1.22.1 rollup: 2.77.0 optionalDependencies: @@ -5331,8 +5329,8 @@ packages: - stylus dev: true - /vitest/0.21.0: - resolution: {integrity: sha512-+BQB2swk4wQdw5loOoL8esIYh/1ifAliuwj2HWHNE2F8SAl/jF7/aoCJBoXGSf/Ws19k3pH4NrWeVtcSwM0j2w==} + /vitest/0.21.1: + resolution: {integrity: sha512-WBIxuFmIDPuK47GO6Lu9eNeRMqHj/FWL3dk73OHH3eyPPWPiu+UB3QHLkLK2PEggCqJW4FaWoWg8R68S7p9+9Q==} engines: {node: '>=v14.16.0'} hasBin: true peerDependencies: @@ -5356,15 +5354,15 @@ packages: jsdom: optional: true dependencies: - '@types/chai': 4.3.1 + '@types/chai': 4.3.3 '@types/chai-subset': 1.3.3 - '@types/node': 18.6.4 + '@types/node': 18.6.5 chai: 4.3.6 debug: 4.3.4 local-pkg: 0.4.2 tinypool: 0.2.4 tinyspy: 1.0.0 - vite: 3.0.4 + vite: 3.0.5 transitivePeerDependencies: - less - sass From 75431943c3055245fe6be8922a449b12d70481ac Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 04:50:22 +0800 Subject: [PATCH 2/9] feat: add test for template context property auto-complete (todo) --- .../complete/script-setup/input/entry.vue | 8 ++++++++ .../complete/script-setup/output/entry.vue | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 packages/vue-test-workspace/complete/script-setup/input/entry.vue create mode 100644 packages/vue-test-workspace/complete/script-setup/output/entry.vue diff --git a/packages/vue-test-workspace/complete/script-setup/input/entry.vue b/packages/vue-test-workspace/complete/script-setup/input/entry.vue new file mode 100644 index 000000000..a129ff9de --- /dev/null +++ b/packages/vue-test-workspace/complete/script-setup/input/entry.vue @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/packages/vue-test-workspace/complete/script-setup/output/entry.vue b/packages/vue-test-workspace/complete/script-setup/output/entry.vue new file mode 100644 index 000000000..279f132f5 --- /dev/null +++ b/packages/vue-test-workspace/complete/script-setup/output/entry.vue @@ -0,0 +1,8 @@ + + + \ No newline at end of file From 14540d7d09f879e9830862f7202f884670bb5883 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 06:37:07 +0800 Subject: [PATCH 3/9] refactor: compileTemplateToHtml -> compileSFCTemplate --- packages/alpine-language-core/src/index.ts | 2 +- .../src/plugins/file-html.ts | 12 +- packages/typescript-vue-plugin/src/index.ts | 4 +- packages/vue-component-meta/src/index.ts | 2 +- .../src/generators/template.ts | 62 ++-------- packages/vue-language-core/src/index.ts | 2 +- .../vue-language-core/src/plugins/empty.ts | 5 +- .../src/plugins/file-html.ts | 5 +- .../vue-language-core/src/plugins/file-md.ts | 5 +- .../vue-language-core/src/plugins/file-vue.ts | 5 +- .../src/plugins/vue-sfc-customblocks.ts | 3 +- .../src/plugins/vue-sfc-scripts.ts | 3 +- .../src/plugins/vue-sfc-styles.ts | 3 +- .../src/plugins/vue-sfc-template.ts | 3 +- .../src/plugins/vue-template-html.ts | 18 +-- .../src/plugins/vue-template-pug.ts | 51 +++++--- .../src/plugins/vue-typescript-scripts.ts | 10 +- packages/vue-language-core/src/sourceFile.ts | 98 ++++++++------- packages/vue-language-core/src/types.ts | 26 ++-- .../src/utils/compileSFCTemplate.ts | 116 ------------------ packages/vue-language-core/src/utils/ts.ts | 22 +++- packages/vue-language-server/src/project.ts | 5 +- .../src/plugins/vue-template.ts | 14 +-- .../vue-language-service/src/vueDocuments.ts | 16 +-- packages/vue-tsc/src/proxy.ts | 2 +- 25 files changed, 192 insertions(+), 302 deletions(-) delete mode 100644 packages/vue-language-core/src/utils/compileSFCTemplate.ts diff --git a/packages/alpine-language-core/src/index.ts b/packages/alpine-language-core/src/index.ts index 7a05fbc3e..0316722b9 100644 --- a/packages/alpine-language-core/src/index.ts +++ b/packages/alpine-language-core/src/index.ts @@ -4,5 +4,5 @@ import useHtmlFilePlugin from './plugins/file-html'; export type LanguageServiceHost = vue.LanguageServiceHost; export function createLanguageContext(host: vue.LanguageServiceHost): vue.LanguageContext { - return vue.createLanguageContext(host, [useHtmlFilePlugin()], ['.html']); + return vue.createLanguageContext(host, [useHtmlFilePlugin], ['.html']); } diff --git a/packages/alpine-language-core/src/plugins/file-html.ts b/packages/alpine-language-core/src/plugins/file-html.ts index 3e9bfc504..fb8425777 100644 --- a/packages/alpine-language-core/src/plugins/file-html.ts +++ b/packages/alpine-language-core/src/plugins/file-html.ts @@ -1,13 +1,12 @@ import { VueLanguagePlugin } from '@volar/vue-language-core'; import useVueHtmlFilePlugin from '@volar/vue-language-core/out/plugins/file-html'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = (ctx) => { - const vueHtmlFilePlugin = useVueHtmlFilePlugin(); + const vueHtmlFilePlugin = useVueHtmlFilePlugin(ctx); return { - - parseSfc(fileName, content) { + parseSFC(fileName, content) { if (fileName.endsWith('.html')) { @@ -29,8 +28,9 @@ export default function (): VueLanguagePlugin { } } - return vueHtmlFilePlugin.parseSfc?.(fileName, newContent); + return vueHtmlFilePlugin.parseSFC?.(fileName, newContent); }; } }; -} +}; +export default plugin; diff --git a/packages/typescript-vue-plugin/src/index.ts b/packages/typescript-vue-plugin/src/index.ts index 153cb71ce..533905adb 100644 --- a/packages/typescript-vue-plugin/src/index.ts +++ b/packages/typescript-vue-plugin/src/index.ts @@ -124,11 +124,11 @@ function createProxyHost(ts: typeof import('typescript/lib/tsserverlibrary'), in ? info.serverHost.watchFile(projectName, () => { onConfigUpdated(); onProjectUpdated(); - parsedCommandLine = vue.tsShared.createParsedCommandLine(ts, ts.sys, projectName); + parsedCommandLine = vue.createParsedCommandLine(ts, ts.sys, projectName); }) : undefined; let parsedCommandLine = tsconfigWatcher // reuse fileExists result - ? vue.tsShared.createParsedCommandLine(ts, ts.sys, projectName) + ? vue.createParsedCommandLine(ts, ts.sys, projectName) : undefined; return { diff --git a/packages/vue-component-meta/src/index.ts b/packages/vue-component-meta/src/index.ts index f284261f9..720952ca5 100644 --- a/packages/vue-component-meta/src/index.ts +++ b/packages/vue-component-meta/src/index.ts @@ -24,7 +24,7 @@ export type { }; export function createComponentMetaChecker(tsconfigPath: string, checkerOptions: MetaCheckerOptions = {}) { - const parsedCommandLine = vue.tsShared.createParsedCommandLine(ts, { + const parsedCommandLine = vue.createParsedCommandLine(ts, { useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames, readDirectory: (path, extensions, exclude, include, depth) => { return ts.sys.readDirectory(path, [...extensions, '.vue'], exclude, include, depth); diff --git a/packages/vue-language-core/src/generators/template.ts b/packages/vue-language-core/src/generators/template.ts index 7a35a0ba1..89f5d293f 100644 --- a/packages/vue-language-core/src/generators/template.ts +++ b/packages/vue-language-core/src/generators/template.ts @@ -69,7 +69,6 @@ export function generate( templateAst: CompilerDOM.RootNode, hasScriptSetup: boolean, cssScopedClasses: string[] = [], - htmlToTemplate: (htmlRange: { start: number, end: number; }) => { start: number, end: number; } | undefined, searchTexts: { getEmitCompletion(tag: string): string, getPropsCompletion(tag: string): string, @@ -219,7 +218,7 @@ export function generate( tagResolves[tagName] = { component: var_componentVar, emit: var_emit, - offsets: tagOffsets.map(offset => htmlToTemplate({ start: offset, end: offset })?.start).filter(notEmpty), + offsets: tagOffsets, }; } @@ -542,7 +541,7 @@ export function generate( else { startTagEnd = node.loc.start.offset + node.loc.source.substring(0, node.loc.source.lastIndexOf('') + 1; } - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange: { start: node.loc.start.offset, end: startTagEnd, @@ -982,7 +981,7 @@ export function generate( tsCodeGen.addText('undefined'); } writePropValueSuffix(isStatic); - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange: { start: prop.loc.start.offset, end: prop.loc.end.offset, @@ -1079,7 +1078,7 @@ export function generate( writePropValueSuffix(true); writePropEnd(true); const diagEnd = tsCodeGen.getText().length; - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange: { start: prop.loc.start.offset, end: prop.loc.end.offset, @@ -1277,12 +1276,6 @@ export function generate( end: prop.arg.loc.start.offset + end, }; - const newStart = htmlToTemplate({ start: sourceRange.start, end: sourceRange.end })?.start; - if (newStart === undefined) continue; - const offset = newStart - sourceRange.start; - sourceRange.start += offset; - sourceRange.end += offset; - cssCodeGen.addText(`${node.tag} { `); cssCodeGen.addCode( content, @@ -1403,7 +1396,7 @@ export function generate( tsCodeGen.addText(`]`); } const diagEnd = tsCodeGen.getText().length; - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ mappedRange: { start: diagStart, end: diagEnd, @@ -1491,7 +1484,7 @@ export function generate( ); } tsCodeGen.addText(`)`); - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange: { start: prop.loc.start.offset, end: prop.loc.end.offset, @@ -1756,7 +1749,7 @@ export function generate( for (let i = 1; i < sourceRanges.length; i++) { const sourceRange = sourceRanges[i]; if (mode === 1 || mode === 2) { - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange, mappedRange: { start: tsCodeGen.getText().length - mapCode.length, @@ -1767,7 +1760,7 @@ export function generate( }); } else if (mode === 3) { - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange, mappedRange: { start: tsCodeGen.getText().length - `['${mapCode}']`.length, @@ -1814,7 +1807,7 @@ export function generate( function writeCodeWithQuotes(mapCode: string, sourceRanges: SourceMaps.Range | SourceMaps.Range[], data: EmbeddedFileMappingData) { const addText = `'${mapCode}'`; for (const sourceRange of 'length' in sourceRanges ? sourceRanges : [sourceRanges]) { - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange, mappedRange: { start: tsCodeGen.getText().length + 1, @@ -1888,7 +1881,7 @@ export function generate( function writeFormatCode(mapCode: string, sourceOffset: number, formatWrapper: [string, string]) { tsFormatCodeGen.addText(formatWrapper[0]); const targetRange = tsFormatCodeGen.addText(mapCode); - addMapping(tsFormatCodeGen, { + tsFormatCodeGen.addMapping2({ mappedRange: targetRange, sourceRange: { start: sourceOffset, @@ -1905,42 +1898,13 @@ export function generate( } function writeCode(mapCode: string, sourceRange: SourceMaps.Range, mode: SourceMaps.Mode, data: EmbeddedFileMappingData) { const targetRange = tsCodeGen.addText(mapCode); - addMapping(tsCodeGen, { + tsCodeGen.addMapping2({ sourceRange, mappedRange: targetRange, mode, data, }); } - function addMapping(gen: typeof tsCodeGen, mapping: SourceMaps.Mapping) { - const newMapping = { ...mapping }; - - const templateStart = htmlToTemplate(mapping.sourceRange)?.start; - if (templateStart === undefined) return; // not found - const offset = templateStart - mapping.sourceRange.start; - newMapping.sourceRange = { - start: mapping.sourceRange.start + offset, - end: mapping.sourceRange.end + offset, - }; - - if (mapping.additional) { - newMapping.additional = []; - for (const other of mapping.additional) { - let otherTemplateStart = htmlToTemplate(other.sourceRange)?.start; - if (otherTemplateStart === undefined) continue; - const otherOffset = otherTemplateStart - other.sourceRange.start; - newMapping.additional.push({ - ...other, - sourceRange: { - start: other.sourceRange.start + otherOffset, - end: other.sourceRange.end + otherOffset, - }, - }); - } - } - - gen.addMapping2(newMapping); - } }; export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode, cb: (node: CompilerDOM.ElementNode) => void) { @@ -2038,7 +2002,3 @@ export function getPatchForSlotNode(node: CompilerDOM.ElementNode) { } } } - -function notEmpty(value: T | null | undefined): value is T { - return value !== null && value !== undefined; -} diff --git a/packages/vue-language-core/src/index.ts b/packages/vue-language-core/src/index.ts index de156c9f0..27d75c3bf 100644 --- a/packages/vue-language-core/src/index.ts +++ b/packages/vue-language-core/src/index.ts @@ -9,5 +9,5 @@ export * as scriptRanges from './parsers/scriptRanges'; export * as scriptSetupConvertRanges from './parsers/scriptSetupConvertRanges'; export * as scriptSetupRanges from './parsers/scriptSetupRanges'; export * as localTypes from './utils/localTypes'; -export * as tsShared from './utils/ts'; +export * from './utils/ts'; export * from './generators/template'; diff --git a/packages/vue-language-core/src/plugins/empty.ts b/packages/vue-language-core/src/plugins/empty.ts index 9018f32d4..7a00935b3 100644 --- a/packages/vue-language-core/src/plugins/empty.ts +++ b/packages/vue-language-core/src/plugins/empty.ts @@ -1,5 +1,6 @@ import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return {}; -} +}; +export default plugin; diff --git a/packages/vue-language-core/src/plugins/file-html.ts b/packages/vue-language-core/src/plugins/file-html.ts index edbc8482a..02f55a63a 100644 --- a/packages/vue-language-core/src/plugins/file-html.ts +++ b/packages/vue-language-core/src/plugins/file-html.ts @@ -3,11 +3,11 @@ import { SourceMapBase } from '@volar/source-map'; import { parse, SFCBlock } from '@vue/compiler-sfc'; import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { - parseSfc(fileName, content) { + parseSFC(fileName, content) { if (fileName.endsWith('.html')) { @@ -86,3 +86,4 @@ export default function (): VueLanguagePlugin { } }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/file-md.ts b/packages/vue-language-core/src/plugins/file-md.ts index b27827eab..25f850658 100644 --- a/packages/vue-language-core/src/plugins/file-md.ts +++ b/packages/vue-language-core/src/plugins/file-md.ts @@ -3,11 +3,11 @@ import { Mode, SourceMapBase } from '@volar/source-map'; import { VueLanguagePlugin } from '../sourceFile'; import { parse, SFCBlock } from '@vue/compiler-sfc'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { - parseSfc(fileName, content) { + parseSFC(fileName, content) { if (fileName.endsWith('.md')) { @@ -85,3 +85,4 @@ export default function (): VueLanguagePlugin { } }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/file-vue.ts b/packages/vue-language-core/src/plugins/file-vue.ts index 55318fac5..e502f3ac2 100644 --- a/packages/vue-language-core/src/plugins/file-vue.ts +++ b/packages/vue-language-core/src/plugins/file-vue.ts @@ -1,11 +1,11 @@ import { VueLanguagePlugin } from '../sourceFile'; import { parse } from '@vue/compiler-sfc'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { - parseSfc(fileName, content) { + parseSFC(fileName, content) { if (fileName.endsWith('.vue')) { @@ -14,3 +14,4 @@ export default function (): VueLanguagePlugin { } }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-sfc-customblocks.ts b/packages/vue-language-core/src/plugins/vue-sfc-customblocks.ts index 7a403244f..6d51cb386 100644 --- a/packages/vue-language-core/src/plugins/vue-sfc-customblocks.ts +++ b/packages/vue-language-core/src/plugins/vue-sfc-customblocks.ts @@ -1,7 +1,7 @@ import * as SourceMaps from '@volar/source-map'; import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { @@ -54,3 +54,4 @@ export default function (): VueLanguagePlugin { }, }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-sfc-scripts.ts b/packages/vue-language-core/src/plugins/vue-sfc-scripts.ts index 76cf77712..d1e867990 100644 --- a/packages/vue-language-core/src/plugins/vue-sfc-scripts.ts +++ b/packages/vue-language-core/src/plugins/vue-sfc-scripts.ts @@ -1,7 +1,7 @@ import * as SourceMaps from '@volar/source-map'; import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { @@ -46,3 +46,4 @@ export default function (): VueLanguagePlugin { }, }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-sfc-styles.ts b/packages/vue-language-core/src/plugins/vue-sfc-styles.ts index a1edcdcce..438108562 100644 --- a/packages/vue-language-core/src/plugins/vue-sfc-styles.ts +++ b/packages/vue-language-core/src/plugins/vue-sfc-styles.ts @@ -1,7 +1,7 @@ import * as SourceMaps from '@volar/source-map'; import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { @@ -54,3 +54,4 @@ export default function (): VueLanguagePlugin { }, }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-sfc-template.ts b/packages/vue-language-core/src/plugins/vue-sfc-template.ts index 41ef7d5fd..69932488c 100644 --- a/packages/vue-language-core/src/plugins/vue-sfc-template.ts +++ b/packages/vue-language-core/src/plugins/vue-sfc-template.ts @@ -1,7 +1,7 @@ import * as SourceMaps from '@volar/source-map'; import { VueLanguagePlugin } from '../sourceFile'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = () => { return { @@ -48,3 +48,4 @@ export default function (): VueLanguagePlugin { }, }; } +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-template-html.ts b/packages/vue-language-core/src/plugins/vue-template-html.ts index ef2f48133..a0b157817 100644 --- a/packages/vue-language-core/src/plugins/vue-template-html.ts +++ b/packages/vue-language-core/src/plugins/vue-template-html.ts @@ -1,18 +1,20 @@ import { VueLanguagePlugin } from '../sourceFile'; +import * as CompilerDom from '@vue/compiler-dom'; +import * as CompilerVue2 from '../utils/vue2TemplateCompiler'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = ({ vueCompilerOptions }) => { return { - compileTemplateToHtml(lang, template) { + compileSFCTemplate(lang, template, options) { if (lang === 'html') { - return { - html: template, - mapping: htmlRange => htmlRange, - }; + const compiler = vueCompilerOptions.target < 3 ? CompilerVue2 : CompilerDom; + + return compiler.compile(template, options); } - } + }, }; -} +}; +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-template-pug.ts b/packages/vue-language-core/src/plugins/vue-template-pug.ts index 8f616d01f..eab695468 100644 --- a/packages/vue-language-core/src/plugins/vue-template-pug.ts +++ b/packages/vue-language-core/src/plugins/vue-template-pug.ts @@ -1,10 +1,12 @@ import { VueLanguagePlugin } from '../sourceFile'; +import * as CompilerDom from '@vue/compiler-dom'; +import * as CompilerVue2 from '../utils/vue2TemplateCompiler'; -export default function (): VueLanguagePlugin { +const plugin: VueLanguagePlugin = ({ vueCompilerOptions }) => { return { - compileTemplateToHtml(lang, template) { + compileSFCTemplate(lang, template, options) { if (lang === 'pug') { @@ -17,26 +19,39 @@ export default function (): VueLanguagePlugin { const pugDoc = pug?.baseParse(template); if (pugDoc) { - return { - html: pugDoc.htmlCode, - mapping: htmlRange => { - const pugRange = pugDoc.sourceMap.getSourceRange(htmlRange.start, htmlRange.end, data => !data?.isEmptyTagCompletion)?.[0]; - if (pugRange) { - return pugRange; - } - else { - const pugStart = pugDoc.sourceMap.getSourceRange(htmlRange.start, htmlRange.start, data => !data?.isEmptyTagCompletion)?.[0]?.start; - const pugEnd = pugDoc.sourceMap.getSourceRange(htmlRange.end, htmlRange.end, data => !data?.isEmptyTagCompletion)?.[0]?.end; + const compiler = vueCompilerOptions.target < 3 ? CompilerVue2 : CompilerDom; + const completed = compiler.compile(pugDoc.htmlCode, { + ...options, + onWarn(warning) { + options?.onWarn?.(createProxyObject(warning)); + }, + onError(error) { + options?.onError?.(createProxyObject(error)); + }, + }); + + return createProxyObject(completed); - if (pugStart !== undefined && pugEnd !== undefined) { - return { start: pugStart, end: pugEnd }; + function createProxyObject(target: any): any { + return new Proxy(target, { + get(target, prop) { + if (prop === 'offset') { + const htmlOffset = target.offset; + const pugOffset = pugDoc!.sourceMap.getSourceRange(htmlOffset, htmlOffset, data => !data?.isEmptyTagCompletion)?.[0]?.start; + return pugOffset ?? -1; + } + const value = target[prop]; + if (typeof value === 'object') { + return createProxyObject(target[prop]); } + return value; } - }, - }; + }); + } } } - } + }, }; -} +}; +export default plugin; diff --git a/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts b/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts index 7b69e7803..c4c611f4b 100644 --- a/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts +++ b/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts @@ -1,8 +1,8 @@ +import { Ref } from '@vue/reactivity'; import { generate as genScript } from '../generators/script'; import type * as templateGen from '../generators/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import { Ref } from '@vue/reactivity'; import { useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; import { VueCompilerOptions } from '../types'; @@ -16,9 +16,9 @@ export default function ( cssModuleClasses: ReturnType, cssScopedClasses: ReturnType, disableTemplateScript: boolean, -): VueLanguagePlugin { +) { - return { + const plugin: VueLanguagePlugin = () => ({ getEmbeddedFileNames(fileName, sfc) { @@ -102,5 +102,7 @@ export default function ( } } }, - }; + }); + + return plugin; } diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index 757922032..68def92bc 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -1,10 +1,9 @@ -import { EmbeddedFileMappingData, TeleportMappingData, TextRange } from './types'; +import { EmbeddedFileMappingData, TeleportMappingData, TextRange, VueCompilerOptions, _VueCompilerOptions } from './types'; import { parseRefSugarCallRanges, parseRefSugarDeclarationRanges } from './parsers/refSugarRanges'; import { parseScriptRanges } from './parsers/scriptRanges'; import { parseScriptSetupRanges } from './parsers/scriptSetupRanges'; import { SFCBlock, SFCParseResult, SFCScriptBlock, SFCStyleBlock, SFCTemplateBlock } from '@vue/compiler-sfc'; import { computed, ComputedRef, reactive, ref, unref } from '@vue/reactivity'; -import { VueCompilerOptions } from './types'; import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; import { SearchTexts } from './utils/string'; import * as templateGen from './generators/template'; @@ -22,26 +21,26 @@ import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; import useVueSfcTemplate from './plugins/vue-sfc-template'; import useVueTsScripts from './plugins/vue-typescript-scripts'; -import type * as _0 from 'typescript/lib/tsserverlibrary'; // fix TS2742 +import type * as ts from 'typescript/lib/tsserverlibrary'; // fix TS2742 import { Mapping, MappingBase } from '@volar/source-map'; import { CodeGen } from '@volar/code-gen'; -import { compileSFCTemplate } from './utils/compileSFCTemplate'; +import * as CompilerDom from '@vue/compiler-dom'; +import { getVueCompilerOptions } from './utils/ts'; -export interface VueLanguagePlugin { - - parseSfc?(fileName: string, content: string): SFCParseResult | undefined; +export type VueLanguagePlugin = (ctx: { + ts: typeof ts, + compilerOptions: ts.CompilerOptions, + vueCompilerOptions: _VueCompilerOptions, +}) => { - compileTemplateToHtml?(lang: string, tmplate: string): { - html: string, - mapping(htmlRange: { start: number, end: number; }): { start: number, end: number; } | undefined, - } | undefined; + parseSFC?(fileName: string, content: string): SFCParseResult | undefined; - // TODO: compileHtmlTemplateToAst + compileSFCTemplate?(lang: string, template: string, options?: CompilerDom.CompilerOptions): CompilerDom.CodegenResult | undefined; getEmbeddedFileNames?(fileName: string, sfc: Sfc): string[]; resolveEmbeddedFile?(fileName: string, sfc: Sfc, embeddedFile: EmbeddedFile): void; -} +}; export interface SourceFile extends ReturnType { } @@ -119,31 +118,44 @@ export function createSourceFile( // use const parsedSfc = computed(() => { for (const plugin of plugins) { - const sfc = plugin.parseSfc?.(fileName, fileContent.value); + const sfc = plugin.parseSFC?.(fileName, fileContent.value); if (sfc) { return sfc; } } }); - const computedHtmlTemplate = computed>>(() => { + const templateAstCompiled = computed(() => { if (sfc.template) { for (const plugin of plugins) { - const compiledHtml = plugin.compileTemplateToHtml?.(sfc.template.lang, sfc.template.content); - if (compiledHtml) { - return compiledHtml; - }; + + const errors: CompilerDom.CompilerError[] = []; + const warnings: CompilerDom.CompilerError[] = []; + let ast: CompilerDom.RootNode | undefined; + + try { + ast = plugin.compileSFCTemplate?.(sfc.template.lang, sfc.template.content, { + onError: (err: CompilerDom.CompilerError) => errors.push(err), + onWarn: (err: CompilerDom.CompilerError) => warnings.push(err), + expressionPlugins: ['typescript'], + ...vueCompilerOptions.experimentalTemplateCompilerOptions, + })?.ast; + } + catch (e) { + const err = e as CompilerDom.CompilerError; + errors.push(err); + } + + + if (ast || errors.length) { + return { + errors, + warnings, + ast, + }; + } } } }); - const templateAstCompiled = computed(() => { - if (computedHtmlTemplate.value) { - return compileSFCTemplate( - computedHtmlTemplate.value.html, - vueCompilerOptions.experimentalTemplateCompilerOptions, - vueCompilerOptions.target ?? 3, - ); - } - }); const cssModuleClasses = useStyleCssClasses(sfc, style => !!style.module); const cssScopedClasses = useStyleCssClasses(sfc, style => { const setting = compilerOptions.experimentalResolveStyleCssClasses ?? 'scoped'; @@ -151,8 +163,6 @@ export function createSourceFile( }); const templateCodeGens = computed(() => { - if (!computedHtmlTemplate.value) - return; if (!templateAstCompiled.value?.ast) return; @@ -168,7 +178,6 @@ export function createSourceFile( templateAstCompiled.value.ast, !!sfc.scriptSetup, Object.values(cssScopedClasses.value).map(style => style.classNames).flat(), - computedHtmlTemplate.value.mapping, { getEmitCompletion: SearchTexts.EmitCompletion, getPropsCompletion: SearchTexts.PropsCompletion, @@ -201,17 +210,17 @@ export function createSourceFile( raws: parseRefSugarCallRanges(ts, scriptSetupAst.value, ['$raw', '$fromRefs']), } : undefined)); - const plugins: VueLanguagePlugin[] = [ + const _plugins: VueLanguagePlugin[] = [ ...extraPlugins, - useVueFilePlugin(), - useMdFilePlugin(), - useHtmlFilePlugin(), - useHtmlPlugin(), - usePugPlugin(), - useVueSfcStyles(), - useVueSfcCustomBlocks(), - useVueSfcScriptsFormat(), - useVueSfcTemplate(), + useVueFilePlugin, + useMdFilePlugin, + useHtmlFilePlugin, + useHtmlPlugin, + usePugPlugin, + useVueSfcStyles, + useVueSfcCustomBlocks, + useVueSfcScriptsFormat, + useVueSfcTemplate, useVueTsScripts( ts, scriptRanges, @@ -224,6 +233,12 @@ export function createSourceFile( !!vueCompilerOptions.experimentalDisableTemplateSupport || compilerOptions.jsx !== ts.JsxEmit.Preserve, ), ]; + const pluginCtx: Parameters[0] = { + ts, + compilerOptions, + vueCompilerOptions: getVueCompilerOptions(vueCompilerOptions), + }; + const plugins = _plugins.map(plugin => plugin(pluginCtx)); // computeds const pluginEmbeddedFiles = plugins.map(plugin => { @@ -395,7 +410,6 @@ export function createSourceFile( set text(value) { update(value); }, - getSfcTemplateLanguageCompiled: () => computedHtmlTemplate.value, getSfcVueTemplateCompiled: () => templateAstCompiled.value, getScriptFileName: () => allEmbeddeds.value.find(e => e.file.fileName.replace(fileName, '').match(/^\.(js|ts)x?$/))?.file.fileName, getDescriptor: () => unref(sfc), diff --git a/packages/vue-language-core/src/types.ts b/packages/vue-language-core/src/types.ts index 94a035104..110bb6767 100644 --- a/packages/vue-language-core/src/types.ts +++ b/packages/vue-language-core/src/types.ts @@ -7,20 +7,22 @@ export type LanguageServiceHost = ts.LanguageServiceHost & { isTsc?: boolean, }; -export interface VueCompilerOptions { - target?: 2 | 2.7 | 3; - strictTemplates?: boolean; +export type VueCompilerOptions = Partial<_VueCompilerOptions>; + +export interface _VueCompilerOptions { + target: 2 | 2.7 | 3; + strictTemplates: boolean; // experimental - experimentalRuntimeMode?: 'runtime-dom' | 'runtime-uni-app'; - experimentalImplicitWrapComponentOptionsWithDefineComponent?: boolean | 'onlyJs'; - experimentalImplicitWrapComponentOptionsWithVue2Extend?: boolean | 'onlyJs'; - experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup?: boolean | 'onlyJs'; - experimentalTemplateCompilerOptions?: any; - experimentalTemplateCompilerOptionsRequirePath?: string; - experimentalDisableTemplateSupport?: boolean; - experimentalResolveStyleCssClasses?: 'scoped' | 'always' | 'never'; - experimentalAllowTypeNarrowingInInlineHandlers?: boolean; + experimentalRuntimeMode: 'runtime-dom' | 'runtime-uni-app'; + experimentalImplicitWrapComponentOptionsWithDefineComponent: boolean | 'onlyJs'; + experimentalImplicitWrapComponentOptionsWithVue2Extend: boolean | 'onlyJs'; + experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup: boolean | 'onlyJs'; + experimentalTemplateCompilerOptions: any; + experimentalTemplateCompilerOptionsRequirePath: string | undefined; + experimentalDisableTemplateSupport: boolean; + experimentalResolveStyleCssClasses: 'scoped' | 'always' | 'never'; + experimentalAllowTypeNarrowingInInlineHandlers: boolean; } export interface EmbeddedFileMappingData { diff --git a/packages/vue-language-core/src/utils/compileSFCTemplate.ts b/packages/vue-language-core/src/utils/compileSFCTemplate.ts deleted file mode 100644 index 993028b76..000000000 --- a/packages/vue-language-core/src/utils/compileSFCTemplate.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as CompilerDom from '@vue/compiler-dom'; -import * as CompilerCore from '@vue/compiler-core'; -import * as CompilerDOM from '@vue/compiler-dom'; -import * as CompilerVue2 from './vue2TemplateCompiler'; - -export function compileSFCTemplate(htmlCode: string, options: CompilerDOM.CompilerOptions = {}, vueVersion: number) { - - const errors: CompilerDOM.CompilerError[] = []; - const warnings: CompilerDOM.CompilerError[] = []; - let ast: CompilerDOM.RootNode | undefined; - - try { - ast = (vueVersion < 3 ? CompilerVue2 : CompilerDOM).compile(htmlCode, { - onError: (err: CompilerDOM.CompilerError) => errors.push(err), - onWarn: (err: CompilerDOM.CompilerError) => warnings.push(err), - expressionPlugins: ['typescript'], - ...options, - }).ast; - } - catch (e) { - const err = e as CompilerDOM.CompilerError; - errors.push(err); - } - - return { - errors, - warnings, - ast, - }; -} - -export function compile( - template: string, - options: CompilerDom.CompilerOptions = {} -): CompilerDom.CodegenResult { - - const onError = options.onError; - options.onError = (error) => { - if ( - error.code === CompilerCore.ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT // :key binding allowed in v-for template child in vue 2 - || error.code === CompilerCore.ErrorCodes.X_V_IF_SAME_KEY // fix https://github.com/johnsoncodehk/volar/issues/1638 - ) { - return; - } - if (onError) { - onError(error); - } - else { - throw error; - } - }; - - return baseCompile( - template, - Object.assign({}, CompilerDom.parserOptions, options, { - nodeTransforms: [ - ...CompilerDom.DOMNodeTransforms, - ...(options.nodeTransforms || []) - ], - directiveTransforms: Object.assign( - {}, - CompilerDom.DOMDirectiveTransforms, - options.directiveTransforms || {} - ), - }) - ); -} - -export function baseCompile( - template: string, - options: CompilerCore.CompilerOptions = {} -): CompilerCore.CodegenResult { - - const onError = options.onError || ((error) => { throw error; }); - const isModuleMode = options.mode === 'module'; - - const prefixIdentifiers = options.prefixIdentifiers === true || isModuleMode; - if (!prefixIdentifiers && options.cacheHandlers) { - onError(CompilerCore.createCompilerError(CompilerCore.ErrorCodes.X_CACHE_HANDLER_NOT_SUPPORTED)); - } - if (options.scopeId && !isModuleMode) { - onError(CompilerCore.createCompilerError(CompilerCore.ErrorCodes.X_SCOPE_ID_NOT_SUPPORTED)); - } - - const ast = CompilerCore.baseParse(template, options); - const [nodeTransforms, directiveTransforms] = CompilerCore.getBaseTransformPreset(prefixIdentifiers); - - // v-for > v-if in vue 2 - const transformIf = nodeTransforms[1]; - const transformFor = nodeTransforms[3]; - nodeTransforms[1] = transformFor; - nodeTransforms[3] = transformIf; - - CompilerCore.transform( - ast, - Object.assign({}, options, { - prefixIdentifiers, - nodeTransforms: [ - ...nodeTransforms, - ...(options.nodeTransforms || []) // user transforms - ], - directiveTransforms: Object.assign( - {}, - directiveTransforms, - options.directiveTransforms || {} // user transforms - ) - }) - ); - - return CompilerCore.generate( - ast, - Object.assign({}, options, { - prefixIdentifiers - }) - ); -} diff --git a/packages/vue-language-core/src/utils/ts.ts b/packages/vue-language-core/src/utils/ts.ts index b11003e2d..d80a2864a 100644 --- a/packages/vue-language-core/src/utils/ts.ts +++ b/packages/vue-language-core/src/utils/ts.ts @@ -1,6 +1,8 @@ import type * as ts from 'typescript/lib/tsserverlibrary'; import * as path from 'path'; -import type { VueCompilerOptions } from '../types'; +import type { VueCompilerOptions, _VueCompilerOptions } from '../types'; + +export type ParsedCommandLine = ReturnType; export function createParsedCommandLine( ts: typeof import('typescript/lib/tsserverlibrary'), @@ -42,6 +44,24 @@ export function createParsedCommandLine( }; } +export function getVueCompilerOptions(vueOptions: VueCompilerOptions): _VueCompilerOptions { + return { + target: vueOptions.target ?? 3, + strictTemplates: vueOptions.strictTemplates ?? false, + + // experimental + experimentalRuntimeMode: vueOptions.experimentalRuntimeMode ?? 'runtime-dom', + experimentalImplicitWrapComponentOptionsWithDefineComponent: vueOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent ?? 'onlyJs', + experimentalImplicitWrapComponentOptionsWithVue2Extend: vueOptions.experimentalImplicitWrapComponentOptionsWithVue2Extend ?? 'onlyJs', + experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup: vueOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup ?? 'onlyJs', + experimentalTemplateCompilerOptions: vueOptions.experimentalTemplateCompilerOptions ?? undefined, + experimentalTemplateCompilerOptionsRequirePath: vueOptions.experimentalTemplateCompilerOptionsRequirePath ?? undefined, + experimentalDisableTemplateSupport: vueOptions.experimentalDisableTemplateSupport ?? false, + experimentalResolveStyleCssClasses: vueOptions.experimentalResolveStyleCssClasses ?? 'scoped', + experimentalAllowTypeNarrowingInInlineHandlers: vueOptions.experimentalAllowTypeNarrowingInInlineHandlers ?? false, + }; +} + function resolveVueCompilerOptions(rawOptions: { [key: string]: any, experimentalTemplateCompilerOptionsRequirePath?: string, diff --git a/packages/vue-language-server/src/project.ts b/packages/vue-language-server/src/project.ts index 0cef0ec9c..afc84e462 100644 --- a/packages/vue-language-server/src/project.ts +++ b/packages/vue-language-server/src/project.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode-languageserver'; import type { createLsConfigs } from './configHost'; import { getDocumentSafely } from './utils'; import { LanguageConfigs, loadCustomPlugins, RuntimeEnvironment } from './common'; -import { tsShared } from '@volar/vue-language-core'; export interface Project extends ReturnType { } @@ -200,7 +199,7 @@ export async function createProject( vueLs?.dispose(); scripts.clear(); } - function createParsedCommandLine(): ReturnType { + function createParsedCommandLine(): ReturnType { const parseConfigHost: ts.ParseConfigHost = { useCaseSensitiveFileNames: projectSys.useCaseSensitiveFileNames, readDirectory: (path, extensions, exclude, include, depth) => { @@ -216,7 +215,7 @@ export async function createProject( readFile: projectSys.readFile, }; if (typeof tsConfig === 'string') { - return tsShared.createParsedCommandLine(ts, parseConfigHost, tsConfig); + return vue.createParsedCommandLine(ts, parseConfigHost, tsConfig); } else { const content = ts.parseJsonConfigFileContent({}, parseConfigHost, rootPath, tsConfig, 'tsconfig.json'); diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index a92f92a3a..2f07c9e99 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -151,9 +151,8 @@ export default function useVueTemplateLanguagePlugin { - const htmlComputed = vueFile.getSfcTemplateLanguageCompiled(); const ast = vueFile.getSfcVueTemplateCompiled()?.ast; const tags = new Map(); const attrs = new Set(); - if (ast && htmlComputed) { + if (ast) { walkElementNodes(ast, node => { if (!tags.has(node.tag)) { @@ -272,18 +271,11 @@ export function parseVueDocument( } const offsets = tags.get(node.tag)!; - const startTagHtmlOffset = node.loc.start.offset + node.loc.source.indexOf(node.tag); - const startTagTemplateOffset = htmlComputed.mapping({ start: startTagHtmlOffset, end: startTagHtmlOffset }); - if (startTagTemplateOffset !== undefined) { - offsets.push(startTagTemplateOffset.start); - } - const endTagHtmlOffset = node.loc.start.offset + node.loc.source.lastIndexOf(node.tag); - const endTagTemplateOffset = htmlComputed.mapping({ start: endTagHtmlOffset, end: endTagHtmlOffset }); - if (endTagTemplateOffset !== undefined) { - offsets.push(endTagTemplateOffset.end); - } + + offsets.push(startTagHtmlOffset); + offsets.push(endTagHtmlOffset); for (const prop of node.props) { if ( diff --git a/packages/vue-tsc/src/proxy.ts b/packages/vue-tsc/src/proxy.ts index 65c0ed025..af093cbf8 100644 --- a/packages/vue-tsc/src/proxy.ts +++ b/packages/vue-tsc/src/proxy.ts @@ -68,7 +68,7 @@ export function createProgramProxy( function getVueCompilerOptions(): vue.VueCompilerOptions { const tsConfig = ctx.options.options.configFilePath; if (typeof tsConfig === 'string') { - return vue.tsShared.createParsedCommandLine(ts, ts.sys, tsConfig).vueOptions; + return vue.createParsedCommandLine(ts, ts.sys, tsConfig).vueOptions; } return {}; } From aff4fa8fc060453e0554225a13ae0541e2f85d61 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 06:47:03 +0800 Subject: [PATCH 4/9] chore: vue-typescript-scripts.ts -> vue-tsx.ts --- .../src/plugins/{vue-typescript-scripts.ts => vue-tsx.ts} | 0 packages/vue-language-core/src/sourceFile.ts | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/vue-language-core/src/plugins/{vue-typescript-scripts.ts => vue-tsx.ts} (100%) diff --git a/packages/vue-language-core/src/plugins/vue-typescript-scripts.ts b/packages/vue-language-core/src/plugins/vue-tsx.ts similarity index 100% rename from packages/vue-language-core/src/plugins/vue-typescript-scripts.ts rename to packages/vue-language-core/src/plugins/vue-tsx.ts diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index 68def92bc..29b27a7e1 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -19,7 +19,7 @@ import useVueSfcStyles from './plugins/vue-sfc-styles'; import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; import useVueSfcTemplate from './plugins/vue-sfc-template'; -import useVueTsScripts from './plugins/vue-typescript-scripts'; +import useVueTsx from './plugins/vue-tsx'; import type * as ts from 'typescript/lib/tsserverlibrary'; // fix TS2742 import { Mapping, MappingBase } from '@volar/source-map'; @@ -221,7 +221,7 @@ export function createSourceFile( useVueSfcCustomBlocks, useVueSfcScriptsFormat, useVueSfcTemplate, - useVueTsScripts( + useVueTsx( ts, scriptRanges, scriptSetupRanges, From 7962804bec77873772f09dc392996b6aca681435 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 07:52:50 +0800 Subject: [PATCH 5/9] refactor: move code gen computeds to plugin --- packages/vue-component-meta/src/index.ts | 7 +- .../vue-language-core/src/documentRegistry.ts | 8 +- packages/vue-language-core/src/index.ts | 4 +- packages/vue-language-core/src/lsContext.ts | 4 +- .../vue-language-core/src/plugins/vue-tsx.ts | 171 ++++++++++++------ packages/vue-language-core/src/sourceFile.ts | 154 ++++++---------- packages/vue-language-core/src/utils/ts.ts | 2 + .../src/documentFeatures/format.ts | 2 +- .../src/documentService.ts | 2 + .../src/ideFeatures/d3.ts | 2 +- .../src/languageFeatures/complete.ts | 2 +- .../src/languageService.ts | 2 + .../src/plugins/vue-autoinsert-parentheses.ts | 2 +- .../src/plugins/vue-convert-htmlpug.ts | 4 +- .../src/plugins/vue-convert-refsugar.ts | 57 +++--- .../src/plugins/vue-convert-scriptsetup.ts | 42 ++--- .../src/plugins/vue-convert-tagcasing.ts | 2 +- .../src/plugins/vue-template.ts | 40 ++-- .../vue-language-service/src/plugins/vue.ts | 17 +- .../src/utils/featureWorkers.ts | 4 +- .../src/utils}/refSugarRanges.ts | 4 +- .../vue-language-service/src/vueDocuments.ts | 8 +- packages/vue-typescript/src/index.ts | 2 +- 23 files changed, 289 insertions(+), 253 deletions(-) rename packages/{vue-language-core/src/parsers => vue-language-service/src/utils}/refSugarRanges.ts (97%) diff --git a/packages/vue-component-meta/src/index.ts b/packages/vue-component-meta/src/index.ts index 720952ca5..cf2ae0b1e 100644 --- a/packages/vue-component-meta/src/index.ts +++ b/packages/vue-component-meta/src/index.ts @@ -1,4 +1,5 @@ import * as vue from '@volar/vue-language-core'; +import { parseScriptSetupRanges } from '@volar/vue-language-core'; import * as ts from 'typescript/lib/tsserverlibrary'; import type { @@ -468,8 +469,8 @@ function readVueComponentDefaultProps(vueFileText: string, printer: ts.Printer | function scriptSetupWorker() { const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, {}, ts); - const descriptor = vueSourceFile.getDescriptor(); - const scriptSetupRanges = vueSourceFile.getScriptSetupRanges(); + const descriptor = vueSourceFile.sfc; + const scriptSetupRanges = descriptor.scriptSetupAst ? parseScriptSetupRanges(ts, descriptor.scriptSetupAst) : undefined; if (descriptor.scriptSetup && scriptSetupRanges?.withDefaultsArg) { @@ -520,7 +521,7 @@ function readVueComponentDefaultProps(vueFileText: string, printer: ts.Printer | function scriptWorker() { const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, {}, ts); - const descriptor = vueSourceFile.getDescriptor(); + const descriptor = vueSourceFile.sfc; if (descriptor.script) { const scriptResult = readTsComponentDefaultProps(descriptor.script.lang, descriptor.script.content, 'default', printer); diff --git a/packages/vue-language-core/src/documentRegistry.ts b/packages/vue-language-core/src/documentRegistry.ts index ee336f9d5..2e1c25a39 100644 --- a/packages/vue-language-core/src/documentRegistry.ts +++ b/packages/vue-language-core/src/documentRegistry.ts @@ -10,7 +10,7 @@ export interface DocumentRegistry extends ReturnType() { const embeddedDocumentsMap = computed(() => { const map = new WeakMap(); for (const sourceFile of all.value) { - for (const embedded of sourceFile.getAllEmbeddeds()) { + for (const embedded of sourceFile.allEmbeddeds) { map.set(embedded.file, sourceFile); } } @@ -34,7 +34,7 @@ function createDocumentRegistryBase() { const sourceMapsByFileName = computed(() => { const map = new Map(); for (const sourceFile of all.value) { - for (const embedded of sourceFile.getAllEmbeddeds()) { + for (const embedded of sourceFile.allEmbeddeds) { map.set(embedded.file.fileName.toLowerCase(), { vueFile: sourceFile, embedded }); } } @@ -44,7 +44,7 @@ function createDocumentRegistryBase() { const map = new Map(); for (const key in files) { const sourceFile = files[key]!; - for (const embedded of sourceFile.getAllEmbeddeds()) { + for (const embedded of sourceFile.allEmbeddeds) { if (embedded.teleport) { map.set(embedded.file.fileName.toLowerCase(), embedded.teleport); } diff --git a/packages/vue-language-core/src/index.ts b/packages/vue-language-core/src/index.ts index 27d75c3bf..a931fd859 100644 --- a/packages/vue-language-core/src/index.ts +++ b/packages/vue-language-core/src/index.ts @@ -4,10 +4,10 @@ export * from './sourceFile'; export * from './documentRegistry'; export * from './types'; export * from './lsContext'; -export * as refSugarRanges from './parsers/refSugarRanges'; +export * from './parsers/scriptSetupRanges'; export * as scriptRanges from './parsers/scriptRanges'; export * as scriptSetupConvertRanges from './parsers/scriptSetupConvertRanges'; -export * as scriptSetupRanges from './parsers/scriptSetupRanges'; +export * from './parsers/scriptSetupRanges'; export * as localTypes from './utils/localTypes'; export * from './utils/ts'; export * from './generators/template'; diff --git a/packages/vue-language-core/src/lsContext.ts b/packages/vue-language-core/src/lsContext.ts index 131c3eb68..d4b4c9e73 100644 --- a/packages/vue-language-core/src/lsContext.ts +++ b/packages/vue-language-core/src/lsContext.ts @@ -234,7 +234,7 @@ export function createLanguageContext( const newScripts: Record = {}; if (!tsFileUpdated) { - for (const embedded of sourceFile.getAllEmbeddeds()) { + for (const embedded of sourceFile.allEmbeddeds) { if (embedded.file.isTsHostFile) { oldScripts[embedded.file.fileName] = embedded.file.codeGen.getText(); } @@ -244,7 +244,7 @@ export function createLanguageContext( sourceFile.text = scriptText; if (!tsFileUpdated) { - for (const embedded of sourceFile.getAllEmbeddeds()) { + for (const embedded of sourceFile.allEmbeddeds) { if (embedded.file.isTsHostFile) { newScripts[embedded.file.fileName] = embedded.file.codeGen.getText(); } diff --git a/packages/vue-language-core/src/plugins/vue-tsx.ts b/packages/vue-language-core/src/plugins/vue-tsx.ts index c4c611f4b..f66f7a344 100644 --- a/packages/vue-language-core/src/plugins/vue-tsx.ts +++ b/packages/vue-language-core/src/plugins/vue-tsx.ts @@ -1,43 +1,25 @@ -import { Ref } from '@vue/reactivity'; +import { computed } from '@vue/reactivity'; import { generate as genScript } from '../generators/script'; -import type * as templateGen from '../generators/template'; +import * as templateGen from '../generators/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import { useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; -import { VueCompilerOptions } from '../types'; - -export default function ( - ts: typeof import('typescript/lib/tsserverlibrary'), - scriptRanges: Ref | undefined>, - scriptSetupRanges: Ref | undefined>, - htmlGen: Ref | undefined>, - compilerOptions: VueCompilerOptions, - cssVars: ReturnType, - cssModuleClasses: ReturnType, - cssScopedClasses: ReturnType, - disableTemplateScript: boolean, -) { - - const plugin: VueLanguagePlugin = () => ({ +import { Sfc, useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; +import { SearchTexts } from '../utils/string'; + +const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOptions }) => { + + const ts = modules.typescript; + const gen = new WeakMap>(); + + return { getEmbeddedFileNames(fileName, sfc) { const fileNames: string[] = []; if (!fileName.endsWith('.html')) { - let lang = !sfc.script && !sfc.scriptSetup ? 'ts' - : sfc.scriptSetup && sfc.scriptSetup.lang !== 'js' ? sfc.scriptSetup.lang - : sfc.script && sfc.script.lang !== 'js' ? sfc.script.lang - : 'js'; - if (!disableTemplateScript) { - if (lang === 'js') { - lang = 'jsx'; - } - else if (lang === 'ts') { - lang = 'tsx'; - } - } - fileNames.push(fileName + '.' + lang); + const _gen = useGen(fileName, sfc); + fileNames.push(fileName + '.' + _gen?.lang.value); } if (sfc.template) { fileNames.push(fileName + '.__VLS_template_format.tsx'); @@ -49,9 +31,8 @@ export default function ( resolveEmbeddedFile(fileName, sfc, embeddedFile) { const suffix = embeddedFile.fileName.replace(fileName, ''); - const match = suffix.match(/^\.(js|ts|jsx|tsx)?$/); - if (match) { - const lang = match[1]; + const _gen = useGen(fileName, sfc); + if (suffix === '.' + _gen?.lang.value) { embeddedFile.isTsHostFile = true; embeddedFile.capabilities = { diagnostics: true, @@ -61,22 +42,11 @@ export default function ( codeActions: true, inlayHints: true, }; - genScript( - ts, - fileName, - lang, - sfc.script ?? undefined, - sfc.scriptSetup ?? undefined, - scriptRanges.value, - scriptSetupRanges.value, - cssVars.value, - cssModuleClasses.value, - cssScopedClasses.value, - htmlGen.value, - compilerOptions, - embeddedFile.codeGen, - embeddedFile.teleportMappings, - ); + const tsx = _gen?.tsxGen.value; + if (tsx) { + embeddedFile.codeGen = tsx.codeGen; + embeddedFile.teleportMappings = tsx.teleports; + } } else if (suffix.match(/^\.__VLS_template_format\.tsx$/)) { @@ -90,19 +60,106 @@ export default function ( inlayHints: false, }; embeddedFile.isTsHostFile = false; - if (htmlGen.value) { - embeddedFile.codeGen = htmlGen.value.formatCodeGen; + + if (_gen?.htmlGen.value) { + embeddedFile.codeGen = _gen.htmlGen.value.formatCodeGen; } } else if (suffix.match(/^\.__VLS_template\.css$/)) { embeddedFile.parentFileName = fileName + '.' + sfc.template?.lang; - if (htmlGen.value) { - embeddedFile.codeGen = htmlGen.value.cssCodeGen; + + if (_gen?.htmlGen.value) { + embeddedFile.codeGen = _gen.htmlGen.value.cssCodeGen; } } }, - }); + }; + + function useGen(fileName: string, sfc: Sfc) { + if (!gen.has(sfc)) { + gen.set(sfc, createGen(fileName, sfc)); + } + return gen.get(sfc); + } + + function createGen(fileName: string, sfc: Sfc) { + + const lang = computed(() => { + let lang = !sfc.script && !sfc.scriptSetup ? 'ts' + : sfc.scriptSetup && sfc.scriptSetup.lang !== 'js' ? sfc.scriptSetup.lang + : sfc.script && sfc.script.lang !== 'js' ? sfc.script.lang + : 'js'; + const disableTemplateScript = vueCompilerOptions.experimentalDisableTemplateSupport || compilerOptions.jsx !== ts.JsxEmit.Preserve; + if (!disableTemplateScript) { + if (lang === 'js') { + lang = 'jsx'; + } + else if (lang === 'ts') { + lang = 'tsx'; + } + } + return lang; + }); + const cssVars = useCssVars(sfc); + const scriptRanges = computed(() => + sfc.scriptAst + ? parseScriptRanges(ts, sfc.scriptAst, !!sfc.scriptSetup, false, false) + : undefined + ); + const scriptSetupRanges = computed(() => + sfc.scriptSetupAst + ? parseScriptSetupRanges(ts, sfc.scriptSetupAst) + : undefined + ); + const cssModuleClasses = useStyleCssClasses(sfc, style => !!style.module); + const cssScopedClasses = useStyleCssClasses(sfc, style => { + const setting = vueCompilerOptions.experimentalResolveStyleCssClasses; + return (setting === 'scoped' && style.scoped) || setting === 'always'; + }); + const htmlGen = computed(() => { + + if (!sfc.templateAst) + return; + + return templateGen.generate( + ts, + { + target: vueCompilerOptions.target ?? 3, + strictTemplates: vueCompilerOptions.strictTemplates ?? false, + experimentalRuntimeMode: vueCompilerOptions.experimentalRuntimeMode, + experimentalAllowTypeNarrowingInInlineHandlers: vueCompilerOptions.experimentalAllowTypeNarrowingInInlineHandlers ?? false, + }, + sfc.template?.lang ?? 'html', + sfc.templateAst, + !!sfc.scriptSetup, + Object.values(cssScopedClasses.value).map(style => style.classNames).flat(), + { + getEmitCompletion: SearchTexts.EmitCompletion, + getPropsCompletion: SearchTexts.PropsCompletion, + } + ); + }); + const tsxGen = computed(() => genScript( + ts, + fileName, + lang.value, + sfc.script ?? undefined, + sfc.scriptSetup ?? undefined, + scriptRanges.value, + scriptSetupRanges.value, + cssVars.value, + cssModuleClasses.value, + cssScopedClasses.value, + htmlGen.value, + vueCompilerOptions, + )); - return plugin; -} + return { + lang, + htmlGen, + tsxGen, + }; + } +}; +export default plugin; diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index 29b27a7e1..30d1c6ec9 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -1,34 +1,31 @@ -import { EmbeddedFileMappingData, TeleportMappingData, TextRange, VueCompilerOptions, _VueCompilerOptions } from './types'; -import { parseRefSugarCallRanges, parseRefSugarDeclarationRanges } from './parsers/refSugarRanges'; -import { parseScriptRanges } from './parsers/scriptRanges'; -import { parseScriptSetupRanges } from './parsers/scriptSetupRanges'; import { SFCBlock, SFCParseResult, SFCScriptBlock, SFCStyleBlock, SFCTemplateBlock } from '@vue/compiler-sfc'; -import { computed, ComputedRef, reactive, ref, unref } from '@vue/reactivity'; -import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; -import { SearchTexts } from './utils/string'; -import * as templateGen from './generators/template'; +import { computed, ComputedRef, reactive, ref } from '@vue/reactivity'; +import { EmbeddedFileMappingData, TeleportMappingData, TextRange, VueCompilerOptions, _VueCompilerOptions } from './types'; import { parseCssClassNames } from './utils/parseCssClassNames'; import { parseCssVars } from './utils/parseCssVars'; +import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; -import useVueFilePlugin from './plugins/file-vue'; -import useMdFilePlugin from './plugins/file-md'; import useHtmlFilePlugin from './plugins/file-html'; -import useHtmlPlugin from './plugins/vue-template-html'; -import usePugPlugin from './plugins/vue-template-pug'; -import useVueSfcStyles from './plugins/vue-sfc-styles'; +import useMdFilePlugin from './plugins/file-md'; +import useVueFilePlugin from './plugins/file-vue'; import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; +import useVueSfcStyles from './plugins/vue-sfc-styles'; import useVueSfcTemplate from './plugins/vue-sfc-template'; +import useHtmlPlugin from './plugins/vue-template-html'; +import usePugPlugin from './plugins/vue-template-pug'; import useVueTsx from './plugins/vue-tsx'; -import type * as ts from 'typescript/lib/tsserverlibrary'; // fix TS2742 -import { Mapping, MappingBase } from '@volar/source-map'; import { CodeGen } from '@volar/code-gen'; +import { Mapping, MappingBase } from '@volar/source-map'; import * as CompilerDom from '@vue/compiler-dom'; +import type * as ts from 'typescript/lib/tsserverlibrary'; import { getVueCompilerOptions } from './utils/ts'; export type VueLanguagePlugin = (ctx: { - ts: typeof ts, + modules: { + typescript: typeof ts, + }, compilerOptions: ts.CompilerOptions, vueCompilerOptions: _VueCompilerOptions, }) => { @@ -78,6 +75,11 @@ export interface Sfc { customBlocks: (SfcBlock & { type: string; })[]; + + // ast + templateAst: CompilerDom.RootNode | undefined; + scriptAst: ts.SourceFile | undefined; + scriptSetupAst: ts.SourceFile | undefined; } export interface EmbeddedFile { @@ -113,9 +115,28 @@ export function createSourceFile( scriptSetup: null, styles: [], customBlocks: [], + get templateAst() { + return compiledSFCTemplate.value?.ast; + }, + get scriptAst() { + return scriptAst.value; + }, + get scriptSetupAst() { + return scriptSetupAst.value; + }, }) as Sfc /* avoid Sfc unwrap in .d.ts by reactive */; // use + const scriptAst = computed(() => { + if (sfc.script) { + return ts.createSourceFile(fileName + '.' + sfc.script.lang, sfc.script.content, ts.ScriptTarget.Latest); + } + }); + const scriptSetupAst = computed(() => { + if (sfc.scriptSetup) { + return ts.createSourceFile(fileName + '.' + sfc.scriptSetup.lang, sfc.scriptSetup.content, ts.ScriptTarget.Latest); + } + }); const parsedSfc = computed(() => { for (const plugin of plugins) { const sfc = plugin.parseSFC?.(fileName, fileContent.value); @@ -124,7 +145,7 @@ export function createSourceFile( } } }); - const templateAstCompiled = computed(() => { + const compiledSFCTemplate = computed(() => { if (sfc.template) { for (const plugin of plugins) { @@ -156,59 +177,6 @@ export function createSourceFile( } } }); - const cssModuleClasses = useStyleCssClasses(sfc, style => !!style.module); - const cssScopedClasses = useStyleCssClasses(sfc, style => { - const setting = compilerOptions.experimentalResolveStyleCssClasses ?? 'scoped'; - return (setting === 'scoped' && style.scoped) || setting === 'always'; - }); - const templateCodeGens = computed(() => { - - if (!templateAstCompiled.value?.ast) - return; - - return templateGen.generate( - ts, - { - target: vueCompilerOptions.target ?? 3, - strictTemplates: vueCompilerOptions.strictTemplates ?? false, - experimentalRuntimeMode: vueCompilerOptions.experimentalRuntimeMode, - experimentalAllowTypeNarrowingInInlineHandlers: vueCompilerOptions.experimentalAllowTypeNarrowingInInlineHandlers ?? false, - }, - sfc.template?.lang ?? 'html', - templateAstCompiled.value.ast, - !!sfc.scriptSetup, - Object.values(cssScopedClasses.value).map(style => style.classNames).flat(), - { - getEmitCompletion: SearchTexts.EmitCompletion, - getPropsCompletion: SearchTexts.PropsCompletion, - } - ); - }); - const cssVars = useCssVars(sfc); - const scriptAst = computed(() => { - if (sfc.script) { - return ts.createSourceFile(fileName + '.' + sfc.script.lang, sfc.script.content, ts.ScriptTarget.Latest); - } - }); - const scriptSetupAst = computed(() => { - if (sfc.scriptSetup) { - return ts.createSourceFile(fileName + '.' + sfc.scriptSetup.lang, sfc.scriptSetup.content, ts.ScriptTarget.Latest); - } - }); - const scriptRanges = computed(() => - scriptAst.value - ? parseScriptRanges(ts, scriptAst.value, !!sfc.scriptSetup, false, false) - : undefined - ); - const scriptSetupRanges = computed(() => - scriptSetupAst.value - ? parseScriptSetupRanges(ts, scriptSetupAst.value) - : undefined - ); - const sfcRefSugarRanges = computed(() => (scriptSetupAst.value ? { - refs: parseRefSugarDeclarationRanges(ts, scriptSetupAst.value, ['$ref', '$computed', '$shallowRef', '$fromRefs']), - raws: parseRefSugarCallRanges(ts, scriptSetupAst.value, ['$raw', '$fromRefs']), - } : undefined)); const _plugins: VueLanguagePlugin[] = [ ...extraPlugins, @@ -221,20 +189,12 @@ export function createSourceFile( useVueSfcCustomBlocks, useVueSfcScriptsFormat, useVueSfcTemplate, - useVueTsx( - ts, - scriptRanges, - scriptSetupRanges, - templateCodeGens, - vueCompilerOptions, - cssVars, - cssModuleClasses, - cssScopedClasses, - !!vueCompilerOptions.experimentalDisableTemplateSupport || compilerOptions.jsx !== ts.JsxEmit.Preserve, - ), + useVueTsx, ]; const pluginCtx: Parameters[0] = { - ts, + modules: { + typescript: ts, + }, compilerOptions, vueCompilerOptions: getVueCompilerOptions(vueCompilerOptions), }; @@ -410,18 +370,24 @@ export function createSourceFile( set text(value) { update(value); }, - getSfcVueTemplateCompiled: () => templateAstCompiled.value, - getScriptFileName: () => allEmbeddeds.value.find(e => e.file.fileName.replace(fileName, '').match(/^\.(js|ts)x?$/))?.file.fileName, - getDescriptor: () => unref(sfc), - getScriptAst: () => scriptAst.value, - getScriptSetupAst: () => scriptSetupAst.value, - getSfcRefSugarRanges: () => sfcRefSugarRanges.value, - getEmbeddeds: () => embeddeds.value, - getScriptSetupRanges: () => scriptSetupRanges.value, - isJsxMissing: () => !vueCompilerOptions.experimentalDisableTemplateSupport && compilerOptions.jsx !== ts.JsxEmit.Preserve, - - getAllEmbeddeds: () => allEmbeddeds.value, - getTeleports: () => teleports.value, + get compiledSFCTemplate() { + return compiledSFCTemplate.value; + }, + get tsFileName() { + return allEmbeddeds.value.find(e => e.file.fileName.replace(fileName, '').match(/^\.(js|ts)x?$/))?.file.fileName ?? ''; + }, + get sfc() { + return sfc; + }, + get embeddeds() { + return embeddeds.value; + }, + get allEmbeddeds() { + return allEmbeddeds.value; + }, + get teleports() { + return teleports.value; + }, }; function embeddedRangeToVueRange(data: EmbeddedFileMappingData, range: Mapping['sourceRange']) { diff --git a/packages/vue-language-core/src/utils/ts.ts b/packages/vue-language-core/src/utils/ts.ts index d80a2864a..3e049ce96 100644 --- a/packages/vue-language-core/src/utils/ts.ts +++ b/packages/vue-language-core/src/utils/ts.ts @@ -46,6 +46,8 @@ export function createParsedCommandLine( export function getVueCompilerOptions(vueOptions: VueCompilerOptions): _VueCompilerOptions { return { + ...vueOptions, + target: vueOptions.target ?? 3, strictTemplates: vueOptions.strictTemplates ?? false, diff --git a/packages/vue-language-service/src/documentFeatures/format.ts b/packages/vue-language-service/src/documentFeatures/format.ts index dba637f6e..480d0c568 100644 --- a/packages/vue-language-service/src/documentFeatures/format.ts +++ b/packages/vue-language-service/src/documentFeatures/format.ts @@ -161,7 +161,7 @@ export function register(context: DocumentServiceRuntimeContext) { function getEmbeddedsByLevel(vueDocument: VueDocument, level: number) { - const embeddeds = vueDocument.file.getEmbeddeds(); + const embeddeds = vueDocument.file.embeddeds; const embeddedsLevels: EmbeddedStructure[][] = [embeddeds]; while (true) { diff --git a/packages/vue-language-service/src/documentService.ts b/packages/vue-language-service/src/documentService.ts index a6cb5c37f..32b4537f2 100644 --- a/packages/vue-language-service/src/documentService.ts +++ b/packages/vue-language-service/src/documentService.ts @@ -44,8 +44,10 @@ export function getDocumentService( // language support plugins const vuePlugin = useVuePlugin({ + ts, getVueDocument: doc => vueDocuments.get(doc), tsLs: undefined, + isJsxMissing: false, }); const htmlPlugin = useHtmlPlugin({ fileSystemProvider, diff --git a/packages/vue-language-service/src/ideFeatures/d3.ts b/packages/vue-language-service/src/ideFeatures/d3.ts index 9c6c18eab..5aeb7bd2a 100644 --- a/packages/vue-language-service/src/ideFeatures/d3.ts +++ b/packages/vue-language-service/src/ideFeatures/d3.ts @@ -40,7 +40,7 @@ // if (vueDocument) { -// const template = vueDocument.vueFile.getDescriptor().template; +// const template = vueDocument.vueFile.sfc.template; // if (template) { // funcs.push({ // type: 'func', diff --git a/packages/vue-language-service/src/languageFeatures/complete.ts b/packages/vue-language-service/src/languageFeatures/complete.ts index 8a938d569..8b718da82 100644 --- a/packages/vue-language-service/src/languageFeatures/complete.ts +++ b/packages/vue-language-service/src/languageFeatures/complete.ts @@ -130,7 +130,7 @@ export function register(context: LanguageServiceRuntimeContext) { if (vueDocument) { - const embeddeds = vueDocument.file.getEmbeddeds(); + const embeddeds = vueDocument.file.embeddeds; await visitEmbedded(vueDocument, embeddeds, async sourceMap => { diff --git a/packages/vue-language-service/src/languageService.ts b/packages/vue-language-service/src/languageService.ts index 4d66736c7..ab69b954e 100644 --- a/packages/vue-language-service/src/languageService.ts +++ b/packages/vue-language-service/src/languageService.ts @@ -162,8 +162,10 @@ export function createLanguageService( }; // plugins const vuePlugin = useVuePlugin({ + ts, getVueDocument: (document) => vueDocuments.get(document.uri), tsLs, + isJsxMissing: !vueLsHost.getVueCompilationSettings().experimentalDisableTemplateSupport && vueLsHost.getCompilationSettings().jsx !== ts.JsxEmit.Preserve, }); const vueTemplateHtmlPlugin = _useVueTemplateLanguagePlugin( 'html', diff --git a/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts b/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts index 90b3f27c0..df0e28c98 100644 --- a/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts +++ b/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts @@ -24,7 +24,7 @@ export default function (options: { if (!vueDocument) return; - const templateFormatScript = vueDocument.file.getAllEmbeddeds().find(e => + const templateFormatScript = vueDocument.file.allEmbeddeds.find(e => e.file.fileName.endsWith('.__VLS_template.format.tsx') || e.file.fileName.endsWith('.__VLS_template.format.jsx') ); diff --git a/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts b/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts index 94cf94c0a..2e5e07a27 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts @@ -28,7 +28,7 @@ export default function (options: { if (!isEnabled) return; - const descriptor = vueDocument.file.getDescriptor(); + const descriptor = vueDocument.file.sfc; if (descriptor.template && (descriptor.template.lang === 'html' || descriptor.template.lang === 'pug')) { @@ -57,7 +57,7 @@ export default function (options: { return worker(uri, vueDocument => { const document = vueDocument.getDocument(); - const desc = vueDocument.file.getDescriptor(); + const desc = vueDocument.file.sfc; if (!desc.template) return; diff --git a/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts b/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts index e29c17841..4151b9f26 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-refsugar.ts @@ -1,6 +1,6 @@ import * as shared from '@volar/shared'; import * as ts2 from '@volar/typescript-language-service'; -import { refSugarRanges } from '@volar/vue-language-core'; +import * as refSugarRanges from '../utils/refSugarRanges'; import * as vscode from 'vscode-languageserver-protocol'; import { mergeWorkspaceEdits } from '../languageFeatures/rename'; import { EmbeddedLanguageServicePlugin, ExecuteCommandContext, useConfigurationHost } from '@volar/vue-language-service-types'; @@ -50,14 +50,16 @@ export default function (options: { return; const result: vscode.CodeLens[] = []; - const descriptor = vueDocument.file.getDescriptor(); - const ranges = vueDocument.file.getSfcRefSugarRanges(); + const sfc = vueDocument.file.sfc; + + if (sfc.scriptSetup && sfc.scriptSetupAst) { + + const ranges = getRanges(options.ts, sfc.scriptSetupAst); - if (descriptor.scriptSetup && ranges) { result.push({ range: { - start: document.positionAt(descriptor.scriptSetup.startTagEnd), - end: document.positionAt(descriptor.scriptSetup.startTagEnd + descriptor.scriptSetup.content.length), + start: document.positionAt(sfc.scriptSetup.startTagEnd), + end: document.positionAt(sfc.scriptSetup.startTagEnd + sfc.scriptSetup.content.length), }, command: { title: 'ref sugar ' + (ranges.refs.length ? '☑' : '☐'), @@ -88,7 +90,7 @@ export default function (options: { const [uri] = args as CommandArgs; return worker(uri, vueDocument => { - return unuseRefSugar(vueDocument, context, options.doCodeActions, options.doCodeActionResolve, options.doRename, options.doValidation); + return unuseRefSugar(options.ts, vueDocument, context, options.doCodeActions, options.doCodeActionResolve, options.doRename, options.doValidation); }); } }, @@ -113,15 +115,13 @@ async function useRefSugar( scriptTsLs: ts2.LanguageService, ) { - const descriptor = vueDocument.file.getDescriptor(); - if (!descriptor.scriptSetup) return; - - const scriptSetupAst = vueDocument.file.getScriptSetupAst(); - if (!scriptSetupAst) return; + const sfc = vueDocument.file.sfc; + if (!sfc.scriptSetup) return; + if (!sfc.scriptSetupAst) return; context.workDoneProgress.begin('Unuse Ref Sugar', 0, '', true); - const edits = await getUseRefSugarEdits(vueDocument, descriptor.scriptSetup, scriptSetupAst); + const edits = await getUseRefSugarEdits(vueDocument, sfc.scriptSetup, sfc.scriptSetupAst); if (context.token.isCancellationRequested) return; @@ -134,8 +134,8 @@ async function useRefSugar( async function getUseRefSugarEdits( _vueDocument: VueDocument, - _scriptSetup: NonNullable, - _scriptSetupAst: NonNullable, + _scriptSetup: NonNullable, + _scriptSetupAst: ts.SourceFile, ) { const ranges = refSugarRanges.parseDeclarationRanges(ts, _scriptSetupAst); @@ -237,6 +237,7 @@ async function useRefSugar( } async function unuseRefSugar( + ts: typeof import('typescript/lib/tsserverlibrary'), vueDocument: VueDocument, context: ExecuteCommandContext, doCodeActions: (uri: string, range: vscode.Range, codeActionContext: vscode.CodeActionContext) => Promise, @@ -245,15 +246,13 @@ async function unuseRefSugar( doValidation: (uri: string) => Promise, ) { - const descriptor = vueDocument.file.getDescriptor(); - if (!descriptor.scriptSetup) return; - - const scriptSetupAst = vueDocument.file.getScriptSetupAst(); - if (!scriptSetupAst) return; + const sfc = vueDocument.file.sfc; + if (!sfc.scriptSetup) return; + if (!sfc.scriptSetupAst) return; context.workDoneProgress.begin('Unuse Ref Sugar', 0, '', true); - const edits = await getUnRefSugarEdits(vueDocument, descriptor.scriptSetup, scriptSetupAst); + const edits = await getUnRefSugarEdits(vueDocument, sfc.scriptSetup, sfc.scriptSetupAst); if (context.token.isCancellationRequested) return; @@ -307,11 +306,11 @@ async function unuseRefSugar( } async function getUnRefSugarEdits( _vueDocument: VueDocument, - _scriptSetup: NonNullable, - _scriptSetupAst: NonNullable, + _scriptSetup: NonNullable, + _scriptSetupAst: ts.SourceFile, ) { - const ranges = _vueDocument.file.getSfcRefSugarRanges(); + const ranges = getRanges(ts, _scriptSetupAst); const document = _vueDocument.getDocument(); const edits: vscode.TextEdit[] = []; @@ -410,3 +409,13 @@ async function unuseRefSugar( } } } + +function getRanges( + ts: typeof import('typescript/lib/tsserverlibrary'), + scriptSetupAst: ts.SourceFile, +) { + return { + refs: refSugarRanges.parseRefSugarDeclarationRanges(ts, scriptSetupAst, ['$ref', '$computed', '$shallowRef', '$fromRefs']), + raws: refSugarRanges.parseRefSugarCallRanges(ts, scriptSetupAst, ['$raw', '$fromRefs']), + }; +} diff --git a/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts b/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts index ce12b7bf3..3dea1d8ba 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-scriptsetup.ts @@ -40,7 +40,7 @@ export default function (options: { return; const result: vscode.CodeLens[] = []; - const descriptor = vueDocument.file.getDescriptor(); + const descriptor = vueDocument.file.sfc; if (descriptor.scriptSetup) { result.push({ @@ -113,16 +113,14 @@ async function useSetupSugar( doCodeActionResolve: (item: vscode.CodeAction) => Promise, ) { - const descriptor = vueDocument.file.getDescriptor(); - if (!descriptor.script) return; - - const scriptAst = vueDocument.file.getScriptAst(); - if (!scriptAst) return; + const sfc = vueDocument.file.sfc; + if (!sfc.script) return; + if (!sfc.scriptAst) return; const edits = await getEdits( vueDocument, - descriptor.script, - scriptAst, + sfc.script, + sfc.scriptAst, ); if (edits?.length) { @@ -138,8 +136,8 @@ async function useSetupSugar( async function getEdits( _vueDocument: NonNullable, - _script: NonNullable, - _scriptAst: NonNullable, + _script: NonNullable, + _scriptAst: ts.SourceFile, ) { const ranges = scriptSetupConvertRanges.parseUseScriptSetupRanges(ts, _scriptAst); @@ -283,18 +281,16 @@ async function unuseSetupSugar( doCodeActionResolve: (item: vscode.CodeAction) => Promise, ) { - const descriptor = vueDocument.file.getDescriptor(); - if (!descriptor.scriptSetup) return; - - const scriptSetupAst = vueDocument.file.getScriptSetupAst(); - if (!scriptSetupAst) return; + const sfc = vueDocument.file.sfc; + if (!sfc.scriptSetup) return; + if (!sfc.scriptSetupAst) return; const edits = await getEdits( vueDocument, - descriptor.script, - descriptor.scriptSetup, - vueDocument.file.getScriptAst(), - scriptSetupAst, + sfc.script, + sfc.scriptSetup, + sfc.scriptAst, + sfc.scriptSetupAst, ); if (edits?.length) { @@ -331,10 +327,10 @@ async function unuseSetupSugar( } async function getEdits( _vueDocument: NonNullable, - _script: typeof descriptor['script'], - _scriptSetup: NonNullable, - _scriptAst: typeof scriptSetupAst, - _scriptSetupAst: NonNullable, + _script: typeof sfc['script'], + _scriptSetup: NonNullable, + _scriptAst: ts.SourceFile | undefined, + _scriptSetupAst: ts.SourceFile, ) { const ranges = scriptSetupConvertRanges.parseUnuseScriptSetupRanges(ts, _scriptSetupAst); diff --git a/packages/vue-language-service/src/plugins/vue-convert-tagcasing.ts b/packages/vue-language-service/src/plugins/vue-convert-tagcasing.ts index 71f72f11a..8b4c294d5 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-tagcasing.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-tagcasing.ts @@ -25,7 +25,7 @@ export default function (options: { return worker(uri, async vueDocument => { - const desc = vueDocument.file.getDescriptor(); + const desc = vueDocument.file.sfc; if (!desc.template) return; diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 2f07c9e99..f94987c9e 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -150,7 +150,7 @@ export default function useVueTemplateLanguagePlugin, }; - const printText = printer.printNode(options.ts.EmitHint.Expression, newNode, scriptAst); + const printText = printer.printNode(options.ts.EmitHint.Expression, newNode, sfc.scriptAst); const editRange = vscode.Range.create( - textDoc.positionAt(descriptor.script.startTagEnd + exportDefault.componentsOption.start), - textDoc.positionAt(descriptor.script.startTagEnd + exportDefault.componentsOption.end), + textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.start), + textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.end), ); autoImportPositions.add(editRange.start); autoImportPositions.add(editRange.end); @@ -370,10 +368,10 @@ export default function useVueTemplateLanguagePlugin, }; - const printText = printer.printNode(options.ts.EmitHint.Expression, newNode, scriptAst); + const printText = printer.printNode(options.ts.EmitHint.Expression, newNode, sfc.scriptAst); const editRange = vscode.Range.create( - textDoc.positionAt(descriptor.script.startTagEnd + exportDefault.args.start), - textDoc.positionAt(descriptor.script.startTagEnd + exportDefault.args.end), + textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.start), + textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.end), ); autoImportPositions.add(editRange.start); autoImportPositions.add(editRange.end); @@ -387,7 +385,7 @@ export default function useVueTemplateLanguagePlugin('volar.completion.autoImportComponent') ?? true; if (enabledComponentAutoImport && (descriptor.script || descriptor.scriptSetup)) { @@ -720,7 +718,7 @@ export default function useVueTemplateLanguagePlugin(); - const file = sourceFile.file.getAllEmbeddeds().find(e => + const file = sourceFile.file.allEmbeddeds.find(e => e.file.fileName.endsWith('.__VLS_template.tsx') || e.file.fileName.endsWith('.__VLS_template.jsx') )?.file; diff --git a/packages/vue-language-service/src/plugins/vue.ts b/packages/vue-language-service/src/plugins/vue.ts index 35f727733..c22533ed0 100644 --- a/packages/vue-language-service/src/plugins/vue.ts +++ b/packages/vue-language-service/src/plugins/vue.ts @@ -1,5 +1,6 @@ import * as shared from '@volar/shared'; import type * as ts2 from '@volar/typescript-language-service'; +import { parseScriptSetupRanges } from '@volar/vue-language-core'; import { EmbeddedLanguageServicePlugin } from '@volar/vue-language-service-types'; import * as html from 'vscode-html-languageservice'; import * as vscode from 'vscode-languageserver-protocol'; @@ -93,8 +94,10 @@ const dataProvider = html.newHTMLDataProvider('vue', { }); export default function (options: { + ts: typeof import('typescript/lib/tsserverlibrary'), getVueDocument(document: TextDocument): VueDocument | undefined, tsLs: ts2.LanguageService | undefined, + isJsxMissing: boolean, }): EmbeddedLanguageServicePlugin { const htmlPlugin = useHtmlPlugin({ @@ -111,10 +114,10 @@ export default function (options: { return worker(document, (vueDocument) => { const result: vscode.Diagnostic[] = []; - const sfc = vueDocument.file.getDescriptor(); - const scriptSetupRanges = vueDocument.file.getScriptSetupRanges(); + const sfc = vueDocument.file.sfc; - if (scriptSetupRanges && sfc.scriptSetup) { + if (sfc.scriptSetup && sfc.scriptSetupAst) { + const scriptSetupRanges = parseScriptSetupRanges(options.ts, sfc.scriptSetupAst); for (const range of scriptSetupRanges.notOnTopTypeExports) { result.push(vscode.Diagnostic.create( { @@ -129,7 +132,7 @@ export default function (options: { } } - if (options.tsLs && !options.tsLs.__internal__.isValidFile(vueDocument.file.getScriptFileName() ?? '')) { + if (options.tsLs && !options.tsLs.__internal__.isValidFile(vueDocument.file.tsFileName)) { for (const script of [sfc.script, sfc.scriptSetup]) { if (!script || script.content === '') @@ -149,7 +152,7 @@ export default function (options: { } } - if (options.tsLs && sfc.template && vueDocument.file.isJsxMissing()) { + if (options.tsLs && sfc.template && options.isJsxMissing) { const error = vscode.Diagnostic.create( { start: document.positionAt(sfc.template.start), @@ -171,7 +174,7 @@ export default function (options: { return worker(document, (vueDocument) => { const result: vscode.SymbolInformation[] = []; - const descriptor = vueDocument.file.getDescriptor(); + const descriptor = vueDocument.file.sfc; if (descriptor.template) { result.push({ @@ -263,7 +266,7 @@ export default function (options: { function getSfcCodeWithEmptyBlocks(vueDocument: VueDocument, sfcCode: string) { - const descriptor = vueDocument.file.getDescriptor(); + const descriptor = vueDocument.file.sfc; const blocks = [ descriptor.template, // relate to below descriptor.script, diff --git a/packages/vue-language-service/src/utils/featureWorkers.ts b/packages/vue-language-service/src/utils/featureWorkers.ts index 906be15d5..a64f873d8 100644 --- a/packages/vue-language-service/src/utils/featureWorkers.ts +++ b/packages/vue-language-service/src/utils/featureWorkers.ts @@ -41,7 +41,7 @@ export async function documentArgFeatureWorker( if (vueDocument) { - const embeddeds = vueDocument.file.getEmbeddeds(); + const embeddeds = vueDocument.file.embeddeds; await visitEmbedded(vueDocument, embeddeds, async sourceMap => { @@ -128,7 +128,7 @@ export async function languageFeatureWorker( if (vueDocument) { - const embeddeds = vueDocument.file.getEmbeddeds(); + const embeddeds = vueDocument.file.embeddeds; await visitEmbedded(vueDocument, embeddeds, async sourceMap => { diff --git a/packages/vue-language-core/src/parsers/refSugarRanges.ts b/packages/vue-language-service/src/utils/refSugarRanges.ts similarity index 97% rename from packages/vue-language-core/src/parsers/refSugarRanges.ts rename to packages/vue-language-service/src/utils/refSugarRanges.ts index daaaf66cc..5bf04b2f3 100644 --- a/packages/vue-language-core/src/parsers/refSugarRanges.ts +++ b/packages/vue-language-service/src/utils/refSugarRanges.ts @@ -1,6 +1,6 @@ +import { TextRange } from '@volar/vue-language-core'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import { getStartEnd, findBindingVars } from './scriptSetupRanges'; -import type { TextRange } from '../types'; +import { getStartEnd, findBindingVars } from '@volar/vue-language-core'; export interface RefSugarDeclarationRanges extends ReturnType { } diff --git a/packages/vue-language-service/src/vueDocuments.ts b/packages/vue-language-service/src/vueDocuments.ts index d7d10940a..db0b6e712 100644 --- a/packages/vue-language-service/src/vueDocuments.ts +++ b/packages/vue-language-service/src/vueDocuments.ts @@ -246,10 +246,10 @@ export function parseVueDocument( // reactivity const document = computed(() => TextDocument.create(shared.fsPathToUri(vueFile.fileName), vueFile.fileName.endsWith('.md') ? 'markdown' : 'vue', documentVersion++, vueFile.text)); const sourceMaps = computed(() => { - return vueFile.getAllEmbeddeds().map(embedded => sourceMapsMap.get(embedded)); + return vueFile.allEmbeddeds.map(embedded => sourceMapsMap.get(embedded)); }); const teleports = computed(() => { - return vueFile.getTeleports().map(teleportAndFile => { + return vueFile.teleports.map(teleportAndFile => { const embeddedDocument = embeddedDocumentsMap.get(teleportAndFile.file); const sourceMap = new TeleportSourceMap( teleportAndFile.file, @@ -260,7 +260,7 @@ export function parseVueDocument( }); }); const templateTagsAndAttrs = computed(() => { - const ast = vueFile.getSfcVueTemplateCompiled()?.ast; + const ast = vueFile.compiledSFCTemplate?.ast; const tags = new Map(); const attrs = new Set(); if (ast) { @@ -318,7 +318,7 @@ export function parseVueDocument( includeCompletionsWithInsertText: true, // if missing, { 'aaa-bbb': any, ccc: any } type only has result ['ccc'] }; - const file = vueFile.getAllEmbeddeds().find(e => e.file.fileName.indexOf('.__VLS_template.') >= 0)?.file; + const file = vueFile.allEmbeddeds.find(e => e.file.fileName.indexOf('.__VLS_template.') >= 0)?.file; if (file && file.codeGen.getText().indexOf(vue.SearchTexts.Components) >= 0) { const document = embeddedDocumentsMap.get(file); diff --git a/packages/vue-typescript/src/index.ts b/packages/vue-typescript/src/index.ts index 8280857e2..71995a24c 100644 --- a/packages/vue-typescript/src/index.ts +++ b/packages/vue-typescript/src/index.ts @@ -53,7 +53,7 @@ export function createLanguageService(...params: Parameters Date: Wed, 10 Aug 2022 08:15:56 +0800 Subject: [PATCH 6/9] refactor: share vue plugins --- packages/vue-component-meta/src/index.ts | 8 +-- packages/vue-language-core/src/index.ts | 4 ++ packages/vue-language-core/src/lsContext.ts | 54 +++++++++++++++++-- packages/vue-language-core/src/sourceFile.ts | 37 +------------ .../src/documentService.ts | 3 +- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/packages/vue-component-meta/src/index.ts b/packages/vue-component-meta/src/index.ts index cf2ae0b1e..167f9d400 100644 --- a/packages/vue-component-meta/src/index.ts +++ b/packages/vue-component-meta/src/index.ts @@ -175,7 +175,7 @@ export function createComponentMetaChecker(tsconfigPath: string, checkerOptions: const snapshot = host.getScriptSnapshot(componentPath)!; const vueDefaults = componentPath.endsWith('.vue') && exportName === 'default' - ? readVueComponentDefaultProps(snapshot.getText(0, snapshot.getLength()), printer) + ? readVueComponentDefaultProps(core, snapshot.getText(0, snapshot.getLength()), printer) : {}; const tsDefaults = !componentPath.endsWith('.vue') ? readTsComponentDefaultProps( componentPath.substring(componentPath.lastIndexOf('.') + 1), // ts | js | tsx | jsx @@ -458,7 +458,7 @@ function createSchemaResolvers(typeChecker: ts.TypeChecker, symbolNode: ts.Expre }; } -function readVueComponentDefaultProps(vueFileText: string, printer: ts.Printer | undefined) { +function readVueComponentDefaultProps(core: vue.LanguageContext, vueFileText: string, printer: ts.Printer | undefined) { let result: Record = {}; scriptSetupWorker(); @@ -468,7 +468,7 @@ function readVueComponentDefaultProps(vueFileText: string, printer: ts.Printer | function scriptSetupWorker() { - const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, {}, ts); + const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, ts, core.plugins); const descriptor = vueSourceFile.sfc; const scriptSetupRanges = descriptor.scriptSetupAst ? parseScriptSetupRanges(ts, descriptor.scriptSetupAst) : undefined; @@ -520,7 +520,7 @@ function readVueComponentDefaultProps(vueFileText: string, printer: ts.Printer | function scriptWorker() { - const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, {}, ts); + const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, ts, core.plugins); const descriptor = vueSourceFile.sfc; if (descriptor.script) { diff --git a/packages/vue-language-core/src/index.ts b/packages/vue-language-core/src/index.ts index a931fd859..98e118056 100644 --- a/packages/vue-language-core/src/index.ts +++ b/packages/vue-language-core/src/index.ts @@ -11,3 +11,7 @@ export * from './parsers/scriptSetupRanges'; export * as localTypes from './utils/localTypes'; export * from './utils/ts'; export * from './generators/template'; + +// fix build +export * as _0 from '@vue/compiler-sfc'; +export * as _1 from '@vue/compiler-core'; diff --git a/packages/vue-language-core/src/lsContext.ts b/packages/vue-language-core/src/lsContext.ts index d4b4c9e73..b580286fb 100644 --- a/packages/vue-language-core/src/lsContext.ts +++ b/packages/vue-language-core/src/lsContext.ts @@ -1,12 +1,56 @@ import { posix as path } from 'path'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import { LanguageServiceHost } from './types'; +import { LanguageServiceHost, VueCompilerOptions } from './types'; import * as localTypes from './utils/localTypes'; import { createSourceFile, EmbeddedFile, VueLanguagePlugin } from './sourceFile'; import { createDocumentRegistry } from './documentRegistry'; +import useHtmlFilePlugin from './plugins/file-html'; +import useMdFilePlugin from './plugins/file-md'; +import useVueFilePlugin from './plugins/file-vue'; +import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; +import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; +import useVueSfcStyles from './plugins/vue-sfc-styles'; +import useVueSfcTemplate from './plugins/vue-sfc-template'; +import useHtmlPlugin from './plugins/vue-template-html'; +import usePugPlugin from './plugins/vue-template-pug'; +import useVueTsx from './plugins/vue-tsx'; +import { getVueCompilerOptions } from './utils/ts'; + export type LanguageContext = ReturnType; +export function getPlugins( + ts: typeof import('typescript/lib/tsserverlibrary'), + compilerOptions: ts.CompilerOptions, + vueCompilerOptions: VueCompilerOptions, + extraPlugins: VueLanguagePlugin[] = [], +) { + + const _plugins: VueLanguagePlugin[] = [ + ...extraPlugins, + useVueFilePlugin, + useMdFilePlugin, + useHtmlFilePlugin, + useHtmlPlugin, + usePugPlugin, + useVueSfcStyles, + useVueSfcCustomBlocks, + useVueSfcScriptsFormat, + useVueSfcTemplate, + useVueTsx, + ]; + const pluginCtx: Parameters[0] = { + modules: { + typescript: ts, + }, + compilerOptions, + vueCompilerOptions: getVueCompilerOptions(vueCompilerOptions), + }; + const plugins = _plugins.map(plugin => plugin(pluginCtx)); + + return plugins; +} + export function createLanguageContext( host: LanguageServiceHost, extraPlugins: VueLanguagePlugin[] = [], @@ -62,10 +106,9 @@ export function createLanguageContext( documentRegistry.set(vueFileName, createSourceFile( vueFileName, scriptSnapshot.getText(0, scriptSnapshot.getLength()), - compilerOptions, vueCompilerOptions, ts, - extraPlugins, + plugins, )); } } @@ -116,6 +159,7 @@ export function createLanguageContext( } }, }; + const plugins = getPlugins(ts, compilerOptions, vueCompilerOptions, extraPlugins); return { typescriptLanguageServiceHost: new Proxy(_tsHost as ts.LanguageServiceHost, { @@ -130,6 +174,7 @@ export function createLanguageContext( return target[property]; }, }), + plugins, }; function update() { @@ -221,10 +266,9 @@ export function createLanguageContext( documentRegistry.set(fileName, createSourceFile( fileName, scriptText, - compilerOptions, vueCompilerOptions, ts, - extraPlugins, + plugins, )); tsFileUpdated = true; } diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index 30d1c6ec9..cd9232bda 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -5,22 +5,10 @@ import { parseCssClassNames } from './utils/parseCssClassNames'; import { parseCssVars } from './utils/parseCssVars'; import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; -import useHtmlFilePlugin from './plugins/file-html'; -import useMdFilePlugin from './plugins/file-md'; -import useVueFilePlugin from './plugins/file-vue'; -import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; -import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; -import useVueSfcStyles from './plugins/vue-sfc-styles'; -import useVueSfcTemplate from './plugins/vue-sfc-template'; -import useHtmlPlugin from './plugins/vue-template-html'; -import usePugPlugin from './plugins/vue-template-pug'; -import useVueTsx from './plugins/vue-tsx'; - import { CodeGen } from '@volar/code-gen'; import { Mapping, MappingBase } from '@volar/source-map'; import * as CompilerDom from '@vue/compiler-dom'; import type * as ts from 'typescript/lib/tsserverlibrary'; -import { getVueCompilerOptions } from './utils/ts'; export type VueLanguagePlugin = (ctx: { modules: { @@ -101,10 +89,9 @@ export interface EmbeddedFile { export function createSourceFile( fileName: string, _content: string, - compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, ts: typeof import('typescript/lib/tsserverlibrary'), - extraPlugins: VueLanguagePlugin[] = [], + plugins: ReturnType[], ) { // refs @@ -178,28 +165,6 @@ export function createSourceFile( } }); - const _plugins: VueLanguagePlugin[] = [ - ...extraPlugins, - useVueFilePlugin, - useMdFilePlugin, - useHtmlFilePlugin, - useHtmlPlugin, - usePugPlugin, - useVueSfcStyles, - useVueSfcCustomBlocks, - useVueSfcScriptsFormat, - useVueSfcTemplate, - useVueTsx, - ]; - const pluginCtx: Parameters[0] = { - modules: { - typescript: ts, - }, - compilerOptions, - vueCompilerOptions: getVueCompilerOptions(vueCompilerOptions), - }; - const plugins = _plugins.map(plugin => plugin(pluginCtx)); - // computeds const pluginEmbeddedFiles = plugins.map(plugin => { const embeddedFiles: Record> = {}; diff --git a/packages/vue-language-service/src/documentService.ts b/packages/vue-language-service/src/documentService.ts index 32b4537f2..f700449f6 100644 --- a/packages/vue-language-service/src/documentService.ts +++ b/packages/vue-language-service/src/documentService.ts @@ -104,6 +104,7 @@ export function getDocumentService( } }, }; + const vuePlugins = vue.getPlugins(ts, {}, {}, []); return { format: format.register(context), @@ -131,8 +132,8 @@ export function getDocumentService( '/untitled.' + shared.languageIdToSyntax(document.languageId), document.getText(), {}, - {}, context.typescript, + vuePlugins, ); vueDoc = parseVueDocument(vueFile, undefined); From 9906ca34e364072b834690f66985bbc7870e73fe Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 08:30:00 +0800 Subject: [PATCH 7/9] chore: move code --- .../src/generators/script.ts | 85 +++++++++---------- .../vue-language-core/src/plugins/vue-tsx.ts | 47 +++++++++- packages/vue-language-core/src/sourceFile.ts | 47 +--------- 3 files changed, 85 insertions(+), 94 deletions(-) diff --git a/packages/vue-language-core/src/generators/script.ts b/packages/vue-language-core/src/generators/script.ts index 1b3729563..614f9831f 100644 --- a/packages/vue-language-core/src/generators/script.ts +++ b/packages/vue-language-core/src/generators/script.ts @@ -5,7 +5,8 @@ import { posix as path } from 'path'; import type * as templateGen from '../generators/template'; import type { ScriptRanges } from '../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import { useCssVars, useStyleCssClasses } from '../sourceFile'; +import { useCssVars, useStyleCssClasses } from '../plugins/vue-tsx'; +import { Sfc } from '../sourceFile'; import type { EmbeddedFileMappingData, TeleportMappingData } from '../types'; import { TextRange, VueCompilerOptions } from '../types'; import { getSlotsPropertyName, getVueLibraryName } from '../utils/shared'; @@ -18,14 +19,8 @@ import { walkInterpolationFragment } from '../utils/transform'; export function generate( ts: typeof import('typescript/lib/tsserverlibrary'), fileName: string, + sfc: Sfc, lang: string, - script: undefined | { - src?: string, - content: string, - }, - scriptSetup: undefined | { - content: string, - }, scriptRanges: ScriptRanges | undefined, scriptSetupRanges: ScriptSetupRanges | undefined, cssVars: ReturnType['value'], @@ -59,7 +54,7 @@ export function generate( writeScriptContentAfterExportDefault(); writeTemplateIfNoScriptSetup(); - if (!script && !scriptSetup) { + if (!sfc.script && !sfc.scriptSetup) { codeGen.addCode( 'export default {} as any', { @@ -74,13 +69,13 @@ export function generate( ); } - if (scriptSetup) { + if (sfc.scriptSetup) { // for code action edits codeGen.addCode( '', { - start: scriptSetup.content.length, - end: scriptSetup.content.length, + start: sfc.scriptSetup.content.length, + end: sfc.scriptSetup.content.length, }, SourceMaps.Mode.Offset, { @@ -109,7 +104,7 @@ export function generate( }); // fix https://github.com/johnsoncodehk/volar/issues/1127 - if (scriptSetup && exportdefaultStart !== undefined && exportdefaultEnd !== undefined) { + if (sfc.scriptSetup && exportdefaultStart !== undefined && exportdefaultEnd !== undefined) { codeGen.addMapping2({ data: { vueTag: 'scriptSetup', @@ -124,7 +119,7 @@ export function generate( }, sourceRange: { start: 0, - end: scriptSetup.content.length, + end: sfc.scriptSetup.content.length, }, }); } @@ -161,10 +156,10 @@ export function generate( } } function writeScriptSrc() { - if (!script?.src) + if (!sfc.script?.src) return; - let src = script.src; + let src = sfc.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); @@ -194,17 +189,17 @@ export function generate( codeGen.addText(`export { default } from '${src}';\n`); } function writeScriptContentBeforeExportDefault() { - if (!script) + if (!sfc.script) return; - if (!!scriptSetup && scriptRanges?.exportDefault) { + if (!!sfc.scriptSetup && scriptRanges?.exportDefault) { addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); exportdefaultStart = codeGen.getText().length - (scriptRanges.exportDefault.expression.start - scriptRanges.exportDefault.start); } else { let isExportRawObject = false; if (scriptRanges?.exportDefault) { - isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); + isExportRawObject = sfc.script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{'); } const wrapMode = getImplicitWrapComponentOptionsMode(lang); if (isExportRawObject && wrapMode && scriptRanges?.exportDefault) { @@ -217,24 +212,24 @@ export function generate( } addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end); codeGen.addText(`)`); - addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length); + addVirtualCode('script', scriptRanges.exportDefault.expression.end, sfc.script.content.length); } else { - addVirtualCode('script', 0, script.content.length); + addVirtualCode('script', 0, sfc.script.content.length); } } } function writeScriptContentAfterExportDefault() { - if (!script) + if (!sfc.script) return; - if (!!scriptSetup && scriptRanges?.exportDefault) { - addVirtualCode('script', scriptRanges.exportDefault.end, script.content.length); + if (!!sfc.scriptSetup && scriptRanges?.exportDefault) { + addVirtualCode('script', scriptRanges.exportDefault.end, sfc.script.content.length); } } function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { codeGen.addCode( - (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end), + (vueTag === 'script' ? sfc.script : sfc.scriptSetup)!.content.substring(start, end), { start, end }, SourceMaps.Mode.Offset, { @@ -253,7 +248,7 @@ export function generate( } function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { codeGen.addCode( - (vueTag === 'scriptSetup' ? scriptSetup : script)!.content.substring(start, end), + (vueTag === 'scriptSetup' ? sfc.scriptSetup : sfc.script)!.content.substring(start, end), { start, end }, SourceMaps.Mode.Offset, { @@ -268,14 +263,14 @@ export function generate( } function writeScriptSetupImportsSegment() { - if (!scriptSetup) + if (!sfc.scriptSetup) return; if (!scriptSetupRanges) return; codeGen.addCode( - scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset), + sfc.scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset), { start: 0, end: scriptSetupRanges.importSectionEndOffset, @@ -297,13 +292,13 @@ export function generate( } function writeTemplateIfNoScriptSetup() { - if (!scriptSetup) { + if (!sfc.scriptSetup) { writeTemplate(); } } function writeScriptSetupAndTemplate() { - if (scriptSetup && scriptSetupRanges) { + if (sfc.scriptSetup && scriptSetupRanges) { if (scriptRanges?.exportDefault) { codeGen.addText('await (async () => {\n'); @@ -316,10 +311,10 @@ export function generate( codeGen.addText('const __VLS_setup = async () => {\n'); codeGen.addCode( - scriptSetup.content.substring(scriptSetupRanges.importSectionEndOffset), + sfc.scriptSetup.content.substring(scriptSetupRanges.importSectionEndOffset), { start: scriptSetupRanges.importSectionEndOffset, - end: scriptSetup.content.length, + end: sfc.scriptSetup.content.length, }, SourceMaps.Mode.Offset, { @@ -440,7 +435,7 @@ export function generate( codeGen.addText(`};\n`); codeGen.addText(`},\n`); - if (script && scriptRanges?.exportDefault?.args) { + if (sfc.script && scriptRanges?.exportDefault?.args) { addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); } @@ -476,7 +471,7 @@ export function generate( } function writeComponentForTemplateUsage(cssIds: Set) { - if (scriptSetup && scriptSetupRanges) { + if (sfc.scriptSetup && scriptSetupRanges) { codeGen.addText(`const __VLS_component = (await import('${vueLibName}')).defineComponent({\n`); codeGen.addText(`setup() {\n`); @@ -507,13 +502,13 @@ export function generate( }[] = []; bindingsArr.push({ bindings: scriptSetupRanges.bindings, - content: scriptSetup.content, + content: sfc.scriptSetup.content, vueTag: 'scriptSetup', }); - if (scriptRanges && script) { + if (scriptRanges && sfc.script) { bindingsArr.push({ bindings: scriptRanges.bindings, - content: script.content, + content: sfc.script.content, vueTag: 'script', }); } @@ -563,11 +558,11 @@ export function generate( function writeExportOptions() { codeGen.addText(`\n`); codeGen.addText(`const __VLS_options = {\n`); - if (script && scriptRanges?.exportDefault?.args) { + if (sfc.script && scriptRanges?.exportDefault?.args) { const args = scriptRanges.exportDefault.args; codeGen.addText(`...(`); codeGen.addCode( - script.content.substring(args.start, args.end), + sfc.script.content.substring(args.start, args.end), args, SourceMaps.Mode.Offset, { @@ -584,13 +579,13 @@ export function generate( } function writeConstNameOption() { codeGen.addText(`\n`); - if (script && scriptRanges?.exportDefault?.args) { + if (sfc.script && scriptRanges?.exportDefault?.args) { const args = scriptRanges.exportDefault.args; codeGen.addText(`const __VLS_name = (await import('./__VLS_types.js')).getNameOption(`); - codeGen.addText(`${script.content.substring(args.start, args.end)} as const`); + codeGen.addText(`${sfc.script.content.substring(args.start, args.end)} as const`); codeGen.addText(`);\n`); } - else if (scriptSetup) { + else if (sfc.scriptSetup) { codeGen.addText(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); } else { @@ -602,7 +597,7 @@ export function generate( const useGlobalThisTypeInCtx = fileName.endsWith('.html'); codeGen.addText(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); - if (scriptSetup) { + if (sfc.scriptSetup) { codeGen.addText(`InstanceType<__VLS_types.PickNotAny {}>> & `); } codeGen.addText(`InstanceType<__VLS_types.PickNotAny {}>> & {\n`); @@ -757,10 +752,10 @@ export function generate( let bindingNames: string[] = []; if (scriptSetupRanges) { - bindingNames = bindingNames.concat(scriptSetupRanges.bindings.map(range => scriptSetup?.content.substring(range.start, range.end) ?? '')); + bindingNames = bindingNames.concat(scriptSetupRanges.bindings.map(range => sfc.scriptSetup?.content.substring(range.start, range.end) ?? '')); } if (scriptRanges) { - bindingNames = bindingNames.concat(scriptRanges.bindings.map(range => script?.content.substring(range.start, range.end) ?? '')); + bindingNames = bindingNames.concat(scriptRanges.bindings.map(range => sfc.script?.content.substring(range.start, range.end) ?? '')); } // fix import components unused report diff --git a/packages/vue-language-core/src/plugins/vue-tsx.ts b/packages/vue-language-core/src/plugins/vue-tsx.ts index f66f7a344..65e7931a5 100644 --- a/packages/vue-language-core/src/plugins/vue-tsx.ts +++ b/packages/vue-language-core/src/plugins/vue-tsx.ts @@ -3,7 +3,10 @@ import { generate as genScript } from '../generators/script'; import * as templateGen from '../generators/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; -import { Sfc, useCssVars, useStyleCssClasses, VueLanguagePlugin } from '../sourceFile'; +import { Sfc, VueLanguagePlugin } from '../sourceFile'; +import { TextRange } from '../types'; +import { parseCssClassNames } from '../utils/parseCssClassNames'; +import { parseCssVars } from '../utils/parseCssVars'; import { SearchTexts } from '../utils/string'; const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOptions }) => { @@ -143,9 +146,8 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption const tsxGen = computed(() => genScript( ts, fileName, + sfc, lang.value, - sfc.script ?? undefined, - sfc.scriptSetup ?? undefined, scriptRanges.value, scriptSetupRanges.value, cssVars.value, @@ -163,3 +165,42 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption } }; export default plugin; + +export function useStyleCssClasses(sfc: Sfc, condition: (style: Sfc['styles'][number]) => boolean) { + return computed(() => { + const result: { + style: typeof sfc.styles[number], + index: number, + classNameRanges: TextRange[], + classNames: string[], + }[] = []; + for (let i = 0; i < sfc.styles.length; i++) { + const style = sfc.styles[i]; + if (condition(style)) { + const classNameRanges = [...parseCssClassNames(style.content)]; + result.push({ + style: style, + index: i, + classNameRanges: classNameRanges, + classNames: classNameRanges.map(range => style.content.substring(range.start + 1, range.end)), + }); + } + } + return result; + }); +} + +export function useCssVars(sfc: Sfc) { + return computed(() => { + const result: { style: typeof sfc.styles[number], styleIndex: number, ranges: TextRange[]; }[] = []; + for (let i = 0; i < sfc.styles.length; i++) { + const style = sfc.styles[i]; + result.push({ + style: style, + styleIndex: i, + ranges: [...parseCssVars(style.content)], + }); + } + return result; + }); +} diff --git a/packages/vue-language-core/src/sourceFile.ts b/packages/vue-language-core/src/sourceFile.ts index cd9232bda..5761d2cb5 100644 --- a/packages/vue-language-core/src/sourceFile.ts +++ b/packages/vue-language-core/src/sourceFile.ts @@ -1,8 +1,6 @@ import { SFCBlock, SFCParseResult, SFCScriptBlock, SFCStyleBlock, SFCTemplateBlock } from '@vue/compiler-sfc'; import { computed, ComputedRef, reactive, ref } from '@vue/reactivity'; -import { EmbeddedFileMappingData, TeleportMappingData, TextRange, VueCompilerOptions, _VueCompilerOptions } from './types'; -import { parseCssClassNames } from './utils/parseCssClassNames'; -import { parseCssVars } from './utils/parseCssVars'; +import { EmbeddedFileMappingData, TeleportMappingData, VueCompilerOptions, _VueCompilerOptions } from './types'; import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps'; import { CodeGen } from '@volar/code-gen'; @@ -17,13 +15,9 @@ export type VueLanguagePlugin = (ctx: { compilerOptions: ts.CompilerOptions, vueCompilerOptions: _VueCompilerOptions, }) => { - parseSFC?(fileName: string, content: string): SFCParseResult | undefined; - compileSFCTemplate?(lang: string, template: string, options?: CompilerDom.CompilerOptions): CompilerDom.CodegenResult | undefined; - getEmbeddedFileNames?(fileName: string, sfc: Sfc): string[]; - resolveEmbeddedFile?(fileName: string, sfc: Sfc, embeddedFile: EmbeddedFile): void; }; @@ -538,45 +532,6 @@ export function createSourceFile( } } -export function useStyleCssClasses(sfc: Sfc, condition: (style: Sfc['styles'][number]) => boolean) { - return computed(() => { - const result: { - style: typeof sfc.styles[number], - index: number, - classNameRanges: TextRange[], - classNames: string[], - }[] = []; - for (let i = 0; i < sfc.styles.length; i++) { - const style = sfc.styles[i]; - if (condition(style)) { - const classNameRanges = [...parseCssClassNames(style.content)]; - result.push({ - style: style, - index: i, - classNameRanges: classNameRanges, - classNames: classNameRanges.map(range => style.content.substring(range.start + 1, range.end)), - }); - } - } - return result; - }); -} - -export function useCssVars(sfc: Sfc) { - return computed(() => { - const result: { style: typeof sfc.styles[number], styleIndex: number, ranges: TextRange[]; }[] = []; - for (let i = 0; i < sfc.styles.length; i++) { - const style = sfc.styles[i]; - result.push({ - style: style, - styleIndex: i, - ranges: [...parseCssVars(style.content)], - }); - } - return result; - }); -} - const validScriptSyntaxs = ['js', 'jsx', 'ts', 'tsx'] as const; type ValidScriptSyntax = typeof validScriptSyntaxs[number]; From 52acb24ceb61852e5cf37a1dc36fac152e7f6881 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 08:46:41 +0800 Subject: [PATCH 8/9] fix: template completion data missing --- packages/vue-language-core/src/plugins/vue-tsx.ts | 2 +- .../src/plugins/vue-autoinsert-parentheses.ts | 4 ++-- packages/vue-language-service/src/plugins/vue-template.ts | 5 +---- packages/vue-language-service/src/vueDocuments.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/vue-language-core/src/plugins/vue-tsx.ts b/packages/vue-language-core/src/plugins/vue-tsx.ts index 65e7931a5..96b620a56 100644 --- a/packages/vue-language-core/src/plugins/vue-tsx.ts +++ b/packages/vue-language-core/src/plugins/vue-tsx.ts @@ -26,7 +26,7 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption } if (sfc.template) { fileNames.push(fileName + '.__VLS_template_format.tsx'); - fileNames.push(fileName + '.__VLS_template.css'); + fileNames.push(fileName + '.__VLS_template_style.css'); } return fileNames; diff --git a/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts b/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts index df0e28c98..193d35b5d 100644 --- a/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts +++ b/packages/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts @@ -25,8 +25,8 @@ export default function (options: { return; const templateFormatScript = vueDocument.file.allEmbeddeds.find(e => - e.file.fileName.endsWith('.__VLS_template.format.tsx') - || e.file.fileName.endsWith('.__VLS_template.format.jsx') + e.file.fileName.endsWith('.__VLS_template_format.tsx') + || e.file.fileName.endsWith('.__VLS_template_format.jsx') ); if (!templateFormatScript) return; diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index f94987c9e..ea8e81a39 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -718,10 +718,7 @@ export default function useVueTemplateLanguagePlugin(); - const file = sourceFile.file.allEmbeddeds.find(e => - e.file.fileName.endsWith('.__VLS_template.tsx') - || e.file.fileName.endsWith('.__VLS_template.jsx') - )?.file; + const file = sourceFile.file.allEmbeddeds.find(e => e.file.fileName === sourceFile.file.tsFileName)?.file; const document = file ? sourceFile.embeddedDocumentsMap.get(file) : undefined; const templateTagNames = [...sourceFile.getTemplateTagsAndAttrs().tags.keys()]; diff --git a/packages/vue-language-service/src/vueDocuments.ts b/packages/vue-language-service/src/vueDocuments.ts index db0b6e712..97e7bc867 100644 --- a/packages/vue-language-service/src/vueDocuments.ts +++ b/packages/vue-language-service/src/vueDocuments.ts @@ -318,7 +318,7 @@ export function parseVueDocument( includeCompletionsWithInsertText: true, // if missing, { 'aaa-bbb': any, ccc: any } type only has result ['ccc'] }; - const file = vueFile.allEmbeddeds.find(e => e.file.fileName.indexOf('.__VLS_template.') >= 0)?.file; + const file = vueFile.allEmbeddeds.find(e => e.file.fileName === vueFile.tsFileName)?.file; if (file && file.codeGen.getText().indexOf(vue.SearchTexts.Components) >= 0) { const document = embeddedDocumentsMap.get(file); From a024bd8d3a356505a1abbcffe444c97e9ba8283d Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 10 Aug 2022 08:52:22 +0800 Subject: [PATCH 9/9] chore: ignore empty test --- packages/vue-language-service/tests/complete.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/vue-language-service/tests/complete.ts b/packages/vue-language-service/tests/complete.ts index 220e48593..5973791cd 100644 --- a/packages/vue-language-service/tests/complete.ts +++ b/packages/vue-language-service/tests/complete.ts @@ -75,6 +75,10 @@ for (const dirName of testDirs) { expect(result.replace(/\r\n/g, '\n')).toBe(expectedFileText.replace(/\r\n/g, '\n')); }); } + + if (!actions.length) { + it(`ignore`); + } } }); }