Skip to content

Commit

Permalink
refactor: write real files to FS for shared global types (#4736)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk authored Aug 28, 2024
1 parent a162f04 commit 38c1e48
Show file tree
Hide file tree
Showing 29 changed files with 608 additions and 648 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
},
"pnpm": {
"overrides": {
"@volar/kit": "https://pkg.pr.new/volarjs/volar.js/@volar/kit@28cbdee",
"@volar/language-core": "https://pkg.pr.new/volarjs/volar.js/@volar/language-core@28cbdee",
"@volar/language-server": "https://pkg.pr.new/volarjs/volar.js/@volar/language-server@28cbdee",
"@volar/language-service": "https://pkg.pr.new/volarjs/volar.js/@volar/language-service@28cbdee",
"@volar/source-map": "https://pkg.pr.new/volarjs/volar.js/@volar/source-map@28cbdee",
"@volar/test-utils": "https://pkg.pr.new/volarjs/volar.js/@volar/test-utils@28cbdee",
"@volar/typescript": "https://pkg.pr.new/volarjs/volar.js/@volar/typescript@28cbdee",
"@volar/vscode": "https://pkg.pr.new/volarjs/volar.js/@volar/vscode@28cbdee",
"@volar/kit": "https://pkg.pr.new/volarjs/volar.js/@volar/kit@0e1be44",
"@volar/language-core": "https://pkg.pr.new/volarjs/volar.js/@volar/language-core@0e1be44",
"@volar/language-server": "https://pkg.pr.new/volarjs/volar.js/@volar/language-server@0e1be44",
"@volar/language-service": "https://pkg.pr.new/volarjs/volar.js/@volar/language-service@0e1be44",
"@volar/source-map": "https://pkg.pr.new/volarjs/volar.js/@volar/source-map@0e1be44",
"@volar/test-utils": "https://pkg.pr.new/volarjs/volar.js/@volar/test-utils@0e1be44",
"@volar/typescript": "https://pkg.pr.new/volarjs/volar.js/@volar/typescript@0e1be44",
"@volar/vscode": "https://pkg.pr.new/volarjs/volar.js/@volar/vscode@0e1be44",
"volar-service-typescript": "https://pkg.pr.new/volarjs/services/volar-service-typescript@177b9ed",
"inquirer": "9.2.23"
}
Expand Down
46 changes: 33 additions & 13 deletions packages/component-meta/lib/base.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript';
import * as vue from '@vue/language-core';
import type * as ts from 'typescript';
import * as path from 'path-browserify';
import type * as ts from 'typescript';
import { code as typeHelpersCode } from 'vue-component-type-helpers';
import { code as vue2TypeHelpersCode } from 'vue-component-type-helpers/vue2';
import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript';

import type {
MetaCheckerOptions,
ComponentMeta,
Declaration,
EventMeta,
ExposeMeta,
MetaCheckerOptions,
PropertyMeta,
PropertyMetaSchema,
SlotMeta,
Declaration
SlotMeta
} from './types';

export * from './types';
Expand Down Expand Up @@ -83,16 +83,11 @@ export function baseCreate(
];
};

