Skip to content

Commit

Permalink
refactor: deprecate nativeTags (#2685)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk authored Apr 23, 2023
1 parent 4e2ea5b commit 6d88299
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 128 deletions.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 6d88299

Please sign in to comment.