Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/vue-component-meta/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function createComponentMetaCheckerWorker(
};

return {
...baseCreate(_host, parsedCommandLine.vueOptions, checkerOptions, globalComponentName, ts),
...baseCreate(_host, vue.resolveVueCompilerOptions(parsedCommandLine.vueOptions), checkerOptions, globalComponentName, ts),
updateFile(fileName: string, text: string) {
fileName = (fileName as path.OsPath).replace(/\\/g, '/') as path.PosixPath;
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(text));
Expand All @@ -110,12 +110,11 @@ function createComponentMetaCheckerWorker(

export function baseCreate(
_host: vue.TypeScriptLanguageHost,
_vueCompilerOptions: Partial<vue.VueCompilerOptions>,
vueCompilerOptions: vue.VueCompilerOptions,
checkerOptions: MetaCheckerOptions,
globalComponentName: string,
ts: typeof import('typescript/lib/tsserverlibrary'),
) {
const vueCompilerOptions = vue.resolveVueCompilerOptions(_vueCompilerOptions);
const globalComponentSnapshot = ts.ScriptSnapshot.fromString('<script setup lang="ts"></script>');
const metaSnapshots: Record<string, ts.IScriptSnapshot> = {};
const host = new Proxy<Partial<vue.TypeScriptLanguageHost>>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@
"jsxTemplates": {
"deprecated": true,
"description": "Deprecated since v1.5.0."
},
"nativeTags": {
"deprecated": true,
"description": "Deprecated since v1.5.1."
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions packages/vue-language-core/schemas/vue-tsconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
"type": "boolean",
"markdownDescription": "https://github.com/vuejs/language-tools/issues/577"
},
"nativeTags": {
"type": "array",
"default": [
"div",
"img",
"..."
],
"markdownDescription": "List of valid intrinsic elements."
},
"dataAttributes": {
"type": "array",
"default": [ ],
Expand Down
95 changes: 63 additions & 32 deletions packages/vue-language-core/src/generators/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function generate(
codegenStack: boolean,
) {

const nativeTags = new Set(vueCompilerOptions.nativeTags);
const [codes, codeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
const [formatCodes, formatCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
const [cssCodes, cssCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
Expand Down Expand Up @@ -179,39 +180,38 @@ export function generate(

const data: Record<string, string> = {};

codes.push(`let __VLS_templateComponents!: __VLS_IntrinsicElements\n`);
codes.push(`let __VLS_templateComponents!: {}\n`);

for (const tagName in tagNames) {

if (nativeTags.has(tagName))
continue;

const isNamespacedTag = tagName.indexOf('.') >= 0;
if (isNamespacedTag)
continue;

const varName = validTsVar.test(tagName) ? tagName : capitalize(camelize(tagName.replace(/:/g, '-')));
const validName = validTsVar.test(tagName) ? tagName : capitalize(camelize(tagName.replace(/:/g, '-')));

codes.push(
`& __VLS_WithComponent<'${varName}', typeof __VLS_components, `,
`& __VLS_WithComponent<'${validName}', typeof __VLS_components, `,
// order is important: https://github.com/vuejs/language-tools/issues/2010
`"${capitalize(camelize(tagName))}", `,
`"${camelize(tagName)}", `,
`"${tagName}"`,
'>\n',
);

data[tagName] = varName;
data[tagName] = validName;
}

codes.push(`;\n`);

for (const tagName in tagNames) {

const varName = data[tagName];
if (!varName)
continue;

const tagOffsets = tagNames[tagName];
const tagRanges: [number, number][] = tagOffsets.map(offset => [offset, offset + tagName.length]);
const names = new Set([
const names = new Set(nativeTags.has(tagName) ? [tagName] : [
// order is important: https://github.com/vuejs/language-tools/issues/2010
capitalize(camelize(tagName)),
camelize(tagName),
Expand All @@ -221,7 +221,7 @@ export function generate(
for (const name of names) {
for (const tagRange of tagRanges) {
codes.push(
name === tagName ? '__VLS_templateComponents' : '__VLS_components',
nativeTags.has(tagName) ? '({} as __VLS_IntrinsicElements)' : '__VLS_components',
...createPropertyAccessCode([
name,
'template',
Expand All @@ -240,25 +240,29 @@ export function generate(
}
codes.push('\n');

codes.push(
'// @ts-ignore\n', // #2304
'[',
);
for (const tagRange of tagRanges) {
codes.push([
varName,
'template',
tagRange,
{
completion: {
additional: true,
autoImportOnly: true,
const validName = data[tagName];

if (validName) {
codes.push(
'// @ts-ignore\n', // #2304
'[',
);
for (const tagRange of tagRanges) {
codes.push([
validName,
'template',
tagRange,
{
completion: {
additional: true,
autoImportOnly: true,
},
},
},
]);
codes.push(',');
]);
codes.push(',');
}
codes.push(`];\n`);
}
codes.push(`];\n`);
}

return data;
Expand Down Expand Up @@ -635,7 +639,23 @@ export function generate(
}
}

if (isNamespacedTag) {
const isIntrinsicElement = nativeTags.has(tag) && tagOffsets.length;

if (isIntrinsicElement) {
codes.push(
'const ',
var_originalComponent,
` = ({} as __VLS_IntrinsicElements)[`,
...createStringLiteralKeyCode([
tag,
'template',
tagOffsets[0],
capabilitiesPresets.diagnosticOnly,
]),
'];\n',
);
}
else if (isNamespacedTag) {
codes.push(
`const ${var_originalComponent} = `,
...createInterpolationCode(tag, node.loc, startTagOffset, capabilitiesPresets.all, '', ''),
Expand All @@ -659,10 +679,18 @@ export function generate(
codes.push(
`const ${var_functionalComponent} = __VLS_asFunctionalComponent(`,
`${var_originalComponent}, `,
`new ${var_originalComponent}({`,
...createPropsCode(node, props, 'extraReferences'),
'}));\n',
);
if (isIntrinsicElement) {
codes.push('{}');
}
else {
codes.push(
`new ${var_originalComponent}({`,
...createPropsCode(node, props, 'extraReferences'),
'})',
);
}
codes.push(');\n');

for (const offset of tagOffsets) {
if (isNamespacedTag) {
Expand All @@ -672,7 +700,10 @@ export function generate(
continue;
}
else {
if (componentVars[tag]) {
if (isIntrinsicElement) {
codes.push(`({} as __VLS_IntrinsicElements).`);
}
else {
codes.push(`__VLS_templateComponents.`);
}
codes.push(
Expand Down
1 change: 1 addition & 0 deletions packages/vue-language-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface VueCompilerOptions {
jsxSlots: boolean;
strictTemplates: boolean;
skipTemplateCodegen: boolean;
nativeTags: string[];
dataAttributes: string[];
htmlAttributes: string[];
optionsWrapper: [string, string] | [];
Expand Down
33 changes: 33 additions & 0 deletions packages/vue-language-core/src/utils/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,31 @@ function getPartialVueCompilerOptions(
}
}

// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
const HTML_TAGS =
'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
'header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' +
'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' +
'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' +
'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' +
'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' +
'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' +
'option,output,progress,select,textarea,details,dialog,menu,' +
'summary,template,blockquote,iframe,tfoot';

// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
const SVG_TAGS =
'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' +
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' +
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' +
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' +
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' +
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' +
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' +
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' +
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' +
'text,textPath,title,tspan,unknown,use,view';

export function resolveVueCompilerOptions(vueOptions: Partial<VueCompilerOptions>): VueCompilerOptions {
const target = vueOptions.target ?? 3.3;
const lib = vueOptions.lib || (target < 2.7 ? '@vue/runtime-dom' : 'vue');
Expand All @@ -212,6 +237,14 @@ export function resolveVueCompilerOptions(vueOptions: Partial<VueCompilerOptions
jsxSlots: vueOptions.jsxSlots ?? false,
strictTemplates: vueOptions.strictTemplates ?? false,
skipTemplateCodegen: vueOptions.skipTemplateCodegen ?? false,
nativeTags: vueOptions.nativeTags ?? [...new Set([
...HTML_TAGS.split(','),
...SVG_TAGS.split(','),
// fix https://github.com/johnsoncodehk/volar/issues/1340
'hgroup',
'slot',
'component',
])],
dataAttributes: vueOptions.dataAttributes ?? [],
htmlAttributes: vueOptions.htmlAttributes ?? ['aria-*'],
optionsWrapper: vueOptions.optionsWrapper ?? (
Expand Down
10 changes: 5 additions & 5 deletions packages/vue-language-server/src/languageServerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function createServerPlugin(connection: Connection) {

const ts = modules.typescript;
const vueFileExtensions: string[] = ['vue'];
const hostToVueOptions = new WeakMap<embedded.TypeScriptLanguageHost, Partial<VueCompilerOptions>>();
const hostToVueOptions = new WeakMap<embedded.TypeScriptLanguageHost, VueCompilerOptions>();

if (initOptions.additionalExtensions) {
for (const additionalExtension of initOptions.additionalExtensions) {
Expand All @@ -38,7 +38,7 @@ export function createServerPlugin(connection: Connection) {
const vueLanguageServiceSettings = getVueLanguageServiceSettings();

if (ctx) {
hostToVueOptions.set(ctx.host, vueOptions);
hostToVueOptions.set(ctx.host, vue.resolveVueCompilerOptions(vueOptions));
}

return vue.resolveConfig(
Expand Down Expand Up @@ -109,14 +109,14 @@ export function createServerPlugin(connection: Connection) {
connection.onRequest(DetectNameCasingRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
return nameCasing.detect(ts, languageService.context, params.textDocument.uri);
return nameCasing.detect(ts, languageService.context, params.textDocument.uri, hostToVueOptions.get(languageService.context.rawHost)!);
}
});

connection.onRequest(GetConvertTagCasingEditsRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
return nameCasing.convertTagName(ts, languageService.context, params.textDocument.uri, params.casing);
return nameCasing.convertTagName(ts, languageService.context, params.textDocument.uri, params.casing, hostToVueOptions.get(languageService.context.rawHost)!);
}
});

Expand All @@ -125,7 +125,7 @@ export function createServerPlugin(connection: Connection) {
if (languageService) {
const vueOptions = hostToVueOptions.get(languageService.context.host);
if (vueOptions) {
return nameCasing.convertAttrName(ts, languageService.context, params.textDocument.uri, params.casing);
return nameCasing.convertAttrName(ts, languageService.context, params.textDocument.uri, params.casing, hostToVueOptions.get(languageService.context.rawHost)!);
}
}
});
Expand Down
Loading