const vueLanguagePlugin = vue.createVueLanguagePlugin2<string>(
const vueLanguagePlugin = vue.createVueLanguagePlugin<string>(
ts,
id => id,
vue.createRootFileChecker(
projectHost.getProjectVersion ? () => projectHost.getProjectVersion!() : undefined,
() => projectHost.getScriptFileNames(),
ts.sys.useCaseSensitiveFileNames
),
projectHost.getCompilationSettings(),
commandLine.vueOptions
commandLine.vueOptions,
id => id
);
const language = vue.createLanguage(
[
Expand Down Expand Up @@ -140,6 +135,31 @@ export function baseCreate(
const { languageServiceHost } = createLanguageServiceHost(ts, ts.sys, language, s => s, projectHost);
const tsLs = ts.createLanguageService(languageServiceHost);

const fileExists = languageServiceHost.fileExists.bind(languageServiceHost);
const getScriptSnapshot = languageServiceHost.getScriptSnapshot.bind(languageServiceHost);
const globalTypesName = `__globalTypes_${commandLine.vueOptions.target}_${commandLine.vueOptions.strictTemplates}.d.ts`;
const snapshots = new Map<string, ts.IScriptSnapshot>();
languageServiceHost.fileExists = path => {
if (path.endsWith(globalTypesName)) {
return true;
}
return fileExists(path);
};
languageServiceHost.getScriptSnapshot = path => {
if (path.endsWith(globalTypesName)) {
if (!snapshots.has(path)) {
const contents = vue.generateGlobalTypes(commandLine.vueOptions.lib, commandLine.vueOptions.target, commandLine.vueOptions.strictTemplates);
snapshots.set(path, {
getText: (start, end) => contents.substring(start, end),
getLength: () => contents.length,
getChangeRange: () => undefined,
});
}
return snapshots.get(path)!;
}
return getScriptSnapshot(path);
};

if (checkerOptions.forceUseTs) {
const getScriptKind = languageServiceHost.getScriptKind?.bind(languageServiceHost);
languageServiceHost.getScriptKind = fileName => {
Expand Down
7 changes: 4 additions & 3 deletions packages/language-core/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
export * from './lib/codegen/globalTypes';
export * from './lib/codegen/template';
export * from './lib/languagePlugin';
export * from './lib/parsers/scriptSetupRanges';
export * from './lib/plugins';
export * from './lib/virtualFile/vueFile';
export * from './lib/types';
export * from './lib/utils/ts';
export * from './lib/utils/parseSfc';
export * from './lib/utils/ts';
export * from './lib/virtualFile/vueFile';

export * as scriptRanges from './lib/parsers/scriptRanges';
export * from './lib/utils/shared';
export { tsCodegen } from './lib/plugins/vue-tsx';
export * from './lib/utils/shared';

export * from '@volar/language-core';
export type * as CompilerDOM from '@vue/compiler-dom';
125 changes: 125 additions & 0 deletions packages/language-core/lib/codegen/globalTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
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>'}`;
return `
const __VLS_globalComponents = { ...{} as import('${lib}').GlobalComponents };
declare const __VLS_intrinsicElements: __VLS_IntrinsicElements;
declare const __VLS_directiveBindingRestFields = { instance: null, oldValue: null, modifiers: null as any, dir: null as any };
type __VLS_IntrinsicElements = ${(
target >= 3.3
? `import('${lib}/jsx-runtime').JSX.IntrinsicElements;`
: `globalThis.JSX.IntrinsicElements;`
)}
type __VLS_Element = ${(
target >= 3.3
? `import('${lib}/jsx-runtime').JSX.Element;`
: `globalThis.JSX.Element;`
)}
type __VLS_GlobalComponents = ${(
target >= 3.5
? `void extends typeof __VLS_globalComponents ? {} : typeof __VLS_globalComponents;`
: `(void extends typeof __VLS_globalComponents ? {} : typeof __VLS_globalComponents) & Pick<typeof import('${lib}'), 'Transition' | 'TransitionGroup' | 'KeepAlive' | 'Suspense' | 'Teleport'>;`
)}
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
type __VLS_WithComponent<N0 extends string, LocalComponents, N1 extends string, N2 extends string, N3 extends string> =
N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N3] } :
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 }'}
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 :
{};
type __VLS_IsFunction<T, K> = K extends keyof T
? __VLS_IsAny<T[K]> extends false
? unknown extends T[K]
? false
: true
: false
: false;
// 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
? U extends T
? never
: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
: never;
type __VLS_OverloadUnion<T> = Exclude<
__VLS_OverloadUnionInner<(() => never) & T>,
T extends () => never ? never : () => never
>;
type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F
? F extends (event: infer E, ...args: infer A) => any
? { [K in E & string]: (...args: A) => void; }
: never
: never;
type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal<
__VLS_UnionToIntersection<
__VLS_ConstructorOverloads<T> & {
[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
}
>
>;
type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
declare function __VLS_getVForSourceType(source: number): [number, number, number][];
declare function __VLS_getVForSourceType(source: string): [string, number, number][];
declare function __VLS_getVForSourceType<T extends any[]>(source: T): [
item: T[number],
key: number,
index: number,
][];
declare function __VLS_getVForSourceType<T extends { [Symbol.iterator](): Iterator<any> }>(source: T): [
item: T extends { [Symbol.iterator](): Iterator<infer T1> } ? T1 : never,
key: number,
index: undefined,
][];
// #3845
declare function __VLS_getVForSourceType<T extends number | { [Symbol.iterator](): Iterator<any> }>(source: T): [
item: number | (Exclude<T, number> extends { [Symbol.iterator](): Iterator<infer T1> } ? T1 : never),
key: number,
index: undefined,
][];
declare function __VLS_getVForSourceType<T>(source: T): [
item: T[keyof T],
key: keyof T,
index: number,
][];
// @ts-ignore
declare function __VLS_getSlotParams<T>(slot: T): Parameters<__VLS_PickNotAny<NonNullable<T>, (...args: any[]) => any>>;
// @ts-ignore
declare function __VLS_getSlotParam<T>(slot: T): Parameters<__VLS_PickNotAny<NonNullable<T>, (...args: any[]) => any>>[0];
declare function __VLS_directiveAsFunction<T extends import('${lib}').Directive>(dir: T): T extends (...args: any) => any
? T | __VLS_unknownDirective
: NonNullable<(T & Record<string, __VLS_unknownDirective>)['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>;
declare function __VLS_withScope<T, K>(ctx: T, scope: K): ctx is T & K;
declare function __VLS_makeOptional<T>(t: T): { [K in keyof T]?: T[K] };
declare function __VLS_nonNullable<T>(t: T): T extends null | undefined ? never : T;
declare function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
T extends new (...args: any) => any
? (props: ${fnPropsType}, ctx?: any) => __VLS_Element & { __ctx?: {
attrs?: any,
slots?: K extends { ${getSlotsPropertyName(target)}: infer Slots } ? Slots : any,
emit?: K extends { $emit: infer Emit } ? Emit : any
} & { 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>'} } };
declare function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
declare function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny<
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
>>;
declare function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
declare function __VLS_tryAsConstant<const T>(t: T): T;
`;
};
148 changes: 148 additions & 0 deletions packages/language-core/lib/codegen/localTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import type * as ts from 'typescript';
import { VueCompilerOptions } from '../types';
import { getSlotsPropertyName } from '../utils/shared';
import { endOfLine } from './common';

export function getLocalTypesGenerator(compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions) {
const used = new Set<string>();

const OmitKeepDiscriminatedUnion = defineHelper(
`__VLS_OmitKeepDiscriminatedUnion`,
() => `
type __VLS_OmitKeepDiscriminatedUnion<T, K extends keyof any> = T extends any
? Pick<T, Exclude<keyof T, K>>
: never;
`.trimStart()
);
const WithDefaults = defineHelper(
`__VLS_WithDefaults`,
() => `
type __VLS_WithDefaults<P, D> = {
[K in keyof Pick<P, keyof P>]: K extends keyof D
? ${PrettifyLocal.name}<P[K] & { default: D[K]}>
: P[K]
};
`.trimStart()
);
const PrettifyLocal = defineHelper(
`__VLS_PrettifyLocal`,
() => `type __VLS_PrettifyLocal<T> = { [K in keyof T]: T[K]; } & {}${endOfLine}`
);
const WithTemplateSlots = defineHelper(
`__VLS_WithTemplateSlots`,
() => `
type __VLS_WithTemplateSlots<T, S> = T & {
new(): {
${getSlotsPropertyName(vueCompilerOptions.target)}: S;
${vueCompilerOptions.jsxSlots ? `$props: ${PropsChildren.name}<S>;` : ''}
}
};
`.trimStart()
);
const PropsChildren = defineHelper(
`__VLS_PropsChildren`,
() => `
type __VLS_PropsChildren<S> = {
[K in keyof (
boolean extends (
// @ts-ignore
JSX.ElementChildrenAttribute extends never
? true
: false
)
? never
// @ts-ignore
: JSX.ElementChildrenAttribute
)]?: S;
};
`.trimStart()
);
const TypePropsToOption = defineHelper(
`__VLS_TypePropsToOption`,
() => compilerOptions.exactOptionalPropertyTypes ?
`
type __VLS_TypePropsToOption<T> = {
[K in keyof T]-?: {} extends Pick<T, K>
? { type: import('${vueCompilerOptions.lib}').PropType<T[K]> }
: { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true }
};
`.trimStart() :
`
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
type __VLS_TypePropsToOption<T> = {
[K in keyof T]-?: {} extends Pick<T, K>
? { type: import('${vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable<T[K]>> }
: { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true }
};
`.trimStart()
);
const OmitIndexSignature = defineHelper(
`__VLS_OmitIndexSignature`,
() => `type __VLS_OmitIndexSignature<T> = { [K in keyof T as {} extends Record<K, unknown> ? never : K]: T[K]; }${endOfLine}`
);
const PickRefsExpose = defineHelper(
`__VLS_PickRefsExpose`,
() => `
type __VLS_PickRefsExpose<T> = T extends object
? { [K in keyof T]: (T[K] extends any[]
? Parameters<T[K][0]['expose']>[0][]
: T[K] extends { expose?: (exposed: infer E) => void }
? E
: T[K]) | null }
: never;
`.trimStart()
);

const helpers = {
[PrettifyLocal.name]: PrettifyLocal,
[OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion,
[WithDefaults.name]: WithDefaults,
[WithTemplateSlots.name]: WithTemplateSlots,
[PropsChildren.name]: PropsChildren,
[TypePropsToOption.name]: TypePropsToOption,
[OmitIndexSignature.name]: OmitIndexSignature,
[PickRefsExpose.name]: PickRefsExpose,
};
used.clear();

return {
generate,
getUsedNames() {
return used;
},
get PrettifyLocal() { return PrettifyLocal.name; },
get OmitKeepDiscriminatedUnion() { return OmitKeepDiscriminatedUnion.name; },
get WithDefaults() { return WithDefaults.name; },
get WithTemplateSlots() { return WithTemplateSlots.name; },
get PropsChildren() { return PropsChildren.name; },
get TypePropsToOption() { return TypePropsToOption.name; },
get OmitIndexSignature() { return OmitIndexSignature.name; },
get PickRefsExpose() { return PickRefsExpose.name; },
};

function* generate(names: string[]) {
const generated = new Set<string>();
while (names.length) {
used.clear();
for (const name of names) {
if (generated.has(name)) {
continue;
}
const helper = helpers[name as keyof typeof helpers];
yield helper.generate();
generated.add(name);
}
names = [...used].filter(name => !generated.has(name));
}
}

function defineHelper(name: string, generate: () => string) {
return {
get name() {
used.add(name);
return name;
},
generate,
};
}
}
Loading

0 comments on commit 38c1e48

Please sign in to comment.