Skip to content

Commit 74d2b60

Browse files
authored
feat(language-core): split strictTemplates (#4880)
1 parent 8b8bfbb commit 74d2b60

File tree

9 files changed

+87
-30
lines changed

9 files changed

+87
-30
lines changed

Diff for: packages/component-meta/lib/base.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ export function baseCreate(
138138
const directoryExists = languageServiceHost.directoryExists?.bind(languageServiceHost);
139139
const fileExists = languageServiceHost.fileExists.bind(languageServiceHost);
140140
const getScriptSnapshot = languageServiceHost.getScriptSnapshot.bind(languageServiceHost);
141-
const globalTypesName = `${commandLine.vueOptions.lib}_${commandLine.vueOptions.target}_${commandLine.vueOptions.strictTemplates}.d.ts`;
142-
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + vue.generateGlobalTypes(commandLine.vueOptions.lib, commandLine.vueOptions.target, commandLine.vueOptions.strictTemplates);
141+
const globalTypesName = vue.resolveGlobalTypesName(commandLine.vueOptions);
142+
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + vue.generateGlobalTypes(commandLine.vueOptions);
143143
const globalTypesSnapshot: ts.IScriptSnapshot = {
144144
getText: (start, end) => globalTypesContents.slice(start, end),
145145
getLength: () => globalTypesContents.length,

Diff for: packages/language-core/lib/codegen/globalTypes.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import type { VueCompilerOptions } from '../types';
12
import { getSlotsPropertyName } from '../utils/shared';
23

3-
export function generateGlobalTypes(lib: string, target: number, strictTemplates: boolean) {
4-
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates ? '' : ' & Record<string, unknown>'}`;
4+
export function resolveGlobalTypesName(options: VueCompilerOptions) {
5+
const { lib, target, strictTemplates } = options;
6+
return `${lib}_${target}_${strictTemplates.attributes}_${strictTemplates.components}.d.ts`;
7+
}
8+
9+
export function generateGlobalTypes(options: VueCompilerOptions) {
10+
const { lib, target, strictTemplates } = options;
11+
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates.attributes ? '' : ' & Record<string, unknown>'}`;
512
let text = ``;
613
if (target < 3.5) {
714
text += `
@@ -49,7 +56,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
4956
N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } :
5057
N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } :
5158
N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } :
52-
${strictTemplates ? '{}' : '{ [K in N0]: unknown }'};
59+
${strictTemplates.components ? '{}' : '{ [K in N0]: unknown }'};
5360
type __VLS_FunctionalComponentProps<T, K> =
5461
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
5562
: T extends (props: infer P, ...args: any) => any ? P :
@@ -69,7 +76,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
6976
: __VLS_IsFunction<Events, CamelizedEvent> extends true
7077
? { [K in onEvent]?: Events[CamelizedEvent] }
7178
: Props
72-
)${strictTemplates ? '' : ' & Record<string, unknown>'};
79+
)${strictTemplates.attributes ? '' : ' & Record<string, unknown>'};
7380
// fix https://github.com/vuejs/language-tools/issues/926
7481
type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
7582
type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
@@ -143,8 +150,8 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
143150
} & { props?: ${fnPropsType}; expose?(exposed: K): void; } }
144151
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
145152
: T extends (...args: any) => any ? T
146-
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
147-
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
153+
: (_: {}${strictTemplates.attributes ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates.attributes ? '' : ' & Record<string, unknown>'} } };
154+
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates.components ? '' : ' & Record<string, unknown>'}) => void;
148155
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
149156
function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
150157
function __VLS_tryAsConstant<const T>(t: T): T;

Diff for: packages/language-core/lib/codegen/script/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type * as ts from 'typescript';
44
import type { ScriptRanges } from '../../parsers/scriptRanges';
55
import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges';
66
import type { Code, Sfc, VueCodeInformation, VueCompilerOptions } from '../../types';
7-
import { generateGlobalTypes } from '../globalTypes';
7+
import { generateGlobalTypes, resolveGlobalTypesName } from '../globalTypes';
88
import type { TemplateCodegenContext } from '../template/context';
99
import { endOfLine, generateSfcBlockSection, newLine } from '../utils';
1010
import { generateComponentSelf } from './componentSelf';
@@ -68,7 +68,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator<Code,
6868
yield `/// <reference types="${relativePath}" />${newLine}`;
6969
}
7070
else {
71-
yield `/// <reference types=".vue-global-types/${options.vueCompilerOptions.lib}_${options.vueCompilerOptions.target}_${options.vueCompilerOptions.strictTemplates}.d.ts" />${newLine}`;
71+
yield `/// <reference types=".vue-global-types/${resolveGlobalTypesName(options.vueCompilerOptions)}" />${newLine}`;
7272
}
7373
}
7474
else {
@@ -163,7 +163,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator<Code,
163163
}
164164
yield* ctx.localTypes.generate([...ctx.localTypes.getUsedNames()]);
165165
if (options.appendGlobalTypes) {
166-
yield generateGlobalTypes(options.vueCompilerOptions.lib, options.vueCompilerOptions.target, options.vueCompilerOptions.strictTemplates);
166+
yield generateGlobalTypes(options.vueCompilerOptions);
167167
}
168168

169169
if (options.sfc.scriptSetup) {

Diff for: packages/language-core/lib/codegen/template/element.ts

+26-3
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,14 @@ export function* generateComponent(
213213

214214
yield `// @ts-ignore${newLine}`;
215215
yield `const ${var_functionalComponent} = __VLS_asFunctionalComponent(${var_originalComponent}, new ${var_originalComponent}({${newLine}`;
216-
yield* generateElementProps(options, ctx, node, props, options.vueCompilerOptions.strictTemplates, false);
216+
yield* generateElementProps(
217+
options,
218+
ctx,
219+
node,
220+
props,
221+
options.vueCompilerOptions.strictTemplates.attributes,
222+
false
223+
);
217224
yield `}))${endOfLine}`;
218225

219226
yield `const `;
@@ -237,7 +244,15 @@ export function* generateComponent(
237244
tagOffsets[0] + node.tag.length,
238245
ctx.codeFeatures.verification,
239246
`{${newLine}`,
240-
...generateElementProps(options, ctx, node, props, options.vueCompilerOptions.strictTemplates, true, failedPropExps),
247+
...generateElementProps(
248+
options,
249+
ctx,
250+
node,
251+
props,
252+
options.vueCompilerOptions.strictTemplates.attributes,
253+
true,
254+
failedPropExps
255+
),
241256
`}`
242257
);
243258
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
@@ -330,7 +345,15 @@ export function* generateElement(
330345
startTagOffset + node.tag.length,
331346
ctx.codeFeatures.verification,
332347
`{${newLine}`,
333-
...generateElementProps(options, ctx, node, node.props, options.vueCompilerOptions.strictTemplates, true, failedPropExps),
348+
...generateElementProps(
349+
options,
350+
ctx,
351+
node,
352+
node.props,
353+
options.vueCompilerOptions.strictTemplates.attributes,
354+
true,
355+
failedPropExps
356+
),
334357
`}`
335358
);
336359
yield `)${endOfLine}`;

Diff for: packages/language-core/lib/codegen/template/slotOutlet.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,28 @@ export function* generateSlotOutlet(
5353
startTagOffset + node.tag.length,
5454
ctx.codeFeatures.verification,
5555
`{${newLine}`,
56-
...generateElementProps(options, ctx, node, node.props.filter(prop => prop !== nameProp), true, true),
56+
...generateElementProps(
57+
options,
58+
ctx,
59+
node,
60+
node.props.filter(prop => prop !== nameProp),
61+
true,
62+
true
63+
),
5764
`}`
5865
);
5966
yield `)${endOfLine}`;
6067
}
6168
else {
6269
yield `var ${varSlot} = {${newLine}`;
63-
yield* generateElementProps(options, ctx, node, node.props.filter(prop => prop !== nameProp), options.vueCompilerOptions.strictTemplates, true);
70+
yield* generateElementProps(
71+
options,
72+
ctx,
73+
node,
74+
node.props.filter(prop => prop !== nameProp),
75+
options.vueCompilerOptions.strictTemplates.attributes,
76+
true
77+
);
6478
yield `}${endOfLine}`;
6579

6680
if (

Diff for: packages/language-core/lib/types.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ export interface VueCompilerOptions {
2828
vitePressExtensions: string[];
2929
petiteVueExtensions: string[];
3030
jsxSlots: boolean;
31-
strictTemplates: boolean;
31+
strictTemplates: {
32+
attributes: boolean;
33+
components: boolean;
34+
};
3235
skipTemplateCodegen: boolean;
3336
fallthroughAttributes: boolean;
3437
dataAttributes: string[];

Diff for: packages/language-core/lib/utils/ts.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { camelize } from '@vue/shared';
22
import { posix as path } from 'path-browserify';
33
import type * as ts from 'typescript';
4-
import { generateGlobalTypes } from '../codegen/globalTypes';
4+
import { generateGlobalTypes, resolveGlobalTypesName } from '../codegen/globalTypes';
55
import { getAllExtensions } from '../languagePlugin';
66
import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from '../types';
77

@@ -220,17 +220,24 @@ function getPartialVueCompilerOptions(
220220
}
221221
}
222222

223-
function getDefaultOptions(options: Partial<VueCompilerOptions>): VueCompilerOptions {
224-
const target = options.target ?? 3.3;
225-
const lib = options.lib ?? 'vue';
223+
export function getDefaultOptions(vueOptions: Partial<VueCompilerOptions>): VueCompilerOptions {
224+
const target = vueOptions.target ?? 3.3;
225+
const lib = vueOptions.lib ?? 'vue';
226+
const strictTemplates = typeof vueOptions.strictTemplates === 'boolean' ? {
227+
attributes: vueOptions.strictTemplates,
228+
components: vueOptions.strictTemplates
229+
} : vueOptions.strictTemplates ?? {
230+
attributes: false,
231+
components: false
232+
};
226233
return {
227234
target,
228235
lib,
229236
extensions: ['.vue'],
230237
vitePressExtensions: [],
231238
petiteVueExtensions: [],
232239
jsxSlots: false,
233-
strictTemplates: false,
240+
strictTemplates,
234241
skipTemplateCodegen: false,
235242
fallthroughAttributes: false,
236243
dataAttributes: [],
@@ -309,8 +316,8 @@ export function setupGlobalTypes(rootDir: string, vueOptions: VueCompilerOptions
309316
}
310317
dir = parentDir;
311318
}
312-
const globalTypesPath = path.join(dir, 'node_modules', '.vue-global-types', `${vueOptions.lib}_${vueOptions.target}_${vueOptions.strictTemplates}.d.ts`);
313-
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueOptions.lib, vueOptions.target, vueOptions.strictTemplates);
319+
const globalTypesPath = path.join(dir, 'node_modules', '.vue-global-types', resolveGlobalTypesName(vueOptions));
320+
const globalTypesContents = `// @ts-nocheck\nexport {};\n` + generateGlobalTypes(vueOptions);
314321
host.writeFile(globalTypesPath, globalTypesContents);
315322
return { absolutePath: globalTypesPath };
316323
} catch { }

Diff for: packages/language-core/schemas/vue-tsconfig.schema.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
"markdownDescription": "Generate slots type for `JSX.ElementChildrenAttribute`."
4242
},
4343
"strictTemplates": {
44-
"type": "boolean",
45-
"default": false,
46-
"markdownDescription": "Strict props, component type-checking in templates."
44+
"type": ["boolean", "object"],
45+
"default": {
46+
"attributes": false,
47+
"components": false
48+
},
49+
"markdownDescription": "Strict props(attrs), component type-checking in templates."
4750
},
4851
"skipTemplateCodegen": {
4952
"type": "boolean",

Diff for: packages/language-server/lib/initialize.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { LanguageServer } from '@volar/language-server';
22
import { createTypeScriptProject } from '@volar/language-server/node';
3-
import { createParsedCommandLine, createVueLanguagePlugin, generateGlobalTypes, getAllExtensions, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core';
3+
import { createParsedCommandLine, createVueLanguagePlugin, generateGlobalTypes, getAllExtensions, resolveGlobalTypesName, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core';
44
import { Disposable, getFullLanguageServicePlugins, InitializeParams } from '@vue/language-service';
55
import type * as ts from 'typescript';
66

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

0 commit comments

Comments
 (0)