Skip to content

Commit

Permalink
feat(language-core): split strictTemplates (#4880)
Browse files Browse the repository at this point in the history
  • Loading branch information
KazariEX authored Jan 22, 2025
1 parent 8b8bfbb commit 74d2b60
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 30 deletions.
4 changes: 2 additions & 2 deletions packages/component-meta/lib/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ export function baseCreate(
const directoryExists = languageServiceHost.directoryExists?.bind(languageServiceHost);
const fileExists = languageServiceHost.fileExists.bind(languageServiceHost);
const getScriptSnapshot = languageServiceHost.getScriptSnapshot.bind(languageServiceHost);
const globalTypesName = `${commandLine.vueOptions.lib}_${commandLine.vueOptions.target}_${commandLine.vueOptions.strictTemplates}.d.ts`;
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + vue.generateGlobalTypes(commandLine.vueOptions.lib, commandLine.vueOptions.target, commandLine.vueOptions.strictTemplates);
const globalTypesName = vue.resolveGlobalTypesName(commandLine.vueOptions);
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + vue.generateGlobalTypes(commandLine.vueOptions);
const globalTypesSnapshot: ts.IScriptSnapshot = {
getText: (start, end) => globalTypesContents.slice(start, end),
getLength: () => globalTypesContents.length,
Expand Down
19 changes: 13 additions & 6 deletions packages/language-core/lib/codegen/globalTypes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { VueCompilerOptions } from '../types';
import { getSlotsPropertyName } from '../utils/shared';

export function generateGlobalTypes(lib: string, target: number, strictTemplates: boolean) {
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates ? '' : ' & Record<string, unknown>'}`;
export function resolveGlobalTypesName(options: VueCompilerOptions) {
const { lib, target, strictTemplates } = options;
return `${lib}_${target}_${strictTemplates.attributes}_${strictTemplates.components}.d.ts`;
}

export function generateGlobalTypes(options: VueCompilerOptions) {
const { lib, target, strictTemplates } = options;
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates.attributes ? '' : ' & Record<string, unknown>'}`;
let text = ``;
if (target < 3.5) {
text += `
Expand Down Expand Up @@ -49,7 +56,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } :
N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } :
N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } :
${strictTemplates ? '{}' : '{ [K in N0]: unknown }'};
${strictTemplates.components ? '{}' : '{ [K in N0]: unknown }'};
type __VLS_FunctionalComponentProps<T, K> =
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P :
Expand All @@ -69,7 +76,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
: __VLS_IsFunction<Events, CamelizedEvent> extends true
? { [K in onEvent]?: Events[CamelizedEvent] }
: Props
)${strictTemplates ? '' : ' & Record<string, unknown>'};
)${strictTemplates.attributes ? '' : ' & Record<string, unknown>'};
// fix https://github.com/vuejs/language-tools/issues/926
type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
Expand Down Expand Up @@ -143,8 +150,8 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
} & { props?: ${fnPropsType}; expose?(exposed: K): void; } }
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
: (_: {}${strictTemplates.attributes ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates.attributes ? '' : ' & Record<string, unknown>'} } };
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates.components ? '' : ' & Record<string, unknown>'}) => void;
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
function __VLS_tryAsConstant<const T>(t: T): T;
Expand Down
6 changes: 3 additions & 3 deletions packages/language-core/lib/codegen/script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type * as ts from 'typescript';
import type { ScriptRanges } from '../../parsers/scriptRanges';
import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges';
import type { Code, Sfc, VueCodeInformation, VueCompilerOptions } from '../../types';
import { generateGlobalTypes } from '../globalTypes';
import { generateGlobalTypes, resolveGlobalTypesName } from '../globalTypes';
import type { TemplateCodegenContext } from '../template/context';
import { endOfLine, generateSfcBlockSection, newLine } from '../utils';
import { generateComponentSelf } from './componentSelf';
Expand Down Expand Up @@ -68,7 +68,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator<Code,
yield `/// <reference types="${relativePath}" />${newLine}`;
}
else {
yield `/// <reference types=".vue-global-types/${options.vueCompilerOptions.lib}_${options.vueCompilerOptions.target}_${options.vueCompilerOptions.strictTemplates}.d.ts" />${newLine}`;
yield `/// <reference types=".vue-global-types/${resolveGlobalTypesName(options.vueCompilerOptions)}" />${newLine}`;
}
}
else {
Expand Down Expand Up @@ -163,7 +163,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator<Code,
}
yield* ctx.localTypes.generate([...ctx.localTypes.getUsedNames()]);
if (options.appendGlobalTypes) {
yield generateGlobalTypes(options.vueCompilerOptions.lib, options.vueCompilerOptions.target, options.vueCompilerOptions.strictTemplates);
yield generateGlobalTypes(options.vueCompilerOptions);
}

if (options.sfc.scriptSetup) {
Expand Down
29 changes: 26 additions & 3 deletions packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,14 @@ export function* generateComponent(

yield `// @ts-ignore${newLine}`;
yield `const ${var_functionalComponent} = __VLS_asFunctionalComponent(${var_originalComponent}, new ${var_originalComponent}({${newLine}`;
yield* generateElementProps(options, ctx, node, props, options.vueCompilerOptions.strictTemplates, false);
yield* generateElementProps(
options,
ctx,
node,
props,
options.vueCompilerOptions.strictTemplates.attributes,
false
);
yield `}))${endOfLine}`;

yield `const `;
Expand All @@ -237,7 +244,15 @@ export function* generateComponent(
tagOffsets[0] + node.tag.length,
ctx.codeFeatures.verification,
`{${newLine}`,
...generateElementProps(options, ctx, node, props, options.vueCompilerOptions.strictTemplates, true, failedPropExps),
...generateElementProps(
options,
ctx,
node,
props,
options.vueCompilerOptions.strictTemplates.attributes,
true,
failedPropExps
),
`}`
);
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
Expand Down Expand Up @@ -330,7 +345,15 @@ export function* generateElement(
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{${newLine}`,
...generateElementProps(options, ctx, node, node.props, options.vueCompilerOptions.strictTemplates, true, failedPropExps),
...generateElementProps(
options,
ctx,
node,
node.props,
options.vueCompilerOptions.strictTemplates.attributes,
true,
failedPropExps
),
`}`
);
yield `)${endOfLine}`;
Expand Down
18 changes: 16 additions & 2 deletions packages/language-core/lib/codegen/template/slotOutlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,28 @@ export function* generateSlotOutlet(
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{${newLine}`,
...generateElementProps(options, ctx, node, node.props.filter(prop => prop !== nameProp), true, true),
...generateElementProps(
options,
ctx,
node,
node.props.filter(prop => prop !== nameProp),
true,
true
),
`}`
);
yield `)${endOfLine}`;
}
else {
yield `var ${varSlot} = {${newLine}`;
yield* generateElementProps(options, ctx, node, node.props.filter(prop => prop !== nameProp), options.vueCompilerOptions.strictTemplates, true);
yield* generateElementProps(
options,
ctx,
node,
node.props.filter(prop => prop !== nameProp),
options.vueCompilerOptions.strictTemplates.attributes,
true
);
yield `}${endOfLine}`;

if (
Expand Down
5 changes: 4 additions & 1 deletion packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ export interface VueCompilerOptions {
vitePressExtensions: string[];
petiteVueExtensions: string[];
jsxSlots: boolean;
strictTemplates: boolean;
strictTemplates: {
attributes: boolean;
components: boolean;
};
skipTemplateCodegen: boolean;
fallthroughAttributes: boolean;
dataAttributes: string[];
Expand Down
21 changes: 14 additions & 7 deletions packages/language-core/lib/utils/ts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { camelize } from '@vue/shared';
import { posix as path } from 'path-browserify';
import type * as ts from 'typescript';
import { generateGlobalTypes } from '../codegen/globalTypes';
import { generateGlobalTypes, resolveGlobalTypesName } from '../codegen/globalTypes';
import { getAllExtensions } from '../languagePlugin';
import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from '../types';

Expand Down Expand Up @@ -220,17 +220,24 @@ function getPartialVueCompilerOptions(
}
}

function getDefaultOptions(options: Partial<VueCompilerOptions>): VueCompilerOptions {
const target = options.target ?? 3.3;
const lib = options.lib ?? 'vue';
export function getDefaultOptions(vueOptions: Partial<VueCompilerOptions>): VueCompilerOptions {
const target = vueOptions.target ?? 3.3;
const lib = vueOptions.lib ?? 'vue';
const strictTemplates = typeof vueOptions.strictTemplates === 'boolean' ? {
attributes: vueOptions.strictTemplates,
components: vueOptions.strictTemplates
} : vueOptions.strictTemplates ?? {
attributes: false,
components: false
};
return {
target,
lib,
extensions: ['.vue'],
vitePressExtensions: [],
petiteVueExtensions: [],
jsxSlots: false,
strictTemplates: false,
strictTemplates,
skipTemplateCodegen: false,
fallthroughAttributes: false,
dataAttributes: [],
Expand Down Expand Up @@ -309,8 +316,8 @@ export function setupGlobalTypes(rootDir: string, vueOptions: VueCompilerOptions
}
dir = parentDir;
}
const globalTypesPath = path.join(dir, 'node_modules', '.vue-global-types', `${vueOptions.lib}_${vueOptions.target}_${vueOptions.strictTemplates}.d.ts`);
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueOptions.lib, vueOptions.target, vueOptions.strictTemplates);
const globalTypesPath = path.join(dir, 'node_modules', '.vue-global-types', resolveGlobalTypesName(vueOptions));
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueOptions);
host.writeFile(globalTypesPath, globalTypesContents);
return { absolutePath: globalTypesPath };
} catch { }
Expand Down
9 changes: 6 additions & 3 deletions packages/language-core/schemas/vue-tsconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@
"markdownDescription": "Generate slots type for `JSX.ElementChildrenAttribute`."
},
"strictTemplates": {
"type": "boolean",
"default": false,
"markdownDescription": "Strict props, component type-checking in templates."
"type": ["boolean", "object"],
"default": {
"attributes": false,
"components": false
},
"markdownDescription": "Strict props(attrs), component type-checking in templates."
},
"skipTemplateCodegen": {
"type": "boolean",
Expand Down
6 changes: 3 additions & 3 deletions packages/language-server/lib/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { LanguageServer } from '@volar/language-server';
import { createTypeScriptProject } from '@volar/language-server/node';
import { createParsedCommandLine, createVueLanguagePlugin, generateGlobalTypes, getAllExtensions, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core';
import { createParsedCommandLine, createVueLanguagePlugin, generateGlobalTypes, getAllExtensions, resolveGlobalTypesName, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core';
import { Disposable, getFullLanguageServicePlugins, InitializeParams } from '@vue/language-service';
import type * as ts from 'typescript';

Expand Down Expand Up @@ -55,8 +55,8 @@ export function initialize(
const directoryExists = project.typescript.languageServiceHost.directoryExists?.bind(project.typescript.languageServiceHost);
const fileExists = project.typescript.languageServiceHost.fileExists.bind(project.typescript.languageServiceHost);
const getScriptSnapshot = project.typescript.languageServiceHost.getScriptSnapshot.bind(project.typescript.languageServiceHost);
const globalTypesName = `${vueCompilerOptions.lib}_${vueCompilerOptions.target}_${vueCompilerOptions.strictTemplates}.d.ts`;
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueCompilerOptions.lib, vueCompilerOptions.target, vueCompilerOptions.strictTemplates);
const globalTypesName = resolveGlobalTypesName(vueCompilerOptions);
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueCompilerOptions);
const globalTypesSnapshot: ts.IScriptSnapshot = {
getText: (start, end) => globalTypesContents.slice(start, end),
getLength: () => globalTypesContents.length,
Expand Down

0 comments on commit 74d2b60

Please sign in to comment.