Skip to content

Commit

Permalink
feat(language-core): typed fallthrough attributes (#4103)
Browse files Browse the repository at this point in the history
Co-authored-by: A5rocks <[email protected]>
Co-authored-by: Johnson Chu <[email protected]>
  • Loading branch information
3 people authored Aug 25, 2024
1 parent 21e3b71 commit 9da831f
Show file tree
Hide file tree
Showing 23 changed files with 264 additions and 34 deletions.
30 changes: 24 additions & 6 deletions packages/language-core/lib/codegen/script/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function* generateComponent(
yield `}${endOfLine}`;
yield `},${newLine}`;
if (!ctx.bypassDefineComponent) {
yield* generateScriptSetupOptions(options, ctx, scriptSetup, scriptSetupRanges);
yield* generateScriptSetupOptions(options, ctx, scriptSetup, scriptSetupRanges, true);
}
if (options.sfc.script && options.scriptRanges) {
yield* generateScriptOptions(options.sfc.script, options.scriptRanges);
Expand Down Expand Up @@ -65,24 +65,42 @@ export function* generateScriptSetupOptions(
options: ScriptCodegenOptions,
ctx: ScriptCodegenContext,
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges
scriptSetupRanges: ScriptSetupRanges,
inheritAttrs: boolean
): Generator<Code> {
yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges);
yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, inheritAttrs);
yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges);
}

