diff --git a/.gitignore b/.gitignore index c136a37d85..b96c3ff009 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules *.tsbuildinfo *.vsix +extensions/vscode/types extensions/vscode/out extensions/vscode/tests/embeddedGrammars/*.tmLanguage.json packages/*/*.d.ts diff --git a/extensions/vscode/rolldown.config.ts b/extensions/vscode/rolldown.config.ts index a49a506643..fedeaedb28 100644 --- a/extensions/vscode/rolldown.config.ts +++ b/extensions/vscode/rolldown.config.ts @@ -29,6 +29,15 @@ export default defineConfig({ fs.rmSync(path.resolve(__dirname, './dist'), { recursive: true, force: true }); }, }, + { + name: 'copy-types', + buildEnd() { + const sourceDir = path.resolve(__dirname, '../../packages/language-core/types'); + const targetDir = path.resolve(__dirname, './types'); + fs.rmSync(targetDir, { recursive: true, force: true }); + fs.cpSync(sourceDir, targetDir, { recursive: true }); + }, + }, { name: 'umd2esm', resolveId: { diff --git a/extensions/vscode/schemas/vue-tsconfig.schema.json b/extensions/vscode/schemas/vue-tsconfig.schema.json index e368f7f872..1fffdced3e 100644 --- a/extensions/vscode/schemas/vue-tsconfig.schema.json +++ b/extensions/vscode/schemas/vue-tsconfig.schema.json @@ -20,9 +20,10 @@ "default": "vue", "markdownDescription": "Specify module name for import regular types." }, - "globalTypesPath": { + "typesRoot": { "type": "string", - "markdownDescription": "Path to the global types file. Manual configuration is required when `node_modules` does not exist in the environment." + "default": "@vue/language-core/types", + "markdownDescription": "Root path for helper type definitions. See: https://github.com/vuejs/language-tools/pull/5872" }, "extensions": { "type": "array", diff --git a/packages/component-meta/lib/base.ts b/packages/component-meta/lib/base.ts index 10c25fa31c..b5b08884ff 100644 --- a/packages/component-meta/lib/base.ts +++ b/packages/component-meta/lib/base.ts @@ -157,8 +157,6 @@ function baseCreate( let fileNamesSet = new Set(fileNames.map(path => path.replace(windowsPathReg, '/'))); let projectVersion = 0; - vueOptions.globalTypesPath = core.createGlobalTypesWriter(vueOptions, ts.sys.writeFile); - const projectHost: TypeScriptProjectHost = { getCurrentDirectory: () => rootPath, getProjectVersion: () => projectVersion.toString(), @@ -267,7 +265,6 @@ function baseCreate( }, reload() { [{ vueOptions, options, projectReferences }, fileNames] = getConfigAndFiles(); - vueOptions.globalTypesPath = core.createGlobalTypesWriter(vueOptions, ts.sys.writeFile); fileNamesSet = new Set(fileNames.map(path => path.replace(windowsPathReg, '/'))); this.clearCache(); }, diff --git a/packages/language-core/index.ts b/packages/language-core/index.ts index 542b29963c..d3509df678 100644 --- a/packages/language-core/index.ts +++ b/packages/language-core/index.ts @@ -1,4 +1,3 @@ -export * from './lib/codegen/globalTypes'; export * from './lib/codegen/template'; export * from './lib/compilerOptions'; export * from './lib/languagePlugin'; diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts deleted file mode 100644 index c782d0f3ad..0000000000 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { VueCompilerOptions } from '../types'; -import * as names from './names'; -import { endOfLine, newLine } from './utils'; - -export function getGlobalTypesFileName(options: VueCompilerOptions) { - return [ - options.lib, - options.target, - ].join('_') + '.d.ts'; -} - -export function generateGlobalTypes(options: VueCompilerOptions) { - const { lib, target } = options; - - let text = `// @ts-nocheck${newLine}`; - text += `export {}${endOfLine}`; - - if (target < 3.5) { - text += `declare module '${lib}' { - export interface GlobalComponents { } - export interface GlobalDirectives { } -}${newLine}`; - } - - text += `declare global { - var ${names.PROPS_FALLBACK}: Record; - - const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any }; - const ${names.placeholder}: any; - const ${names.intrinsics}: ${ - target >= 3.3 - ? `import('${lib}/jsx-runtime').JSX.IntrinsicElements` - : `globalThis.JSX.IntrinsicElements` - }; - - type __VLS_Elements = __VLS_SpreadMerge; - type __VLS_GlobalComponents = ${ - target >= 3.5 - ? `import('${lib}').GlobalComponents` - : `import('${lib}').GlobalComponents & Pick` - }; - type __VLS_GlobalDirectives = import('${lib}').GlobalDirectives; - type __VLS_IsAny = 0 extends 1 & T ? true : false; - type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; - type __VLS_SpreadMerge = Omit & B; - type __VLS_WithComponent = - N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } : - N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } : - N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } : - Self extends object ? { [K in N0]: Self } : - N1 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N1] } : - N2 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N2] } : - N3 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N3] } : - {}; - type __VLS_FunctionalComponentCtx = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny - ? K extends { __ctx?: infer Ctx } ? NonNullable : never : any - , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any - >; - type __VLS_FunctionalComponentProps = '__ctx' extends keyof __VLS_PickNotAny - ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never - : T extends (props: infer P, ...args: any) => any ? P - : {}; - type __VLS_FunctionalComponent0 = (props: (T extends { $props: infer Props } ? Props : {}), ctx?: any) => ${ - target >= 3.3 - ? `import('${lib}/jsx-runtime').JSX.Element` - : `globalThis.JSX.Element` - } & { - __ctx?: { - attrs?: any; - slots?: T extends { $slots: infer Slots } ? Slots : Record; - emit?: T extends { $emit: infer Emit } ? Emit : {}; - props?: typeof props; - expose?: (exposed: T) => void; - }; - }; - type __VLS_FunctionalComponent1 = (props: (T extends { $props: infer Props } ? Props : {}) & Record, ctx?: any) => ${ - target >= 3.3 - ? `import('${lib}/jsx-runtime').JSX.Element` - : `globalThis.JSX.Element` - } & { - __ctx?: { - attrs?: any; - slots?: T extends { $slots: infer Slots } ? Slots : Record; - emit?: T extends { $emit: infer Emit } ? Emit : {}; - props?: typeof props; - expose?: (exposed: T) => void; - }; - }; - type __VLS_IsFunction = K extends keyof T - ? __VLS_IsAny extends false - ? unknown extends T[K] - ? false - : true - : false - : false; - type __VLS_NormalizeComponentEvent< - Props, - Emits, - onEvent extends keyof Props, - Event extends keyof Emits, - CamelizedEvent extends keyof Emits, - > = __VLS_IsFunction extends true - ? Props - : __VLS_IsFunction extends true - ? { [K in onEvent]?: Emits[Event] } - : __VLS_IsFunction extends true - ? { [K in onEvent]?: Emits[CamelizedEvent] } - : Props; - // fix https://github.com/vuejs/language-tools/issues/926 - type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; - type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R - ? U extends T - ? never - : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) - : never; - type __VLS_OverloadUnion = Exclude< - __VLS_OverloadUnionInner<(() => never) & T>, - T extends () => never ? never : () => never - >; - type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F - ? F extends (event: infer E, ...args: infer A) => any - ? { [K in E & string]: (...args: A) => void; } - : never - : never; - type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< - __VLS_UnionToIntersection< - __VLS_ConstructorOverloads & { - [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never - } - > - >; - type __VLS_EmitsToProps = __VLS_PrettifyGlobal<{ - [K in string & keyof T as \`on\${Capitalize}\`]?: - (...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never) => any; - }>; - type __VLS_ResolveEmits< - Comp, - Emits, - TypeEmits = ${ - target >= 3.6 - ? `Comp extends { __typeEmits?: infer T } ? unknown extends T ? {} : import('${lib}').ShortEmitsToObject : {}` - : `{}` - }, - NormalizedEmits = __VLS_NormalizeEmits extends infer E ? string extends keyof E ? {} : E : never, - > = __VLS_SpreadMerge; - type __VLS_ResolveDirectives = { - [K in keyof T & string as \`v\${Capitalize}\`]: T[K]; - }; - type __VLS_PrettifyGlobal = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {}; - type __VLS_UseTemplateRef = Readonly>; - type __VLS_ProxyRefs = import('${lib}').ShallowUnwrapRef; - - function __VLS_vFor(source: T): - T extends number ? [number, number][] - : T extends string ? [string, number][] - : T extends any[] ? [T[number], number][] - : T extends Iterable ? [V, number][] - : [T[keyof T], \`\${keyof T}\`, number][]; - function __VLS_vSlot(slot: S, decl?: D): - D extends (...args: infer P) => any ? P : any[]; - function __VLS_asFunctionalDirective(dir: T): T extends import('${lib}').ObjectDirective - ? NonNullable - : T extends (...args: any) => any - ? T - : (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; - function __VLS_asFunctionalComponent0 any ? InstanceType : unknown>(t: T, instance?: K): - T extends new (...args: any) => any ? __VLS_FunctionalComponent0 - : T extends () => any ? (props: {}, ctx?: any) => ReturnType - : T extends (...args: any) => any ? T - : __VLS_FunctionalComponent0<{}>; - function __VLS_asFunctionalComponent1 any ? InstanceType : unknown>(t: T, instance?: K): - T extends new (...args: any) => any ? __VLS_FunctionalComponent1 - : T extends () => any ? (props: {}, ctx?: any) => ReturnType - : T extends (...args: any) => any ? T - : __VLS_FunctionalComponent1<{}>; - function __VLS_functionalComponentArgsRest any>(t: T): 2 extends Parameters['length'] ? [any] : []; - function __VLS_asFunctionalElement0(tag: T, endTag?: T): (attrs: T) => void; - function __VLS_asFunctionalElement1(tag: T, endTag?: T): (attrs: T & Record) => void; - function __VLS_asFunctionalSlot(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable; - function __VLS_tryAsConstant(t: T): T; -}${newLine}`; - - return text; -} diff --git a/packages/language-core/lib/codegen/names.ts b/packages/language-core/lib/codegen/names.ts index 1c24eb6d7d..e7a3e9e4cd 100644 --- a/packages/language-core/lib/codegen/names.ts +++ b/packages/language-core/lib/codegen/names.ts @@ -11,7 +11,6 @@ export const setup = '__VLS_setup'; export const components = '__VLS_components'; export const directives = '__VLS_directives'; export const intrinsics = '__VLS_intrinsics'; -export const placeholder = '__VLS_placeholder'; export const _export = '__VLS_export'; export const ModelProps = '__VLS_ModelProps'; @@ -27,5 +26,3 @@ export const Emit = '__VLS_Emit'; export const SetupExposed = '__VLS_SetupExposed'; export const PublicProps = '__VLS_PublicProps'; export const StyleModules = '__VLS_StyleModules'; - -export const PROPS_FALLBACK = '__VLS_PROPS_FALLBACK'; diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index d878c46333..08e2bd4283 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -234,24 +234,31 @@ function* generateScriptWithExportDefault( yield* generateSfcBlockSection(script, exportDefault.end, script.content.length, codeFeatures.all); } -function* generateGlobalTypesReference(vueCompilerOptions: VueCompilerOptions, fileName: string): Generator { - const globalTypesPath = vueCompilerOptions.globalTypesPath(fileName); - if (!globalTypesPath) { - yield `/* placeholder */${newLine}`; - } - else if (path.isAbsolute(globalTypesPath)) { - let relativePath = path.relative(path.dirname(fileName), globalTypesPath); +function* generateGlobalTypesReference( + { typesRoot, lib, target, checkUnknownProps }: VueCompilerOptions, + fileName: string, +): Generator { + let typesPath: string; + if (path.isAbsolute(typesRoot)) { + let relativePath = path.relative(path.dirname(fileName), typesRoot); if ( - relativePath !== globalTypesPath + relativePath !== typesRoot && !relativePath.startsWith('./') && !relativePath.startsWith('../') ) { relativePath = './' + relativePath; } - yield `/// ${newLine}`; + typesPath = relativePath; } else { - yield `/// ${newLine}`; + typesPath = typesRoot; + } + yield `/// ${newLine}`; + if (!checkUnknownProps) { + yield `/// ${newLine}`; + } + if (lib === 'vue' && target < 3.5) { + yield `/// ${newLine}`; } } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 9820cd5241..a9bf1d99e7 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -98,10 +98,7 @@ export function* generateGeneric( if (propTypes.length) { yield ` & ${ctx.localTypes.PrettifyLocal}<${propTypes.join(` & `)}>`; } - if (!vueCompilerOptions.checkUnknownProps) { - yield ` & (typeof globalThis extends { ${names.PROPS_FALLBACK}: infer P } ? P : {})`; - } - yield endOfLine; + yield ` & (typeof globalThis extends { __VLS_PROPS_FALLBACK: infer P } ? P : {})${endOfLine}`; yield ` expose: (exposed: `; yield scriptSetupRanges.defineExpose ? `import('${vueCompilerOptions.lib}').ShallowUnwrapRef` @@ -219,7 +216,7 @@ export function* generateSetupFunction( yield `])`; }), replace(arg.start, arg.end, function*() { - yield names.placeholder; + yield `{} as any`; }), ); } @@ -253,7 +250,7 @@ export function* generateSetupFunction( yield `(`; }), insert(callExp.end, function*() { - yield ` as __VLS_UseTemplateRef<`; + yield ` as Readonly)`; + yield ` | null>>)`; }), ); if (arg) { transforms.push( replace(arg.start, arg.end, function*() { - yield names.placeholder; + yield `{} as any`; }), ); } diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 903c0944fc..8eaa49f9bf 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -80,7 +80,7 @@ function* generateTemplateCtx( } function* generateTemplateComponents( - { script, scriptRanges }: ScriptCodegenOptions, + { vueCompilerOptions, script, scriptRanges }: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { const types: string[] = []; @@ -102,11 +102,21 @@ function* generateTemplateComponents( } yield `type __VLS_LocalComponents = ${types.length ? types.join(` & `) : `{}`}${endOfLine}`; + yield `type __VLS_GlobalComponents = ${ + vueCompilerOptions.target >= 3.5 + ? `import('${vueCompilerOptions.lib}').GlobalComponents` + : `import('${vueCompilerOptions.lib}').GlobalComponents & Pick` + }${endOfLine}`; yield `let ${names.components}!: __VLS_LocalComponents & __VLS_GlobalComponents${endOfLine}`; + yield `let ${names.intrinsics}!: ${ + vueCompilerOptions.target >= 3.3 + ? `import('${vueCompilerOptions.lib}/jsx-runtime').JSX.IntrinsicElements` + : `globalThis.JSX.IntrinsicElements` + }${endOfLine}`; } function* generateTemplateDirectives( - { script, scriptRanges }: ScriptCodegenOptions, + { vueCompilerOptions, script, scriptRanges }: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { const types: string[] = []; @@ -128,11 +138,11 @@ function* generateTemplateDirectives( } yield `type __VLS_LocalDirectives = ${types.length ? types.join(` & `) : `{}`}${endOfLine}`; - yield `let ${names.directives}!: __VLS_LocalDirectives & __VLS_GlobalDirectives${endOfLine}`; + yield `let ${names.directives}!: __VLS_LocalDirectives & import('${vueCompilerOptions.lib}').GlobalDirectives${endOfLine}`; } function* generateSetupExposed( - { exposed }: ScriptCodegenOptions, + { vueCompilerOptions, exposed }: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { if (!exposed.size) { @@ -140,7 +150,7 @@ function* generateSetupExposed( } ctx.generatedTypes.add(names.SetupExposed); - yield `type ${names.SetupExposed} = __VLS_ProxyRefs<{${newLine}`; + yield `type ${names.SetupExposed} = import('${vueCompilerOptions.lib}').ShallowUnwrapRef<{${newLine}`; for (const bindingName of exposed) { const token = Symbol(bindingName.length); yield ['', undefined, 0, { __linkedToken: token }]; diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index caee57b5ed..fc435999af 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -115,7 +115,7 @@ export function* generateComponent( yield endOfLine; } else { - yield `let ${componentVar}!: __VLS_WithComponent<'${tag}', __VLS_LocalComponents`; + yield `let ${componentVar}!: __VLS_WithComponent<'${tag}', __VLS_LocalComponents, __VLS_GlobalComponents`; yield originalNames.has(options.componentName) ? `, typeof ${names._export}` : `, void`; diff --git a/packages/language-core/lib/codegen/template/elementDirectives.ts b/packages/language-core/lib/codegen/template/elementDirectives.ts index fb5e0ed038..17e634a6ca 100644 --- a/packages/language-core/lib/codegen/template/elementDirectives.ts +++ b/packages/language-core/lib/codegen/template/elementDirectives.ts @@ -40,7 +40,7 @@ export function* generateElementDirectives( const token = yield* startBoundary('template', prop.loc.start.offset, codeFeatures.verification); yield `__VLS_asFunctionalDirective(`; yield* generateIdentifier(options, ctx, prop); - yield `)(null!, { ...__VLS_directiveBindingRestFields, `; + yield `, {} as import('${options.vueCompilerOptions.lib}').ObjectDirective)(null!, { ...__VLS_directiveBindingRestFields, `; yield* generateArg(options, ctx, prop); yield* generateModifiers(options, ctx, prop); yield* generateValue(options, ctx, prop); diff --git a/packages/language-core/lib/compilerOptions.ts b/packages/language-core/lib/compilerOptions.ts index 483d3b257c..1b532f75ec 100644 --- a/packages/language-core/lib/compilerOptions.ts +++ b/packages/language-core/lib/compilerOptions.ts @@ -1,7 +1,6 @@ -import { camelize, NOOP as noop } from '@vue/shared'; +import { camelize } from '@vue/shared'; import { posix as path } from 'path-browserify'; import type * as ts from 'typescript'; -import { generateGlobalTypes, getGlobalTypesFileName } from './codegen/globalTypes'; import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from './types'; import { hyphenateTag } from './utils/shared'; @@ -113,9 +112,9 @@ export function createParsedCommandLine( } export class CompilerOptionsResolver { - options: Omit = {}; + options: Omit = {}; target: number | undefined; - globalTypesPath: string | undefined; + typesRoot: string | undefined; plugins: VueLanguagePlugin[] = []; constructor( @@ -141,9 +140,14 @@ export class CompilerOptionsResolver { this.options.checkUnknownDirectives ??= strict; this.options.checkUnknownComponents ??= strict; break; - case 'globalTypesPath': + case 'typesRoot': if (options[key] !== undefined) { - this.globalTypesPath = path.join(rootDir, options[key]); + if (path.isAbsolute(options[key])) { + this.typesRoot = options[key]; + } + else { + this.typesRoot = path.join(rootDir, options[key]); + } } break; case 'plugins': @@ -177,11 +181,17 @@ export class CompilerOptionsResolver { } } - build(defaults = getDefaultCompilerOptions()) { + build( + defaults = getDefaultCompilerOptions( + this.target, + this.options.lib, + undefined, + this.typesRoot, + ), + ): VueCompilerOptions { const resolvedOptions: VueCompilerOptions = { ...defaults, ...this.options, - target: this.target ?? defaults.target, plugins: this.plugins, macros: { ...defaults.macros, @@ -204,47 +214,8 @@ export class CompilerOptionsResolver { ), }; - if (resolvedOptions.globalTypesPath === noop) { - if (this.fileExists && this.globalTypesPath === undefined) { - const fileDirToGlobalTypesPath = new Map(); - resolvedOptions.globalTypesPath = fileName => { - const fileDir = path.dirname(fileName); - if (fileDirToGlobalTypesPath.has(fileDir)) { - return fileDirToGlobalTypesPath.get(fileDir); - } - - const root = this.findNodeModulesRoot(fileDir, resolvedOptions.lib); - const result = root - ? path.join( - root, - 'node_modules', - '.vue-global-types', - getGlobalTypesFileName(resolvedOptions), - ) - : undefined; - - fileDirToGlobalTypesPath.set(fileDir, result); - return result; - }; - } - else { - resolvedOptions.globalTypesPath = () => this.globalTypesPath; - } - } - return resolvedOptions; } - - private findNodeModulesRoot(dir: string, lib: string) { - while (!this.fileExists!(path.join(dir, 'node_modules', lib, 'package.json'))) { - const parentDir = path.dirname(dir); - if (dir === parentDir) { - return; - } - dir = parentDir; - } - return dir; - } } function findVueVersion(rootDir: string) { @@ -273,11 +244,18 @@ function resolvePath(scriptPath: string, root: string) { } } -export function getDefaultCompilerOptions(target = 99, lib = 'vue', strictTemplates = false): VueCompilerOptions { +export function getDefaultCompilerOptions( + target = 99, + lib = 'vue', + strictTemplates = false, + typesRoot = typeof __dirname !== 'undefined' + ? path.join(__dirname.replace(/\\/g, '/'), '..', 'types') + : '@vue/language-core/types', +): VueCompilerOptions { return { target, lib, - globalTypesPath: noop, + typesRoot, extensions: ['.vue'], vitePressExtensions: [], petiteVueExtensions: [], @@ -335,29 +313,3 @@ export function getDefaultCompilerOptions(target = 99, lib = 'vue', strictTempla }, }; } - -export function createGlobalTypesWriter( - vueOptions: VueCompilerOptions, - writeFile: (fileName: string, data: string) => void, -) { - const writed = new Set(); - const { globalTypesPath } = vueOptions; - return (fileName: string) => { - const result = globalTypesPath(fileName); - if (result && !writed.has(result)) { - writed.add(result); - writeFile(result, generateGlobalTypes(vueOptions)); - } - return result; - }; -} - -/** - * @deprecated use `createGlobalTypesWriter` instead - */ -export function writeGlobalTypes( - vueOptions: VueCompilerOptions, - writeFile: (fileName: string, data: string) => void, -) { - vueOptions.globalTypesPath = createGlobalTypesWriter(vueOptions, writeFile); -} diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index d9111ca1c6..60d3cdb8c4 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -9,10 +9,9 @@ export type { SFCParseResult } from '@vue/compiler-sfc'; export { VueEmbeddedCode }; -export type RawVueCompilerOptions = Partial> & { +export type RawVueCompilerOptions = Partial> & { strictTemplates?: boolean; target?: 'auto' | 3 | 3.3 | 3.5 | 3.6 | 99 | number; - globalTypesPath?: string; plugins?: string[]; }; @@ -27,7 +26,7 @@ export type Code = Segment; export interface VueCompilerOptions { target: number; lib: string; - globalTypesPath: (fileName: string) => string | void; + typesRoot: string; extensions: string[]; vitePressExtensions: string[]; petiteVueExtensions: string[]; diff --git a/packages/language-core/types/props-fallback.d.ts b/packages/language-core/types/props-fallback.d.ts new file mode 100644 index 0000000000..2e9c26ee39 --- /dev/null +++ b/packages/language-core/types/props-fallback.d.ts @@ -0,0 +1,5 @@ +declare global { + var __VLS_PROPS_FALLBACK: Record; +} + +export {}; diff --git a/packages/language-core/types/template-helpers.d.ts b/packages/language-core/types/template-helpers.d.ts new file mode 100644 index 0000000000..d1058ff2f9 --- /dev/null +++ b/packages/language-core/types/template-helpers.d.ts @@ -0,0 +1,146 @@ +declare global { + const __VLS_directiveBindingRestFields: { instance: null; oldValue: null; modifiers: any; dir: any }; + + type __VLS_Elements = __VLS_SpreadMerge; + type __VLS_IsAny = 0 extends 1 & T ? true : false; + type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; + type __VLS_SpreadMerge = Omit & B; + type __VLS_WithComponent< + N0 extends string, + LocalComponents, + GlobalComponents, + Self, + N1 extends string, + N2 extends string = N1, + N3 extends string = N1, + > = N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } + : N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } + : N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } + : Self extends object ? { [K in N0]: Self } + : N1 extends keyof GlobalComponents ? { [K in N0]: GlobalComponents[N1] } + : N2 extends keyof GlobalComponents ? { [K in N0]: GlobalComponents[N2] } + : N3 extends keyof GlobalComponents ? { [K in N0]: GlobalComponents[N3] } + : {}; + type __VLS_FunctionalComponentCtx = __VLS_PickNotAny< + '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? NonNullable : never : any, + T extends (props: any, ctx: infer Ctx) => any ? Ctx : any + >; + type __VLS_FunctionalComponentProps = '__ctx' extends keyof __VLS_PickNotAny + ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never + : T extends (props: infer P, ...args: any) => any ? P + : {}; + type __VLS_FunctionalComponent0 = (props: T extends { $props: infer Props } ? Props : {}, ctx?: any) => { + __ctx?: { + attrs?: any; + slots?: T extends { $slots: infer Slots } ? Slots : Record; + emit?: T extends { $emit: infer Emit } ? Emit : {}; + props?: typeof props; + expose?: (exposed: T) => void; + }; + }; + type __VLS_FunctionalComponent1 = ( + props: (T extends { $props: infer Props } ? Props : {}) & Record, + ctx?: any, + ) => { + __ctx?: { + attrs?: any; + slots?: T extends { $slots: infer Slots } ? Slots : Record; + emit?: T extends { $emit: infer Emit } ? Emit : {}; + props?: typeof props; + expose?: (exposed: T) => void; + }; + }; + type __VLS_IsFunction = K extends keyof T ? __VLS_IsAny extends false ? unknown extends T[K] ? false + : true + : false + : false; + type __VLS_NormalizeComponentEvent< + Props, + Emits, + onEvent extends keyof Props, + Event extends keyof Emits, + CamelizedEvent extends keyof Emits, + > = __VLS_IsFunction extends true ? Props + : __VLS_IsFunction extends true ? { [K in onEvent]?: Emits[Event] } + : __VLS_IsFunction extends true ? { [K in onEvent]?: Emits[CamelizedEvent] } + : Props; + // fix https://github.com/vuejs/language-tools/issues/926 + type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends + ((arg: infer P) => unknown) ? P : never; + type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R ? U extends T ? never + : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) + : never; + type __VLS_OverloadUnion = Exclude< + __VLS_OverloadUnionInner<(() => never) & T>, + T extends () => never ? never : () => never + >; + type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F + ? F extends (event: infer E, ...args: infer A) => any ? { [K in E & string]: (...args: A) => void } + : never + : never; + type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< + __VLS_UnionToIntersection< + & __VLS_ConstructorOverloads + & { + [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never; + } + > + >; + type __VLS_EmitsToProps = __VLS_PrettifyGlobal< + { + [K in string & keyof T as `on${Capitalize}`]?: ( + ...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never + ) => any; + } + >; + type __VLS_ShortEmitsToObject = E extends Record ? { [K in keyof E]: (...args: E[K]) => any } + : E; + type __VLS_ResolveEmits< + Comp, + Emits, + TypeEmits = Comp extends { __typeEmits?: infer T } ? unknown extends T ? {} : __VLS_ShortEmitsToObject : {}, + NormalizedEmits = __VLS_NormalizeEmits extends infer E ? string extends keyof E ? {} : E : never, + > = __VLS_SpreadMerge; + type __VLS_ResolveDirectives = { + [K in keyof T & string as `v${Capitalize}`]: T[K]; + }; + type __VLS_PrettifyGlobal = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {}; + + function __VLS_vFor(source: T): T extends number ? [number, number][] + : T extends string ? [string, number][] + : T extends any[] ? [T[number], number][] + : T extends Iterable ? [V, number][] + : [T[keyof T], `${keyof T & (string | number)}`, number][]; + function __VLS_vSlot(slot: S, decl?: D): D extends (...args: infer P) => any ? P : any[]; + function __VLS_asFunctionalDirective( + dir: T, + od: ObjectDirective, + ): T extends ObjectDirective ? NonNullable< + T[keyof T & ('created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted')] + > + : T extends (...args: any) => any ? T + : (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; + function __VLS_asFunctionalComponent0 any ? InstanceType : unknown>( + t: T, + instance?: K, + ): T extends new(...args: any) => any ? __VLS_FunctionalComponent0 + : T extends () => any ? (props: {}, ctx?: any) => ReturnType + : T extends (...args: any) => any ? T + : __VLS_FunctionalComponent0<{}>; + function __VLS_asFunctionalComponent1 any ? InstanceType : unknown>( + t: T, + instance?: K, + ): T extends new(...args: any) => any ? __VLS_FunctionalComponent1 + : T extends () => any ? (props: {}, ctx?: any) => ReturnType + : T extends (...args: any) => any ? T + : __VLS_FunctionalComponent1<{}>; + function __VLS_functionalComponentArgsRest any>( + t: T, + ): 2 extends Parameters['length'] ? [any] : []; + function __VLS_asFunctionalElement0(tag: T, endTag?: T): (attrs: T) => void; + function __VLS_asFunctionalElement1(tag: T, endTag?: T): (attrs: T & Record) => void; + function __VLS_asFunctionalSlot(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable; + function __VLS_tryAsConstant(t: T): T; +} + +export {}; diff --git a/packages/language-core/types/vue-3.4-shims.d.ts b/packages/language-core/types/vue-3.4-shims.d.ts new file mode 100644 index 0000000000..b6258a28df --- /dev/null +++ b/packages/language-core/types/vue-3.4-shims.d.ts @@ -0,0 +1,6 @@ +declare module 'vue' { + export interface GlobalComponents {} + export interface GlobalDirectives {} +} + +export {}; diff --git a/packages/language-service/index.ts b/packages/language-service/index.ts index 787ee00d94..2d0e6deb46 100644 --- a/packages/language-service/index.ts +++ b/packages/language-service/index.ts @@ -21,7 +21,6 @@ import { create as createVueDocumentDropPlugin } from './lib/plugins/vue-documen import { create as createVueDocumentHighlightsPlugin } from './lib/plugins/vue-document-highlights'; import { create as createVueExtractFilePlugin } from './lib/plugins/vue-extract-file'; import { create as createVueFormatPerBlockPlugin } from './lib/plugins/vue-format-per-block'; -import { create as createVueGlobalTypesErrorPlugin } from './lib/plugins/vue-global-types-error'; import { create as createVueInlayHintsPlugin } from './lib/plugins/vue-inlayhints'; import { create as createVueMissingPropsHintsPlugin } from './lib/plugins/vue-missing-props-hints'; import { create as createVueScopedClassLinksPlugin } from './lib/plugins/vue-scoped-class-links'; @@ -48,7 +47,6 @@ export function createVueLanguageServicePlugins( createVueAutoSpacePlugin(), createVueCompilerDomErrorsPlugin(), createVueDirectiveCommentsPlugin(), - createVueGlobalTypesErrorPlugin(), createVueScopedClassLinksPlugin(), createVueSfcPlugin(), createVueTemplateRefLinksPlugin(), diff --git a/packages/language-service/lib/plugins/vue-global-types-error.ts b/packages/language-service/lib/plugins/vue-global-types-error.ts deleted file mode 100644 index 7cfb9cf3d2..0000000000 --- a/packages/language-service/lib/plugins/vue-global-types-error.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { DiagnosticSeverity, LanguageServicePlugin } from '@volar/language-service'; -import { resolveEmbeddedCode } from '../utils'; - -export function create(): LanguageServicePlugin { - return { - name: 'vue-global-types-error', - capabilities: { - diagnosticProvider: { - interFileDependencies: false, - workspaceDiagnostics: false, - }, - }, - create(context) { - return { - provideDiagnostics(document) { - const info = resolveEmbeddedCode(context, document.uri); - if (info?.code.id !== 'root_tags') { - return; - } - if (info.script.id.scheme !== 'file') { - return; - } - - const { vueCompilerOptions } = info.root; - const globalTypesPath = vueCompilerOptions.globalTypesPath(info.root.fileName); - if (globalTypesPath) { - return; - } - - return [{ - range: { - start: document.positionAt(0), - end: document.positionAt(0), - }, - severity: 2 satisfies typeof DiagnosticSeverity.Warning, - code: 404, - source: 'vue', - message: ` -Failed to write the global types file. Make sure that: - -1. "node_modules" directory exists. -2. "${vueCompilerOptions.lib}" is installed as a direct dependency. - -Alternatively, you can manually set "vueCompilerOptions.globalTypesPath" in your "tsconfig.json" or "jsconfig.json". - -If all dependencies are installed, try running the "vue.action.restartServer" command to restart Vue and TS servers. - `.trim(), - }]; - }, - }; - }, - }; -} diff --git a/packages/tsc/index.ts b/packages/tsc/index.ts index 2be54a24d9..703a120d49 100644 --- a/packages/tsc/index.ts +++ b/packages/tsc/index.ts @@ -17,7 +17,6 @@ export function run(tscPath = require.resolve('typescript/lib/tsc')) { ? core.createParsedCommandLine(ts, ts.sys, configFilePath.replace(windowsPathReg, '/')).vueOptions : core.createParsedCommandLineByJson(ts, ts.sys, (options.host ?? ts.sys).getCurrentDirectory(), {}) .vueOptions; - vueOptions.globalTypesPath = core.createGlobalTypesWriter(vueOptions, ts.sys.writeFile); const allExtensions = core.getAllExtensions(vueOptions); if ( runExtensions.length === allExtensions.length diff --git a/packages/tsc/tests/dts.spec.ts b/packages/tsc/tests/dts.spec.ts index 031edf2ab6..0b908aab67 100644 --- a/packages/tsc/tests/dts.spec.ts +++ b/packages/tsc/tests/dts.spec.ts @@ -35,7 +35,6 @@ const createProgram = proxyCreateProgram(ts, ts.createProgram, (ts, options) => vueOptions.target = 99; vueOptions.extensions = ['vue', 'cext']; } - vueOptions.globalTypesPath = core.createGlobalTypesWriter(vueOptions, ts.sys.writeFile); const vueLanguagePlugin = core.createVueLanguagePlugin( ts, options.options, diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts index a9008031f6..eeaa7092f1 100644 --- a/packages/typescript-plugin/index.ts +++ b/packages/typescript-plugin/index.ts @@ -34,7 +34,6 @@ export = createLanguageServicePlugin( vueOptions, id => id, ); - vueOptions.globalTypesPath = core.createGlobalTypesWriter(vueOptions, ts.sys.writeFile); addVueCommands(); let _language: core.Language | undefined; @@ -290,12 +289,12 @@ export = createLanguageServicePlugin( session.addProtocolHandler('_vue:getElementAttrs', request => { const [fileName, tag]: Parameters = request.arguments; const { project } = getProject(fileName); - return createResponse(getElementAttrs(ts, project.getLanguageService().getProgram()!, tag)); + return createResponse(getElementAttrs(ts, project.getLanguageService().getProgram()!, fileName, tag)); }); session.addProtocolHandler('_vue:getElementNames', request => { const [fileName]: Parameters = request.arguments; const { project } = getProject(fileName); - return createResponse(getElementNames(ts, project.getLanguageService().getProgram()!)); + return createResponse(getElementNames(ts, project.getLanguageService().getProgram()!, fileName)); }); session.addProtocolHandler('_vue:resolveModuleName', request => { const [fileName, moduleName]: Parameters = request.arguments; diff --git a/packages/typescript-plugin/lib/requests/getElementAttrs.ts b/packages/typescript-plugin/lib/requests/getElementAttrs.ts index 7e0f213297..53db2d9053 100644 --- a/packages/typescript-plugin/lib/requests/getElementAttrs.ts +++ b/packages/typescript-plugin/lib/requests/getElementAttrs.ts @@ -1,18 +1,25 @@ import { names } from '@vue/language-core'; import type * as ts from 'typescript'; +import { getVariableType } from './utils'; export function getElementAttrs( ts: typeof import('typescript'), program: ts.Program, + fileName: string, tag: string, ): string[] { + const sourceFile = program.getSourceFile(fileName); + if (!sourceFile) { + return []; + } + const checker = program.getTypeChecker(); - const elements = checker.resolveName(names.intrinsics, undefined, ts.SymbolFlags.Variable, false); + const elements = getVariableType(ts, checker, sourceFile, names.intrinsics); if (!elements) { return []; } - const elementType = checker.getTypeOfSymbol(elements).getProperty(tag); + const elementType = elements.type.getProperty(tag); if (!elementType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getElementNames.ts b/packages/typescript-plugin/lib/requests/getElementNames.ts index 523229dd54..55f97f3ed9 100644 --- a/packages/typescript-plugin/lib/requests/getElementNames.ts +++ b/packages/typescript-plugin/lib/requests/getElementNames.ts @@ -1,15 +1,22 @@ import { names } from '@vue/language-core'; import type * as ts from 'typescript'; +import { getVariableType } from './utils'; export function getElementNames( ts: typeof import('typescript'), program: ts.Program, + fileName: string, ): string[] { + const sourceFile = program.getSourceFile(fileName); + if (!sourceFile) { + return []; + } + const checker = program.getTypeChecker(); - const elements = checker.resolveName(names.intrinsics, undefined, ts.SymbolFlags.Variable, false); + const elements = getVariableType(ts, checker, sourceFile, names.intrinsics); if (!elements) { return []; } - return checker.getTypeOfSymbol(elements).getProperties().map(c => c.name); + return elements.type.getProperties().map(c => c.name); }