Skip to content

Commit

Permalink
refactor: deprecate nativeTags (#2685)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Apr 23, 2023
1 parent 4e2ea5b commit 6d88299
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 128 deletions.
Expand Up @@ -61,7 +61,12 @@
"deprecated": true
},
"jsxTemplates": {
"deprecated": true
"deprecated": true,
"description": "Deprecated since v1.5.0."
},
"nativeTags": {
"deprecated": true,
"description": "Deprecated since v1.5.1."
}
}
}
Expand Down
9 changes: 0 additions & 9 deletions packages/vue-language-core/schemas/vue-tsconfig.schema.json
Expand Up @@ -28,15 +28,6 @@
"type": "boolean",
"markdownDescription": "https://github.com/johnsoncodehk/volar/issues/577"
},
"nativeTags": {
"type": "array",
"default": [
"div",
"img",
"..."
],
"markdownDescription": "List of valid intrinsic elements."
},
"dataAttributes": {
"type": "array",
"default": [ ],
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-language-core/src/generators/script.ts
Expand Up @@ -812,7 +812,7 @@ declare function defineProp<T>(value?: T | (() => T), required?: boolean, rest?:
codes.push('/* Components */\n');
codes.push(`let __VLS_localComponents!: NonNullable<typeof __VLS_internalComponent extends { components: infer C } ? C : {}> & typeof __VLS_componentsOption & typeof __VLS_ctx;\n`);
codes.push(`let __VLS_otherComponents!: typeof __VLS_localComponents & import('./__VLS_types').GlobalComponents;\n`);
codes.push(`let __VLS_own!: import('./__VLS_types').SelfComponent<typeof __VLS_name, typeof __VLS_internalComponent & typeof __VLS_publicComponent & (new () => { ${getSlotsPropertyName(vueCompilerOptions.target ?? 3)}: typeof __VLS_slots })>;\n`);
codes.push(`let __VLS_own!: import('./__VLS_types').SelfComponent<typeof __VLS_name, typeof __VLS_internalComponent & typeof __VLS_publicComponent & (new () => { ${getSlotsPropertyName(vueCompilerOptions.target)}: typeof __VLS_slots })>;\n`);
codes.push(`let __VLS_components!: typeof __VLS_otherComponents & Omit<typeof __VLS_own, keyof typeof __VLS_otherComponents>;\n`);

/* Style Scoped */
Expand Down
55 changes: 8 additions & 47 deletions packages/vue-language-core/src/generators/template.ts
Expand Up @@ -59,7 +59,6 @@ export function generate(
cssScopedClasses: string[] = [],
) {

const nativeTags = new Set(vueCompilerOptions.nativeTags);
const codes: Code[] = [];
const formatCodes: Code[] = [];
const cssCodes: Code[] = [];
Expand Down Expand Up @@ -160,26 +159,18 @@ export function generate(

for (const tagName in tagNames) {

if (nativeTags.has(tagName))
continue;

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

const names = new Set([
// order is important: https://github.com/johnsoncodehk/volar/issues/2010
capitalize(camelize(tagName)),
camelize(tagName),
tagName,
]);
const varName = validTsVar.test(tagName) ? tagName : capitalize(camelize(tagName.replace(/:/g, '-')));

codes.push(
'& import("./__VLS_types").WithComponent<"',
varName,
'", typeof __VLS_components, ',
[...names].map(name => `'${name}'`).join(', '),
'& import("./__VLS_types").WithComponent<typeof __VLS_components, ',
// order is important: https://github.com/johnsoncodehk/volar/issues/2010
`"${capitalize(camelize(tagName))}", `,
`"${camelize(tagName)}", `,
`"${tagName}"`,
'>\n',
);

Expand All @@ -206,7 +197,7 @@ export function generate(
for (const name of names) {
for (const tagRange of tagRanges) {
codes.push(
'__VLS_components',
name === tagName ? '__VLS_templateComponents' : '__VLS_components',
...createPropertyAccessCode([
name,
'template',
Expand Down Expand Up @@ -493,26 +484,11 @@ export function generate(

const propsFailedExps: CompilerDOM.SimpleExpressionNode[] = [];
const tagOffsets = endTagOffset !== undefined ? [startTagOffset, endTagOffset] : [startTagOffset];
const isIntrinsicElement = nativeTags.has(node.tag);
const isNamespacedTag = node.tag.indexOf('.') >= 0;
const componentVar = `__VLS_${elementIndex++}`;
const componentInstanceVar = `__VLS_${elementIndex++}`;

if (isIntrinsicElement) {
codes.push(
'const ',
componentVar,
` = (await import('./__VLS_types')).asFunctionalComponent(({} as import('./__VLS_types').IntrinsicElements)[`,
...createStringLiteralKeyCode([
node.tag,
'template',
tagOffsets[0],
capabilitiesPresets.diagnosticOnly,
]),
']);\n',
);
}
else if (isNamespacedTag) {
if (isNamespacedTag) {
codes.push(
`const ${componentVar} = (await import('./__VLS_types')).asFunctionalComponent(${node.tag}, new ${node.tag}({`,
...createPropsCode(node, 'slots'),
Expand All @@ -530,22 +506,7 @@ export function generate(
}

for (const offset of tagOffsets) {
if (isIntrinsicElement) {
codes.push(
`({} as import('./__VLS_types').IntrinsicElements)`,
...createPropertyAccessCode([
node.tag,
'template',
offset,
{
...capabilitiesPresets.tagReference,
...capabilitiesPresets.tagHover,
},
]),
';\n',
);
}
else if (isNamespacedTag) {
if (isNamespacedTag) {
codes.push(
[node.tag, 'template', [offset, offset + node.tag.length], capabilitiesPresets.all],
';\n',
Expand Down
1 change: 0 additions & 1 deletion packages/vue-language-core/src/types.ts
Expand Up @@ -21,7 +21,6 @@ export interface VueCompilerOptions {
extensions: string[];
strictTemplates: boolean;
skipTemplateCodegen: boolean;
nativeTags: string[];
dataAttributes: string[];
htmlAttributes: string[];
optionsWrapper: [string, string] | [];
Expand Down
17 changes: 9 additions & 8 deletions packages/vue-language-core/src/utils/localTypes.ts
Expand Up @@ -14,10 +14,9 @@ import type {
ObjectDirective,
FunctionDirective,
} from '${libName}';
${vueCompilerOptions.target >= 3.3 ? `import { JSX } from 'vue/jsx-runtime';` : ''}
export type IntrinsicElements = JSX.IntrinsicElements;
export type Element = JSX.Element;
export type IntrinsicElements = PickNotAny<import('vue/jsx-runtime').JSX, PickNotAny<JSX.IntrinsicElements, Record<string, any>>>;
export type Element = PickNotAny<import('vue/jsx-runtime').JSX, JSX.Element>;
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
export type PickNotAny<A, B> = IsAny<A> extends true ? B : A;
Expand Down Expand Up @@ -58,11 +57,13 @@ export declare function withScope<T, K>(ctx: T, scope: K): ctx is T & K;
export declare function makeOptional<T>(t: T): { [K in keyof T]?: T[K] };
export type SelfComponent<N, C> = string extends N ? {} : N extends string ? { [P in N]: C } : {};
export type WithComponent<N0 extends string, Components, N1, N2 = unknown, N3 = unknown> =
N1 extends keyof Components ? { [K in N0]: Components[N1] } :
N2 extends keyof Components ? { [K in N0]: Components[N2] } :
N3 extends keyof Components ? { [K in N0]: Components[N3] } :
${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: any }'};
export type WithComponent<Components, N1 extends string, N2 extends string, N0 extends string> =
IsAny<IntrinsicElements[N0]> extends true ? (
N1 extends keyof Components ? N1 extends N0 ? Pick<Components, N0> : { [K in N0]: Components[N1] } :
N2 extends keyof Components ? N2 extends N0 ? Pick<Components, N0> : { [K in N0]: Components[N2] } :
N0 extends keyof Components ? Pick<Components, N0> :
${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: any }'}
) : Pick<IntrinsicElements, N0>;
export type FillingEventArg_ParametersLength<E extends (...args: any) => any> = IsAny<Parameters<E>> extends true ? -1 : Parameters<E>['length'];
export type FillingEventArg<E> = E extends (...args: any) => any ? FillingEventArg_ParametersLength<E> extends 0 ? ($event?: undefined) => ReturnType<E> : E : E;
Expand Down
33 changes: 0 additions & 33 deletions packages/vue-language-core/src/utils/ts.ts
Expand Up @@ -170,31 +170,6 @@ function getVueCompilerOptions(
}
}

// 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;
return {
Expand All @@ -203,14 +178,6 @@ export function resolveVueCompilerOptions(vueOptions: Partial<VueCompilerOptions
extensions: vueOptions.extensions ?? ['.vue'],
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
4 changes: 2 additions & 2 deletions packages/vue-language-server/src/languageServerPlugin.ts
Expand Up @@ -7,7 +7,7 @@ import { DetectNameCasingRequest, GetConvertAttrCasingEditsRequest, GetConvertTa
import { VueServerInitializationOptions } from './types';
import type * as ts from 'typescript/lib/tsserverlibrary';
import * as componentMeta from 'vue-component-meta';
import { resolveVueCompilerOptions, VueCompilerOptions } from '@volar/vue-language-core';
import { VueCompilerOptions } from '@volar/vue-language-core';

export function createServerPlugin(connection: Connection) {

Expand Down Expand Up @@ -121,7 +121,7 @@ export function createServerPlugin(connection: Connection) {
if (languageService?.context.typescript) {
const vueOptions = hostToVueOptions.get(languageService.context.host);
if (vueOptions) {
return nameCasing.convertAttrName(languageService.context, languageService.context.typescript, params.textDocument.uri, params.casing, resolveVueCompilerOptions(vueOptions));
return nameCasing.convertAttrName(languageService.context, languageService.context.typescript, params.textDocument.uri, params.casing);
}
}
});
Expand Down
41 changes: 36 additions & 5 deletions packages/vue-language-service/src/helpers.ts
Expand Up @@ -4,7 +4,6 @@ import * as CompilerDOM from '@vue/compiler-dom';
import { computed, ComputedRef } from '@vue/reactivity';
import { typesFileName } from '@volar/vue-language-core/out/utils/localTypes';
import { camelize, capitalize } from '@vue/shared';
import type { VueCompilerOptions } from './types';

import type * as ts from 'typescript/lib/tsserverlibrary';

Expand All @@ -13,7 +12,7 @@ export function checkPropsOfTag(
tsLs: ts.LanguageService,
sourceFile: embedded.VirtualFile,
tag: string,
vueCompilerOptions: VueCompilerOptions,
nativeTags: Set<string>,
requiredOnly = false,
) {

Expand All @@ -26,7 +25,7 @@ export function checkPropsOfTag(

let componentSymbol = components.componentsType.getProperty(name[0]);

if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
if (!componentSymbol && !nativeTags.has(name[0])) {
componentSymbol = components.componentsType.getProperty(camelize(name[0]))
?? components.componentsType.getProperty(capitalize(camelize(name[0])));
}
Expand Down Expand Up @@ -86,7 +85,7 @@ export function checkEventsOfTag(
tsLs: ts.LanguageService,
sourceFile: embedded.VirtualFile,
tag: string,
vueCompilerOptions: VueCompilerOptions,
nativeTags: Set<string>,
) {

const checker = tsLs.getProgram()!.getTypeChecker();
Expand All @@ -98,7 +97,7 @@ export function checkEventsOfTag(

let componentSymbol = components.componentsType.getProperty(name[0]);

if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
if (!componentSymbol && !nativeTags.has(name[0])) {
componentSymbol = components.componentsType.getProperty(camelize(name[0]))
?? components.componentsType.getProperty(capitalize(camelize(name[0])));
}
Expand Down Expand Up @@ -151,15 +150,47 @@ export function checkComponentNames(
ts: typeof import('typescript/lib/tsserverlibrary'),
tsLs: ts.LanguageService,
sourceFile: embedded.VirtualFile,
nativeTags: Set<string>,
) {
return getComponentsType(ts, tsLs, sourceFile)
?.componentsType
?.getProperties()
.map(c => c.name)
.filter(entry => entry.indexOf('$') === -1 && !entry.startsWith('_'))
.filter(entry => !nativeTags.has(entry))
?? [];
}

export function checkNativeTags(
ts: typeof import('typescript/lib/tsserverlibrary'),
tsLs: ts.LanguageService,
fileName: string,
) {

const sharedTypesFileName = fileName.substring(0, fileName.lastIndexOf('/')) + '/' + typesFileName;
const result = new Set<string>();

let tsSourceFile: ts.SourceFile | undefined;

if (tsSourceFile = tsLs.getProgram()?.getSourceFile(sharedTypesFileName)) {

const typeNode = tsSourceFile.statements.find((node): node is ts.TypeAliasDeclaration => ts.isTypeAliasDeclaration(node) && node.name.getText() === 'IntrinsicElements');
const checker = tsLs.getProgram()?.getTypeChecker();

if (checker && typeNode) {

const type = checker.getTypeFromTypeNode(typeNode.type);
const props = type.getProperties();

for (const prop of props) {
result.add(prop.name);
}
}
}

return result;
}

export function getElementAttrs(
ts: typeof import('typescript/lib/tsserverlibrary'),
tsLs: ts.LanguageService,
Expand Down
16 changes: 9 additions & 7 deletions packages/vue-language-service/src/ideFeatures/nameCasing.ts
@@ -1,9 +1,9 @@
import { hyphenate } from '@vue/shared';
import { LanguageServicePluginContext, VirtualFile } from '@volar/language-service';
import { checkComponentNames, getTemplateTagsAndAttrs, checkPropsOfTag } from '../helpers';
import { checkComponentNames, getTemplateTagsAndAttrs, checkPropsOfTag, checkNativeTags } from '../helpers';
import * as vue from '@volar/vue-language-core';
import * as vscode from 'vscode-languageserver-protocol';
import { AttrNameCasing, TagNameCasing, VueCompilerOptions } from '../types';
import { AttrNameCasing, TagNameCasing } from '../types';

export async function convertTagName(
context: LanguageServicePluginContext,
Expand All @@ -23,7 +23,8 @@ export async function convertTagName(
const template = desc.template;
const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
const edits: vscode.TextEdit[] = [];
const components = checkComponentNames(_ts.module, _ts.languageService, rootFile);
const nativeTags = checkNativeTags(_ts.module, _ts.languageService, rootFile.fileName);
const components = checkComponentNames(_ts.module, _ts.languageService, rootFile, nativeTags);
const tags = getTemplateTagsAndAttrs(rootFile);

for (const [tagName, { offsets }] of tags) {
Expand Down Expand Up @@ -51,7 +52,6 @@ export async function convertAttrName(
_ts: NonNullable<LanguageServicePluginContext['typescript']>,
uri: string,
casing: AttrNameCasing,
vueCompilerOptions: VueCompilerOptions,
) {

const rootFile = context.documents.getSourceByUri(uri)?.root;
Expand All @@ -65,13 +65,14 @@ export async function convertAttrName(
const template = desc.template;
const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
const edits: vscode.TextEdit[] = [];
const components = checkComponentNames(_ts.module, _ts.languageService, rootFile);
const nativeTags = checkNativeTags(_ts.module, _ts.languageService, rootFile.fileName);
const components = checkComponentNames(_ts.module, _ts.languageService, rootFile, nativeTags);
const tags = getTemplateTagsAndAttrs(rootFile);

for (const [tagName, { attrs }] of tags) {
const componentName = components.find(component => component === tagName || hyphenate(component) === tagName);
if (componentName) {
const props = checkPropsOfTag(_ts.module, _ts.languageService, rootFile, componentName, vueCompilerOptions);
const props = checkPropsOfTag(_ts.module, _ts.languageService, rootFile, componentName, nativeTags);
for (const [attrName, { offsets }] of attrs) {
const propName = props.find(prop => prop === attrName || hyphenate(prop) === attrName);
if (propName) {
Expand Down Expand Up @@ -162,7 +163,8 @@ export function detect(
}
function getTagNameCase(file: VirtualFile): TagNameCasing[] {

const components = checkComponentNames(_ts.module, _ts.languageService, file);
const nativeTags = checkNativeTags(_ts.module, _ts.languageService, file.fileName);
const components = checkComponentNames(_ts.module, _ts.languageService, file, nativeTags);
const tagNames = getTemplateTagsAndAttrs(file);
const result: TagNameCasing[] = [];

Expand Down

0 comments on commit 6d88299

Please sign in to comment.