diff --git a/packages/vue-language-core/src/generators/script.ts b/packages/vue-language-core/src/generators/script.ts index 16ff66443..4e04ac785 100644 --- a/packages/vue-language-core/src/generators/script.ts +++ b/packages/vue-language-core/src/generators/script.ts @@ -1,19 +1,17 @@ -import { getLength, Segment } from '@volar/source-map'; import { FileRangeCapabilities, MirrorBehaviorCapabilities } from '@volar/language-core'; -import type { TextRange } from '../types'; import * as SourceMaps from '@volar/source-map'; -import { hyphenate } from '@vue/shared'; +import { Segment, getLength } from '@volar/source-map'; +import * as muggle from 'muggle-string'; import { posix as path } from 'path'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type * as templateGen from '../generators/template'; import type { ScriptRanges } from '../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; +import type { TextRange, VueCompilerOptions } from '../types'; import { Sfc } from '../types'; -import type { VueCompilerOptions } from '../types'; -import { getSlotsPropertyName } from '../utils/shared'; -import { walkInterpolationFragment } from '../utils/transform'; import * as sharedTypes from '../utils/globalTypes'; -import * as muggle from 'muggle-string'; +import { getSlotsPropertyName, hyphenateTag } from '../utils/shared'; +import { walkInterpolationFragment } from '../utils/transform'; export function generate( ts: typeof import('typescript/lib/tsserverlibrary'), @@ -974,7 +972,7 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?: // fix import components unused report for (const varName of bindingNames) { - if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenate(varName)]) { + if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenateTag(varName)]) { usageVars.add(varName); } } diff --git a/packages/vue-language-core/src/generators/template.ts b/packages/vue-language-core/src/generators/template.ts index b3a154a8e..c802ee17c 100644 --- a/packages/vue-language-core/src/generators/template.ts +++ b/packages/vue-language-core/src/generators/template.ts @@ -1,12 +1,13 @@ -import { Segment } from '@volar/source-map'; import { FileRangeCapabilities } from '@volar/language-core'; +import { Segment } from '@volar/source-map'; import * as CompilerDOM from '@vue/compiler-dom'; -import { camelize, capitalize, hyphenate } from '@vue/shared'; +import { camelize, capitalize } from '@vue/shared'; +import { minimatch } from 'minimatch'; +import * as muggle from 'muggle-string'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { Sfc, VueCompilerOptions } from '../types'; +import { hyphenateAttr, hyphenateTag } from '../utils/shared'; import { colletVars, walkInterpolationFragment } from '../utils/transform'; -import { minimatch } from 'minimatch'; -import * as muggle from 'muggle-string'; const capabilitiesPresets = { all: FileRangeCapabilities.full, @@ -233,7 +234,7 @@ export function generate( ...capabilitiesPresets.tagReference, rename: { normalize: tagName === name ? capabilitiesPresets.tagReference.rename.normalize : camelizeComponentName, - apply: getRenameApply(tagName), + apply: getTagRenameApply(tagName), }, }, ]), @@ -963,8 +964,8 @@ export function generate( }, // onClickOutside -> @click-outside apply(newName) { - const hName = hyphenate(newName); - if (hyphenate(newName).startsWith('on-')) { + const hName = hyphenateAttr(newName); + if (hyphenateAttr(newName).startsWith('on-')) { return camelize(hName.slice('on-'.length)); } return newName; @@ -1188,7 +1189,7 @@ export function generate( if ( (!prop.arg || (prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)) // isStatic - && hyphenate(attrNameText) === attrNameText + && hyphenateAttr(attrNameText) === attrNameText && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern)) ) { attrNameText = camelize(attrNameText); @@ -1222,7 +1223,7 @@ export function generate( ...caps_attr, rename: { normalize: camelize, - apply: camelized ? hyphenate : noEditApply, + apply: camelized ? hyphenateAttr : noEditApply, }, }, ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})), @@ -1238,7 +1239,7 @@ export function generate( ...caps_attr, rename: { normalize: camelize, - apply: camelized ? hyphenate : noEditApply, + apply: camelized ? hyphenateAttr : noEditApply, }, }, ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})), @@ -1294,7 +1295,7 @@ export function generate( let camelized = false; if ( - hyphenate(prop.name) === prop.name + hyphenateAttr(prop.name) === prop.name && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern)) ) { attrNameText = camelize(prop.name); @@ -1317,7 +1318,7 @@ export function generate( ...caps_attr, rename: { normalize: camelize, - apply: camelized ? hyphenate : noEditApply, + apply: camelized ? hyphenateAttr : noEditApply, }, }, ], (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {})) @@ -1479,7 +1480,7 @@ export function generate( }, rename: { normalize: camelize, - apply: getRenameApply(prop.name), + apply: getPropRenameApply(prop.name), }, }, ], @@ -1642,7 +1643,7 @@ export function generate( ...capabilitiesPresets.slotProp, rename: { normalize: camelize, - apply: getRenameApply(prop.arg.content), + apply: getPropRenameApply(prop.arg.content), }, }, ], prop.arg.loc), @@ -1671,7 +1672,7 @@ export function generate( ...capabilitiesPresets.attr, rename: { normalize: camelize, - apply: getRenameApply(prop.name), + apply: getPropRenameApply(prop.name), }, }, ], prop.loc), @@ -1952,8 +1953,12 @@ function camelizeComponentName(newName: string) { return camelize('-' + newName); } -function getRenameApply(oldName: string) { - return oldName === hyphenate(oldName) ? hyphenate : noEditApply; +function getTagRenameApply(oldName: string) { + return oldName === hyphenateTag(oldName) ? hyphenateTag : noEditApply; +} + +function getPropRenameApply(oldName: string) { + return oldName === hyphenateAttr(oldName) ? hyphenateAttr : noEditApply; } function noEditApply(n: string) { @@ -1965,7 +1970,7 @@ function getModelValuePropName(node: CompilerDOM.ElementNode, vueVersion: number for (const modelName in vueCompilerOptions.experimentalModelPropName) { const tags = vueCompilerOptions.experimentalModelPropName[modelName]; for (const tag in tags) { - if (node.tag === tag || node.tag === hyphenate(tag)) { + if (node.tag === tag || node.tag === hyphenateTag(tag)) { const v = tags[tag]; if (typeof v === 'object') { const arr = Array.isArray(v) ? v : [v]; @@ -1991,7 +1996,7 @@ function getModelValuePropName(node: CompilerDOM.ElementNode, vueVersion: number for (const modelName in vueCompilerOptions.experimentalModelPropName) { const tags = vueCompilerOptions.experimentalModelPropName[modelName]; for (const tag in tags) { - if (node.tag === tag || node.tag === hyphenate(tag)) { + if (node.tag === tag || node.tag === hyphenateTag(tag)) { const attrs = tags[tag]; if (attrs === true) { return modelName || undefined; diff --git a/packages/vue-language-core/src/index.ts b/packages/vue-language-core/src/index.ts index 968eacf09..a1651daeb 100644 --- a/packages/vue-language-core/src/index.ts +++ b/packages/vue-language-core/src/index.ts @@ -9,6 +9,7 @@ export * from './utils/parseSfc'; export * as scriptRanges from './parsers/scriptRanges'; export * as sharedTypes from './utils/globalTypes'; +export * from './utils/shared'; export { tsCodegen } from './plugins/vue-tsx'; export * from '@volar/language-core'; diff --git a/packages/vue-language-core/src/languageModule.ts b/packages/vue-language-core/src/languageModule.ts index 90ded3be9..be17351be 100644 --- a/packages/vue-language-core/src/languageModule.ts +++ b/packages/vue-language-core/src/languageModule.ts @@ -115,7 +115,7 @@ export function createVueLanguage( } /** - * @deprecated planed to remove in 2.0, please use getOrCreateVueLanguage instead of + * @deprecated planed to remove in 2.0, please use createVueLanguage instead of */ export function createLanguages( compilerOptions: ts.CompilerOptions = {}, diff --git a/packages/vue-language-core/src/utils/shared.ts b/packages/vue-language-core/src/utils/shared.ts index abd48fe5b..b52f7a586 100644 --- a/packages/vue-language-core/src/utils/shared.ts +++ b/packages/vue-language-core/src/utils/shared.ts @@ -1,3 +1,16 @@ +import { hyphenate } from '@vue/shared'; + export function getSlotsPropertyName(vueVersion: number) { return vueVersion < 3 ? '$scopedSlots' : '$slots'; } + +export { hyphenate as hyphenateTag } from '@vue/shared'; + +export function hyphenateAttr(str: string) { + let hyphencase = hyphenate(str); + // fix https://github.com/vuejs/core/issues/8811 + if (str.length && str[0] !== str[0].toLowerCase()) { + hyphencase = '-' + hyphencase; + } + return hyphencase; +} diff --git a/packages/vue-language-service/src/ideFeatures/nameCasing.ts b/packages/vue-language-service/src/ideFeatures/nameCasing.ts index 3d0ebed33..d64264b90 100644 --- a/packages/vue-language-service/src/ideFeatures/nameCasing.ts +++ b/packages/vue-language-service/src/ideFeatures/nameCasing.ts @@ -1,21 +1,20 @@ -import { hyphenate } from '@vue/shared'; import { ServiceContext, VirtualFile } from '@volar/language-service'; -import { getComponentNames, getTemplateTagsAndAttrs, getPropsByTag } from '../helpers'; -import * as vue from '@vue/language-core'; +import { VueCompilerOptions, VueFile, hyphenateAttr, hyphenateTag } from '@vue/language-core'; +import type { Provide } from 'volar-service-typescript'; import type * as vscode from 'vscode-languageserver-protocol'; +import { getComponentNames, getPropsByTag, getTemplateTagsAndAttrs } from '../helpers'; import { AttrNameCasing, TagNameCasing } from '../types'; -import type { Provide } from 'volar-service-typescript'; export async function convertTagName( ts: typeof import('typescript/lib/tsserverlibrary'), context: ServiceContext, uri: string, casing: TagNameCasing, - vueCompilerOptions: vue.VueCompilerOptions, + vueCompilerOptions: VueCompilerOptions, ) { const rootFile = context.documents.getSourceByUri(uri)?.root; - if (!(rootFile instanceof vue.VueFile)) + if (!(rootFile instanceof VueFile)) return; const desc = rootFile.sfc; @@ -30,14 +29,14 @@ export async function convertTagName( const tags = getTemplateTagsAndAttrs(rootFile); for (const [tagName, { offsets }] of tags) { - const componentName = components.find(component => component === tagName || hyphenate(component) === tagName); + const componentName = components.find(component => component === tagName || hyphenateTag(component) === tagName); if (componentName) { for (const offset of offsets) { const start = document.positionAt(template.startTagEnd + offset); const end = document.positionAt(template.startTagEnd + offset + tagName.length); const range: vscode.Range = { start, end }; - if (casing === TagNameCasing.Kebab && tagName !== hyphenate(componentName)) { - edits.push({ range, newText: hyphenate(componentName) }); + if (casing === TagNameCasing.Kebab && tagName !== hyphenateTag(componentName)) { + edits.push({ range, newText: hyphenateTag(componentName) }); } if (casing === TagNameCasing.Pascal && tagName !== componentName) { edits.push({ range, newText: componentName }); @@ -54,11 +53,11 @@ export async function convertAttrName( context: ServiceContext, uri: string, casing: AttrNameCasing, - vueCompilerOptions: vue.VueCompilerOptions, + vueCompilerOptions: VueCompilerOptions, ) { const rootFile = context.documents.getSourceByUri(uri)?.root; - if (!(rootFile instanceof vue.VueFile)) + if (!(rootFile instanceof VueFile)) return; const desc = rootFile.sfc; @@ -73,18 +72,18 @@ export async function convertAttrName( const tags = getTemplateTagsAndAttrs(rootFile); for (const [tagName, { attrs }] of tags) { - const componentName = components.find(component => component === tagName || hyphenate(component) === tagName); + const componentName = components.find(component => component === tagName || hyphenateTag(component) === tagName); if (componentName) { const props = getPropsByTag(ts, languageService, rootFile, componentName, vueCompilerOptions); for (const [attrName, { offsets }] of attrs) { - const propName = props.find(prop => prop === attrName || hyphenate(prop) === attrName); + const propName = props.find(prop => prop === attrName || hyphenateAttr(prop) === attrName); if (propName) { for (const offset of offsets) { const start = document.positionAt(template.startTagEnd + offset); const end = document.positionAt(template.startTagEnd + offset + attrName.length); const range: vscode.Range = { start, end }; - if (casing === AttrNameCasing.Kebab && attrName !== hyphenate(propName)) { - edits.push({ range, newText: hyphenate(propName) }); + if (casing === AttrNameCasing.Kebab && attrName !== hyphenateAttr(propName)) { + edits.push({ range, newText: hyphenateAttr(propName) }); } if (casing === AttrNameCasing.Camel && attrName !== propName) { edits.push({ range, newText: propName }); @@ -102,7 +101,7 @@ export async function getNameCasing( ts: typeof import('typescript/lib/tsserverlibrary'), context: ServiceContext, uri: string, - vueCompilerOptions: vue.VueCompilerOptions, + vueCompilerOptions: VueCompilerOptions, ) { const detected = detect(ts, context, uri, vueCompilerOptions); @@ -123,14 +122,14 @@ export function detect( ts: typeof import('typescript/lib/tsserverlibrary'), context: ServiceContext, uri: string, - vueCompilerOptions: vue.VueCompilerOptions, + vueCompilerOptions: VueCompilerOptions, ): { tag: TagNameCasing[], attr: AttrNameCasing[], } { const rootFile = context.documents.getSourceByUri(uri)?.root; - if (!(rootFile instanceof vue.VueFile)) { + if (!(rootFile instanceof VueFile)) { return { tag: [], attr: [], @@ -152,7 +151,7 @@ export function detect( for (const [_, { attrs }] of tags) { for (const [tagName] of attrs) { // attrName - if (tagName !== hyphenate(tagName)) { + if (tagName !== hyphenateTag(tagName)) { result.push(AttrNameCasing.Camel); break; } @@ -177,7 +176,7 @@ export function detect( let anyComponentUsed = false; for (const component of components) { - if (tagNames.has(component) || tagNames.has(hyphenate(component))) { + if (tagNames.has(component) || tagNames.has(hyphenateTag(component))) { anyComponentUsed = true; break; } @@ -188,7 +187,7 @@ export function detect( for (const [tagName] of tagNames) { // TagName - if (tagName !== hyphenate(tagName)) { + if (tagName !== hyphenateTag(tagName)) { result.push(TagNameCasing.Pascal); break; } @@ -196,7 +195,7 @@ export function detect( for (const component of components) { // Tagname -> tagname // TagName -> tag-name - if (component !== hyphenate(component) && tagNames.has(hyphenate(component))) { + if (component !== hyphenateTag(component) && tagNames.has(hyphenateTag(component))) { result.push(TagNameCasing.Kebab); break; } diff --git a/packages/vue-language-service/src/languageService.ts b/packages/vue-language-service/src/languageService.ts index 0a22df912..6aaf15401 100644 --- a/packages/vue-language-service/src/languageService.ts +++ b/packages/vue-language-service/src/languageService.ts @@ -1,6 +1,6 @@ import { Config, Service, ServiceContext } from '@volar/language-service'; -import * as vue from '@vue/language-core'; -import { capitalize, hyphenate } from '@vue/shared'; +import { VueFile, createLanguages, hyphenateTag, resolveVueCompilerOptions, scriptRanges } from '@vue/language-core'; +import { capitalize } from '@vue/shared'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type { Data } from 'volar-service-typescript/out/features/completions/basic'; import type * as html from 'vscode-html-languageservice'; @@ -24,12 +24,12 @@ import * as AutoDotValueService from './plugins/vue-autoinsert-dotvalue'; import * as AutoWrapParenthesesService from './plugins/vue-autoinsert-parentheses'; import * as AutoAddSpaceService from './plugins/vue-autoinsert-space'; import * as ReferencesCodeLensService from './plugins/vue-codelens-references'; -import * as VueTemplateLanguageService from './plugins/vue-template'; -import * as VueTqService from './plugins/vue-twoslash-queries'; -import * as VisualizeHiddenCallbackParamService from './plugins/vue-visualize-hidden-callback-param'; import * as DirectiveCommentsService from './plugins/vue-directive-comments'; import * as ExtractComponentService from './plugins/vue-extract-file'; +import * as VueTemplateLanguageService from './plugins/vue-template'; import * as ToggleVBindService from './plugins/vue-toggle-v-bind-codeaction'; +import * as VueTqService from './plugins/vue-twoslash-queries'; +import * as VisualizeHiddenCallbackParamService from './plugins/vue-visualize-hidden-callback-param'; export interface Settings { json?: Parameters[0]; @@ -38,13 +38,13 @@ export interface Settings { export function resolveConfig( config: Config, compilerOptions: ts.CompilerOptions = {}, - vueCompilerOptions: Partial = {}, + vueCompilerOptions: Partial = {}, ts: typeof import('typescript/lib/tsserverlibrary') = require('typescript'), codegenStack: boolean = false, ) { - const resolvedVueCompilerOptions = vue.resolveVueCompilerOptions(vueCompilerOptions); - const vueLanguageModules = vue.createLanguages(compilerOptions, resolvedVueCompilerOptions, ts, codegenStack); + const resolvedVueCompilerOptions = resolveVueCompilerOptions(vueCompilerOptions); + const vueLanguageModules = createLanguages(compilerOptions, resolvedVueCompilerOptions, ts, codegenStack); config.languages = Object.assign({}, vueLanguageModules, config.languages); config.services = resolvePlugins(config.services, resolvedVueCompilerOptions); @@ -86,7 +86,7 @@ function resolvePlugins( for (const [_, map] of ctx.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = ctx.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (virtualFile instanceof vue.VueFile) { + if (virtualFile instanceof VueFile) { const isAutoImport = !!map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly); if (isAutoImport) { const source = ctx.documents.getVirtualFileByUri(document.uri)[1]; @@ -99,7 +99,7 @@ function resolvePlugins( casing ??= await getNameCasing(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions); if (casing.tag === TagNameCasing.Kebab) { for (const item of result.items) { - item.filterText = hyphenate(item.filterText ?? item.label); + item.filterText = hyphenateTag(item.filterText ?? item.label); } } } @@ -150,7 +150,7 @@ function resolvePlugins( if (source) { const casing = await getNameCasing(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions); if (casing.tag === TagNameCasing.Kebab) { - item.textEdit.newText = hyphenate(item.textEdit.newText); + item.textEdit.newText = hyphenateTag(item.textEdit.newText); } } } @@ -166,7 +166,7 @@ function resolvePlugins( const langaugeService = ctx.inject('typescript/languageService'); const [virtualFile] = ctx.virtualFiles.getVirtualFile(fileName); const ast = langaugeService.getProgram()?.getSourceFile(fileName); - const exportDefault = ast ? vue.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined; + const exportDefault = ast ? scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined; if (virtualFile && ast && exportDefault) { const componentName = newName ?? item.textEdit.newText; const optionEdit = ExtractComponentService.createAddComponentToOptionEdit(ts, ast, componentName); diff --git a/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts b/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts index 91102b093..336863dff 100644 --- a/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts +++ b/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts @@ -1,5 +1,5 @@ import { AutoInsertionContext, Service, ServiceContext } from '@volar/language-service'; -import { hyphenate } from '@vue/shared'; +import { hyphenateAttr } from '@vue/language-core'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type * as vscode from 'vscode-languageserver-protocol'; import type { TextDocument } from 'vscode-languageserver-textdocument'; @@ -165,7 +165,7 @@ export function isBlacklistNode(ts: typeof import('typescript/lib/tsserverlibrar || fnName === 'unref' || fnName === 'triggerRef' || fnName === 'isRef' - || hyphenate(fnName).startsWith('use-'); + || hyphenateAttr(fnName).startsWith('use-'); } function isTopLevelArgOrArrayTopLevelItemItem(node: ts.CallExpression) { for (const arg of node.arguments) { diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 88a514fd4..17a098b1a 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -1,13 +1,13 @@ import { FileRangeCapabilities, Service, ServiceContext, SourceMapWithDocuments } from '@volar/language-service'; -import * as vue from '@vue/language-core'; -import { hyphenate, capitalize, camelize } from '@vue/shared'; +import { VueFile, hyphenateAttr, hyphenateTag, parseScriptSetupRanges, tsCodegen } from '@vue/language-core'; +import { camelize, capitalize } from '@vue/shared'; import * as html from 'vscode-html-languageservice'; import type * as vscode from 'vscode-languageserver-protocol'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { getComponentNames, getEventsOfTag, getPropsByTag, getElementAttrs, getTemplateCtx } from '../helpers'; +import { getComponentNames, getElementAttrs, getEventsOfTag, getPropsByTag, getTemplateCtx } from '../helpers'; import { getNameCasing } from '../ideFeatures/nameCasing'; -import { AttrNameCasing, VueCompilerOptions, TagNameCasing } from '../types'; -import { loadTemplateData, loadModelModifiersData } from './data'; +import { AttrNameCasing, TagNameCasing, VueCompilerOptions } from '../types'; +import { loadModelModifiersData, loadTemplateData } from './data'; let builtInData: html.HTMLDataV1; let modelData: html.HTMLDataV1; @@ -77,7 +77,7 @@ export const create = (options: { for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (virtualFile && virtualFile instanceof vue.VueFile) { + if (virtualFile && virtualFile instanceof VueFile) { await provideHtmlData(map, virtualFile); } } @@ -88,7 +88,7 @@ export const create = (options: { for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (virtualFile && virtualFile instanceof vue.VueFile) { + if (virtualFile && virtualFile instanceof VueFile) { afterHtmlCompletion(htmlComplete, map, virtualFile); } } @@ -111,7 +111,7 @@ export const create = (options: { for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; const scanner = options.getScanner(htmlOrPugService, document); - if (virtualFile && virtualFile instanceof vue.VueFile && scanner) { + if (virtualFile && virtualFile instanceof VueFile && scanner) { // visualize missing required props const casing = await getNameCasing(ts, _context, map.sourceFileDocument.uri, options.vueCompilerOptions); @@ -129,7 +129,7 @@ export const create = (options: { const component = tagName.indexOf('.') >= 0 ? components.find(component => component === tagName.split('.')[0]) - : components.find(component => component === tagName || hyphenate(component) === tagName); + : components.find(component => component === tagName || hyphenateTag(component) === tagName); const checkTag = tagName.indexOf('.') >= 0 ? tagName : component; if (checkTag) { componentProps[checkTag] ??= getPropsByTag(ts, languageService, virtualFile, checkTag, options.vueCompilerOptions, true); @@ -166,12 +166,12 @@ export const create = (options: { attrText = options.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName? } else if (attrText.startsWith('@')) { - attrText = 'on-' + hyphenate(attrText.substring('@'.length)); + attrText = 'on-' + hyphenateAttr(attrText.substring('@'.length)); } current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => { return attrText !== propName - && attrText !== hyphenate(propName); + && attrText !== hyphenateAttr(propName); }); } } @@ -189,7 +189,7 @@ export const create = (options: { start: document.positionAt(current.insertOffset), end: document.positionAt(current.insertOffset), }, - newText: ` :${casing.attr === AttrNameCasing.Kebab ? hyphenate(requiredProp) : requiredProp}=`, + newText: ` :${casing.attr === AttrNameCasing.Kebab ? hyphenateAttr(requiredProp) : requiredProp}=`, }], }); } @@ -229,7 +229,7 @@ export const create = (options: { for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (!virtualFile || !(virtualFile instanceof vue.VueFile)) + if (!virtualFile || !(virtualFile instanceof VueFile)) continue; const templateErrors: vscode.Diagnostic[] = []; @@ -288,13 +288,13 @@ export const create = (options: { for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; - if (!virtualFile || !(virtualFile instanceof vue.VueFile)) + if (!virtualFile || !(virtualFile instanceof VueFile)) continue; const templateScriptData = getComponentNames(ts, languageService, virtualFile, options.vueCompilerOptions); const components = new Set([ ...templateScriptData, - ...templateScriptData.map(hyphenate), + ...templateScriptData.map(hyphenateTag), ]); const offsetRange = { start: document.offsetAt(range.start), @@ -337,7 +337,7 @@ export const create = (options: { }, }; - async function provideHtmlData(map: SourceMapWithDocuments, vueSourceFile: vue.VueFile) { + async function provideHtmlData(map: SourceMapWithDocuments, vueSourceFile: VueFile) { const languageService = _context!.inject('typescript/languageService'); const languageServiceHost = _context!.inject('typescript/languageServiceHost'); @@ -352,7 +352,7 @@ export const create = (options: { if (tag.name === 'template') continue; if (casing.tag === TagNameCasing.Kebab) { - tag.name = hyphenate(tag.name); + tag.name = hyphenateTag(tag.name); } else { tag.name = camelize(capitalize(tag.name)); @@ -375,13 +375,13 @@ export const create = (options: { && name !== 'Suspense' && name !== 'Teleport' ); - const scriptSetupRanges = vueSourceFile.sfc.scriptSetupAst ? vue.parseScriptSetupRanges(ts, vueSourceFile.sfc.scriptSetupAst, options.vueCompilerOptions) : undefined; + const scriptSetupRanges = vueSourceFile.sfc.scriptSetupAst ? parseScriptSetupRanges(ts, vueSourceFile.sfc.scriptSetupAst, options.vueCompilerOptions) : undefined; const names = new Set(); const tags: html.ITagData[] = []; for (const tag of components) { if (casing.tag === TagNameCasing.Kebab) { - names.add(hyphenate(tag)); + names.add(hyphenateTag(tag)); } else if (casing.tag === TagNameCasing.Pascal) { names.add(tag); @@ -391,7 +391,7 @@ export const create = (options: { for (const binding of scriptSetupRanges?.bindings ?? []) { const name = vueSourceFile.sfc.scriptSetup!.content.substring(binding.start, binding.end); if (casing.tag === TagNameCasing.Kebab) { - names.add(hyphenate(name)); + names.add(hyphenateTag(name)); } else if (casing.tag === TagNameCasing.Pascal) { names.add(name); @@ -413,16 +413,16 @@ export const create = (options: { const props = new Set(getPropsByTag(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions)); const events = getEventsOfTag(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions); const attributes: html.IAttributeData[] = []; - const tsCodegen = vue.tsCodegen.get(vueSourceFile.sfc); + const _tsCodegen = tsCodegen.get(vueSourceFile.sfc); - if (tsCodegen) { + if (_tsCodegen) { let ctxVars = [ - ...tsCodegen.scriptRanges.value?.bindings.map(binding => vueSourceFile.sfc.script!.content.substring(binding.start, binding.end)) ?? [], - ...tsCodegen.scriptSetupRanges.value?.bindings.map(binding => vueSourceFile.sfc.scriptSetup!.content.substring(binding.start, binding.end)) ?? [], + ..._tsCodegen.scriptRanges.value?.bindings.map(binding => vueSourceFile.sfc.script!.content.substring(binding.start, binding.end)) ?? [], + ..._tsCodegen.scriptSetupRanges.value?.bindings.map(binding => vueSourceFile.sfc.scriptSetup!.content.substring(binding.start, binding.end)) ?? [], ...getTemplateCtx(ts, languageService, vueSourceFile) ?? [], ]; ctxVars = [...new Set(ctxVars)]; - const dirs = ctxVars.map(hyphenate).filter(v => v.startsWith('v-')); + const dirs = ctxVars.map(hyphenateAttr).filter(v => v.startsWith('v-')); for (const dir of dirs) { attributes.push( { @@ -435,9 +435,9 @@ export const create = (options: { for (const prop of [...props, ...attrs]) { const isGlobal = !props.has(prop); - const name = casing.attr === AttrNameCasing.Camel ? prop : hyphenate(prop); + const name = casing.attr === AttrNameCasing.Camel ? prop : hyphenateAttr(prop); - if (hyphenate(name).startsWith('on-')) { + if (hyphenateAttr(name).startsWith('on-')) { const propNameBase = name.startsWith('on-') ? name.slice('on-'.length) @@ -479,7 +479,7 @@ export const create = (options: { for (const event of events) { - const name = casing.attr === AttrNameCasing.Camel ? event : hyphenate(event); + const name = casing.attr === AttrNameCasing.Camel ? event : hyphenateAttr(event); const propKey = createInternalItemId('componentEvent', [tag, name]); attributes.push({ @@ -508,7 +508,7 @@ export const create = (options: { for (const [isGlobal, model] of models) { - const name = casing.attr === AttrNameCasing.Camel ? model : hyphenate(model); + const name = casing.attr === AttrNameCasing.Camel ? model : hyphenateAttr(model); const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, name]); attributes.push({ @@ -531,11 +531,11 @@ export const create = (options: { ]); } - function afterHtmlCompletion(completionList: vscode.CompletionList, map: SourceMapWithDocuments, vueSourceFile: vue.VueFile) { + function afterHtmlCompletion(completionList: vscode.CompletionList, map: SourceMapWithDocuments, vueSourceFile: VueFile) { const languageService = _context!.inject('typescript/languageService'); const replacement = getReplacement(completionList, map.sourceFileDocument); - const componentNames = new Set(getComponentNames(ts, languageService, vueSourceFile, options.vueCompilerOptions).map(hyphenate)); + const componentNames = new Set(getComponentNames(ts, languageService, vueSourceFile, options.vueCompilerOptions).map(hyphenateTag)); if (replacement) { @@ -613,7 +613,7 @@ export const create = (options: { item.documentation = undefined; } - if (item.kind === 10 satisfies typeof vscode.CompletionItemKind.Property && componentNames.has(hyphenate(item.label))) { + if (item.kind === 10 satisfies typeof vscode.CompletionItemKind.Property && componentNames.has(hyphenateTag(item.label))) { item.kind = 6 satisfies typeof vscode.CompletionItemKind.Variable; item.sortText = '\u0000' + (item.sortText ?? item.label); } diff --git a/packages/vue-test-workspace/complete/core#8811/input/entry.vue b/packages/vue-test-workspace/complete/core#8811/input/entry.vue new file mode 100644 index 000000000..68b817417 --- /dev/null +++ b/packages/vue-test-workspace/complete/core#8811/input/entry.vue @@ -0,0 +1,12 @@ + + + diff --git a/packages/vue-test-workspace/complete/core#8811/output/entry.vue b/packages/vue-test-workspace/complete/core#8811/output/entry.vue new file mode 100644 index 000000000..e6ed09a9a --- /dev/null +++ b/packages/vue-test-workspace/complete/core#8811/output/entry.vue @@ -0,0 +1,12 @@ + + +