export function* generatePropsOption(
options: ScriptCodegenOptions,
ctx: ScriptCodegenContext,
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges
scriptSetupRanges: ScriptSetupRanges,
inheritAttrs: boolean
) {
if (options.vueCompilerOptions.target >= 3.5 && ctx.generatedPropsType) {
yield `__typeProps: {} as __VLS_PublicProps,${newLine}`;

if (options.vueCompilerOptions.target >= 3.5) {
const types = [];
if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
types.push('typeof __VLS_template>[1]');
}
if (ctx.generatedPropsType) {
types.push('{} as __VLS_PublicProps');
}
if (types.length) {
yield `__typeProps: ${types.join(' & ')},${newLine}`;
}
}
if (options.vueCompilerOptions.target < 3.5 || !ctx.generatedPropsType || scriptSetupRanges.props.withDefaults) {
const codegens: (() => Generator<Code>)[] = [];

if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
codegens.push(function* () {
yield `{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<ReturnType<typeof __VLS_template>[1]>, {}>>`;
});
}

if (ctx.generatedPropsType) {
codegens.push(function* () {
yield `{} as `;
Expand Down
9 changes: 9 additions & 0 deletions packages/language-core/lib/codegen/script/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ export function createScriptCodegenContext(options: ScriptCodegenOptions) {
};`;
},
} satisfies HelperType as HelperType,
OmitIndexSignature: {
get name() {
this.used = true;
return `__VLS_OmitIndexSignature`;
},
get code() {
return `type __VLS_OmitIndexSignature<T> = { [K in keyof T as {} extends Record<K, unknown> ? never : K]: T[K]; };`;
}
} satisfies HelperType as HelperType,
};
const inlayHints: InlayHintInfo[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function* generateInternalComponent(
yield `}${endOfLine}`; // return {
yield `},${newLine}`; // setup() {
if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) {
yield* generateScriptSetupOptions(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges);
yield* generateScriptSetupOptions(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges, false);
}
if (options.sfc.script && options.scriptRanges) {
yield* generateScriptOptions(options.sfc.script, options.scriptRanges);
Expand Down
4 changes: 2 additions & 2 deletions packages/language-core/lib/codegen/script/scriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function* generateScriptSetup(
+ ` props: ${ctx.helperTypes.Prettify.name}<typeof __VLS_functionalComponentProps & __VLS_PublicProps> & __VLS_BuiltInPublicProps,${newLine}`
+ ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}`
+ ` attrs: any,${newLine}`
+ ` slots: ReturnType<typeof __VLS_template>,${newLine}`
+ ` slots: ReturnType<typeof __VLS_template>[0],${newLine}`
+ ` emit: ${emitTypes.join(' & ')},${newLine}`
+ ` }${endOfLine}`;
yield ` })(),${newLine}`; // __VLS_setup = (async () => {
Expand Down Expand Up @@ -250,7 +250,7 @@ function* generateSetupFunction(
yield* generateComponent(options, ctx, scriptSetup, scriptSetupRanges);
yield endOfLine;
yield `${syntax} `;
yield `{} as ${ctx.helperTypes.WithTemplateSlots.name}<typeof __VLS_component, ReturnType<typeof __VLS_template>>${endOfLine}`;
yield `{} as ${ctx.helperTypes.WithTemplateSlots.name}<typeof __VLS_component, ReturnType<typeof __VLS_template>[0]>${endOfLine}`;
}
else {
yield `${syntax} `;
Expand Down
5 changes: 3 additions & 2 deletions packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function* generateTemplate(
const templateUsageVars = [...getTemplateUsageVars(options, ctx)];
yield `// @ts-ignore${newLine}`;
yield `[${templateUsageVars.join(', ')}]${newLine}`;
yield `return {}${endOfLine}`;
yield `return [{}, {}] as const${endOfLine}`;
yield `}${newLine}`;
}
}
Expand Down Expand Up @@ -154,10 +154,11 @@ function* generateTemplateContext(
yield `// no template${newLine}`;
if (!options.scriptSetupRanges?.slots.define) {
yield `const __VLS_slots = {}${endOfLine}`;
yield `const __VLS_inheritedAttrs = {}${endOfLine}`;
}
}

yield `return ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'}${endOfLine}`;
yield `return [${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'}, __VLS_inheritedAttrs] as const${endOfLine}`;
}

function* generateCssClassProperty(
Expand Down
2 changes: 2 additions & 0 deletions packages/language-core/lib/codegen/template/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo
emptyClassOffsets,
inlayHints,
hasSlot: false,
inheritedAttrVars: new Set(),
singleRootNode: undefined as CompilerDOM.ElementNode | undefined,
accessExternalVariable(name: string, offset?: number) {
let arr = accessExternalVariables.get(name);
if (!arr) {
Expand Down
18 changes: 17 additions & 1 deletion packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export function* generateComponent(
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
}
else {
// without strictTemplates, this only for instacne type
// without strictTemplates, this only for instance type
yield `const ${var_componentInstance} = ${var_functionalComponent}({`;
yield* generateElementProps(options, ctx, node, props, false);
yield `}, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
Expand Down Expand Up @@ -271,6 +271,15 @@ export function* generateComponent(
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
}

if (
node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs')
|| node === ctx.singleRootNode
) {
const varAttrs = ctx.getInternalVariable();
ctx.inheritedAttrVars.add(varAttrs);
yield `var ${varAttrs}!: Parameters<typeof ${var_functionalComponent}>[0];\n`;
}

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir) {
yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, componentCtxVar);
Expand Down Expand Up @@ -349,6 +358,13 @@ export function* generateElement(
else {
yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar);
}

if (
node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs')
|| node === ctx.singleRootNode
) {
ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`);
}
}

function* generateVScope(
Expand Down
11 changes: 11 additions & 0 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface TemplateCodegenOptions {
hasDefineSlots?: boolean;
slotsAssignName?: string;
propsAssignName?: string;
inheritAttrs: boolean;
}

export function* generateTemplate(options: TemplateCodegenOptions): Generator<Code, TemplateCodegenContext> {
Expand All @@ -43,6 +44,8 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
yield endOfLine;
}

yield* generateInheritedAttrs();

yield* ctx.generateAutoImportCompletion();

return ctx;
Expand Down Expand Up @@ -78,6 +81,14 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
yield `}`;
}

function* generateInheritedAttrs(): Generator<Code> {
yield 'var __VLS_inheritedAttrs!: {}';
for (const varName of ctx.inheritedAttrVars) {
yield ` & typeof ${varName}`;
}
yield endOfLine;
}

function* generateStyleScopedClasses(): Generator<Code> {
yield `if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {${newLine}`;
for (const offset of ctx.emptyClassOffsets) {
Expand Down
5 changes: 5 additions & 0 deletions packages/language-core/lib/codegen/template/templateChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ export function* generateTemplateChild(
}
}

const shouldInheritRootNodeAttrs = options.inheritAttrs;

if (node.type === CompilerDOM.NodeTypes.ROOT) {
let prev: CompilerDOM.TemplateChildNode | undefined;
if (shouldInheritRootNodeAttrs && node.children.length === 1 && node.children[0].type === CompilerDOM.NodeTypes.ELEMENT) {
ctx.singleRootNode = node.children[0];
}
for (const childNode of node.children) {
yield* generateTemplateChild(options, ctx, childNode, currentComponent, prev, componentCtxVar);
prev = childNode;
Expand Down
6 changes: 6 additions & 0 deletions packages/language-core/lib/parsers/scriptRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
componentsOption: TextRange | undefined,
componentsOptionNode: ts.ObjectLiteralExpression | undefined,
nameOption: TextRange | undefined,
inheritAttrsOption: string | undefined,
}) | undefined;
let classBlockEnd: number | undefined;

Expand Down Expand Up @@ -40,6 +41,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
if (obj) {
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
let nameOptionNode: ts.Expression | undefined;
let inheritAttrsOption: string | undefined;
ts.forEachChild(obj, node => {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
const name = getNodeText(ts, node.name, ast);
Expand All @@ -49,6 +51,9 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
if (name === 'name') {
nameOptionNode = node.initializer;
}
if (name === 'inheritAttrs') {
inheritAttrsOption = getNodeText(ts, node.initializer, ast);
}
}
});
exportDefault = {
Expand All @@ -59,6 +64,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
componentsOption: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
componentsOptionNode: withNode ? componentsOptionNode : undefined,
nameOption: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
inheritAttrsOption,
};
}
}
Expand Down
12 changes: 11 additions & 1 deletion packages/language-core/lib/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function parseScriptSetupRanges(
} = {};
const options: {
name?: string;
inheritAttrs?: string;
} = {};

const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
Expand Down Expand Up @@ -101,8 +102,8 @@ export function parseScriptSetupRanges(
slots,
emits,
expose,
defineProp,
options,
defineProp,
};

function _getStartEnd(node: ts.Node) {
Expand Down Expand Up @@ -282,6 +283,15 @@ export function parseScriptSetupRanges(
}
else if (vueCompilerOptions.macros.defineOptions.includes(callText)) {
if (node.arguments.length && ts.isObjectLiteralExpression(node.arguments[0])) {
const obj = node.arguments[0];
ts.forEachChild(obj, node => {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
const name = getNodeText(ts, node.name, ast);
if (name === 'inheritAttrs') {
options.inheritAttrs = getNodeText(ts, node.initializer, ast);
}
}
});
for (const prop of node.arguments[0].properties) {
if ((ts.isPropertyAssignment(prop)) && getNodeText(ts, prop.name, ast) === 'name' && ts.isStringLiteral(prop.initializer)) {
options.name = prop.initializer.text;
Expand Down
5 changes: 5 additions & 0 deletions packages/language-core/lib/plugins/vue-tsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ function createTsx(
hasDefineSlots: hasDefineSlots(),
slotsAssignName: slotsAssignName(),
propsAssignName: propsAssignName(),
inheritAttrs: inheritAttrs(),
});

let current = codegen.next();
Expand Down Expand Up @@ -135,6 +136,10 @@ function createTsx(
});
const slotsAssignName = computed(() => scriptSetupRanges()?.slots.name);
const propsAssignName = computed(() => scriptSetupRanges()?.props.name);
const inheritAttrs = computed(() => {
const value = scriptSetupRanges()?.options.inheritAttrs ?? scriptRanges()?.exportDefault?.inheritAttrsOption;
return value !== 'false';
});
const generatedScript = computed(() => {
const codes: Code[] = [];
const linkedCodeMappings: Mapping[] = [];
Expand Down
Loading

0 comments on commit 9da831f

Please sign in to comment.