UNPKG

@vue-macros/volar

Version:

Volar plugin for Vue Macros.

1,026 lines (1,010 loc) 35.6 kB
import { addCode, getStart, getText, isJsxExpression } from "./chunk-DCLIBDVT.js"; // src/jsx-directive.ts import { createFilter } from "@vue-macros/common"; import { createPlugin } from "ts-macro"; // src/jsx-directive/index.ts import { replaceSourceRange as replaceSourceRange10 } from "muggle-string"; // src/jsx-directive/context.ts import { replaceSourceRange as replaceSourceRange2 } from "muggle-string"; // src/jsx-directive/v-model.ts import { replaceSourceRange } from "muggle-string"; import { allCodeFeatures } from "ts-macro"; var isNativeFormElement = (tag) => { return ["input", "select", "textarea"].includes(tag); }; function transformVModel(nodeMap, ctxMap, options) { if (!nodeMap.size) return; for (const [, nodes] of nodeMap) { transform(nodes, ctxMap, options); } getModelsType(options.codes); } function transform(nodes, ctxMap, options) { const { codes, ts, source, prefix } = options; let firstNamespacedNode; const result = []; const emitsResult = []; const modifiersResult = []; const offset = `${prefix}model`.length + 1; for (const { attribute: attribute2, node: node2 } of nodes) { const isNative = isNativeFormElement(getTagName(node2, options)); const modelValue = isNative ? "value" : "modelValue"; const isArrayExpression = isJsxExpression(attribute2.initializer) && attribute2.initializer.expression && ts.isArrayLiteralExpression(attribute2.initializer.expression); const name = getText(attribute2.name, options); const start = getStart(attribute2.name, options); if (name.startsWith(`${prefix}model:`) || isArrayExpression) { let isDynamic = false; const [attributeName2, ...modifiers] = name.slice(offset).split(/\s/)[0].replace(/^\$(.*)\$/, (_, $1) => { isDynamic = true; return $1.replaceAll("_", "."); }).split("_"); firstNamespacedNode ??= { attribute: attribute2, attributeName: attributeName2, node: node2 }; if (firstNamespacedNode.attribute !== attribute2) { replaceSourceRange( codes, source, getStart(attribute2, options), attribute2.end ); result.push(","); } if (isArrayExpression) { const { elements } = attribute2.initializer.expression; if (elements[1] && !ts.isArrayLiteralExpression(elements[1])) { isDynamic = !ts.isStringLiteral(elements[1]); result.push( isDynamic ? "[`${" : "", [ getText(elements[1], options), source, getStart(elements[1], options), allCodeFeatures ], isDynamic ? "}`]" : "" ); } else { result.push(modelValue); } if (elements[0]) result.push(":", [ getText(elements[0], options), source, getStart(elements[0], options), allCodeFeatures ]); } else { result.push( isDynamic ? "[`${" : "", ...attributeName2.split("-").map((code, index, codes2) => [ index ? code.at(0)?.toUpperCase() + code.slice(1) : code, source, start + offset + (isDynamic ? 1 : 0) + (index && codes2[index - 1].length + 1), allCodeFeatures ]), isDynamic ? "}`]" : "" ); if (attributeName2) { if (isJsxExpression(attribute2?.initializer) && attribute2.initializer.expression) { result.push(":", [ getText(attribute2.initializer.expression, options), source, getStart(attribute2.initializer.expression, options), allCodeFeatures ]); } if (!isDynamic && modifiers.length) { modifiersResult.push( ` {...{`, ...modifiers.flatMap((modify, index) => [ modify ? "" : `'`, [ modify, source, start + offset + attributeName2.length + 1 + (index ? modifiers.slice(0, index).join("").length + index : 0), allCodeFeatures ], modify ? ": true, " : `'` ]), `} satisfies typeof ${ctxMap.get(node2)}.props.${attributeName2}Modifiers}` ); } } } emitsResult.push(`'onUpdate:${attributeName2}': () => {}, `); } else { const [, ...modifiers] = name.split("_"); const result2 = []; result2.push( ...isNative ? [[modelValue, source, start + 2, allCodeFeatures]] : [ modelValue.slice(0, 3), [modelValue.slice(3), source, start, allCodeFeatures] ] ); if (modifiers.length) { result2.unshift( `{...{`, ...modifiers.flatMap((modify, index) => [ modify ? "" : `'`, [ modify, source, start + offset + (index ? modifiers.slice(0, index).join("").length + index : 0), allCodeFeatures ], modify ? ": true, " : `'` ]), `} satisfies `, isNative ? "{ trim?: true, number?: true, lazy?: true}" : `typeof ${ctxMap.get(node2)}.props.modelValueModifiers`, `} ` ); } if (!isNative) { result2.unshift(`{...{'onUpdate:${modelValue}': () => {} }} `); } replaceSourceRange(codes, source, start, attribute2.name.end, ...result2); } } if (!firstNamespacedNode) return; const { attribute, attributeName, node } = firstNamespacedNode; const end = attributeName ? attribute.end : getStart(attribute, options) + offset; replaceSourceRange( codes, source, getStart(attribute, options), end, `{...{`, ...result, `} satisfies __VLS_GetModels<__VLS_NormalizeProps<typeof ${ctxMap.get(node)}.props>>}`, ...modifiersResult, ` {...{`, ...emitsResult, `}}` ); } function getModelsType(codes) { codes.push(` type __VLS_NormalizeProps<T> = T extends object ? { [K in keyof T as {} extends Record<K, 1> ? never : K extends keyof import('vue').VNodeProps | 'class' | 'style' ? never : K]: T[K] } : never; type __VLS_CamelCase<S extends string> = S extends \`\${infer F}-\${infer RF}\${infer R}\` ? \`\${F}\${Uppercase<RF>}\${__VLS_CamelCase<R>}\` : S; type __VLS_PropsToEmits<T> = T extends object ? { [K in keyof T as K extends \`onUpdate:\${infer R}\` ? R extends 'modelValue' ? never : __VLS_CamelCase<R> : never]: T[K] } : never type __VLS_GetModels<P, E = __VLS_PropsToEmits<P>> = P extends object ? { [K in keyof P as K extends keyof E ? K : never]: K extends keyof P ? P[K] : never } : {}; `); } // src/jsx-directive/context.ts function resolveCtxMap(ctxNodeMap, options) { if (ctxNodeMap.size) { options.codes.push(` // @ts-ignore type __VLS_IsAny<T> = 0 extends 1 & T ? true : false; type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A; type __VLS_Element = globalThis.JSX.Element; 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: (K extends { $props: infer Props } ? Props : any) & Record<string, unknown>, ctx?: any) => __VLS_Element & { __ctx?: { attrs?: any, slots?: K extends { $scopedSlots: infer Slots } ? Slots : K extends { $slots: infer Slots } ? Slots : any, emit?: K extends { $emit: infer Emit } ? Emit : any } & { props?: (K extends { $props: infer Props } ? Props : any) & Record<string, unknown>; expose?(exposed: K): void; } } : T extends () => any ? (props: {}, ctx?: any) => ReturnType<T> : T extends (...args: any) => any ? T : (_: {} & Record<string, unknown>, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {} & Record<string, unknown> } }; const __VLS_nativeElements = { ...{} as SVGElementTagNameMap, ...{} as HTMLElementTagNameMap, }; declare function __VLS_getFunctionalComponentCtx<T, K, const S>( comp: T, compInstance: K, s: S, ): S extends keyof typeof __VLS_nativeElements ? { expose: (exposed: (typeof __VLS_nativeElements)[S]) => any } : '__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : T extends (props: infer P, ctx: infer Ctx) => any ? { props: P } & Ctx : {}; `); } return new Map( Array.from(ctxNodeMap).map(([node, root], index) => [ node, transformCtx(node, root, index, options) ]) ); } function transformCtx(node, root, index, options) { const { ts, codes, prefix } = options; const openingElement = getOpeningElement(node, options); if (!openingElement) return ""; let props = ""; let refValue; for (const prop of openingElement.attributes.properties) { if (!ts.isJsxAttribute(prop)) continue; let name = getText(prop.name, options); if (name === "ref" && prop.initializer && isJsxExpression(prop.initializer) && prop.initializer.expression) { refValue = getRefValue(prop.initializer.expression, options); continue; } const prefixModel = `${prefix}model`; if (name.startsWith(prefixModel)) { name = name.split("$")[0].split("_")[0].split(":")[1] ?? "modelValue"; } else if (name.includes("_")) { name = name.split("_")[0]; } else if (prefix && name.startsWith(prefix)) { continue; } if (!name) continue; const value = prop.initializer ? isJsxExpression(prop.initializer) && prop.initializer.expression ? getText(prop.initializer.expression, options) : getText(prop.initializer, options) : "true"; props += `'${name}': ${value},`; } const ctxName = `__VLS_ctx_${refValue || index}`; let tagName = getTagName(node, options); if (isNativeFormElement(tagName)) { tagName = `{}`; } else { let types = ""; if (openingElement.typeArguments?.length) { types = `<${openingElement.typeArguments.map((argument) => getText(argument, options)).join(", ")}>`; tagName += types; } } const result = ` const ${ctxName} = __VLS_getFunctionalComponentCtx(${tagName}, __VLS_asFunctionalComponent(${tagName})({${props}}), '${tagName}'); `; if (root) { replaceSourceRange2( codes, options.source, root.end - 1, root.end - 1, result ); } else { addCode(codes, result); } return ctxName; } function getRefValue(expression, options) { const { ts } = options; if (ts.isIdentifier(expression)) { return getText(expression, options); } else if (ts.isFunctionLike(expression)) { let left; if (ts.isBinaryExpression(expression.body)) { left = expression.body.left; } ts.forEachChild(expression.body, (node) => { if (ts.isBinaryExpression(node)) { left = node.left; } else if (ts.isExpressionStatement(node) && ts.isBinaryExpression(node.expression)) { left = node.expression.left; } }); return left && getText( ts.isPropertyAccessExpression(left) || ts.isElementAccessExpression(left) ? left.expression : left, options ); } else if (ts.isCallExpression(expression) && expression.arguments[0] && ts.isIdentifier(expression.arguments[0])) { return getText(expression.arguments[0], options); } } // src/jsx-directive/custom-directive.ts import { replaceSourceRange as replaceSourceRange3 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures2 } from "ts-macro"; function transformCustomDirective(attributes, options) { if (!attributes.length) return; attributes.forEach((attribute) => transform2(attribute, options)); options.codes.push(` type __VLS_ResolveDirective<T> = T extends import('vue').Directive< any, infer V, infer M extends string, infer A extends string > ? [any, V, A, Record<M, true>] : any; `); } function transform2(attribute, options) { const { codes, source, ts, ast } = options; const attributeName = getText(attribute.name, options); let directiveName = attributeName.split(/\s/)[0].split("v-")[1]; let modifiers = []; if (directiveName.includes("_")) { ; [directiveName, ...modifiers] = directiveName.split("_"); } if (directiveName) directiveName = directiveName[0].toUpperCase() + directiveName.slice(1); let arg; if (directiveName.includes(":")) { ; [directiveName, arg] = directiveName.split(":"); } const start = getStart(attribute, options); const offset = start + directiveName.length + 2; replaceSourceRange3( codes, source, start, attribute.end, [ast.text.slice(start, offset), source, start, { verification: true }], `={[`, [`v`, source, start, { ...allCodeFeatures2, verification: false }], [ directiveName, source, start + 2, { ...allCodeFeatures2, verification: false } ], `,`, attribute.initializer ? [ ts.isStringLiteral(attribute.initializer) ? getText(attribute.initializer, options) : getText(attribute.initializer, options).slice(1, -1), source, getStart(attribute.initializer, options) + (ts.isStringLiteral(attribute.initializer) ? 0 : 1), allCodeFeatures2 ] : "{} as any", ",", ...arg !== void 0 ? [ [`'`, source, offset + 1, { verification: true }], [arg, source, offset + 1, allCodeFeatures2], [ `'`, source, offset + arg.length, allCodeFeatures2, { verification: true } ] ] : ["{} as any"], ",", ...modifiers.length ? [ "{", ...modifiers.flatMap((modify, index) => [ modify ? "" : `'`, [ modify, source, getStart(attribute, options) + (attributeName.indexOf("_") + 1) + (index ? modifiers.slice(0, index).join("").length + index : 0), allCodeFeatures2 ], modify ? ": true," : `'` ]), "}" ] : ["{} as any"], `] satisfies __VLS_ResolveDirective<typeof v${directiveName}>}` ); } // src/jsx-directive/ref.ts import { replaceSourceRange as replaceSourceRange4 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures3 } from "ts-macro"; function transformRef(nodes, ctxMap, options) { const { codes, source, ts } = options; for (const { node, attribute } of nodes) { if (attribute.initializer && isJsxExpression(attribute.initializer) && attribute.initializer.expression && (ts.isFunctionExpression(attribute.initializer.expression) || ts.isArrowFunction(attribute.initializer.expression))) { replaceSourceRange4( codes, source, getStart(attribute, options), attribute.end, "{...({ ", ["ref", source, getStart(attribute.name, options), allCodeFeatures3], ": ", [ getText(attribute.initializer.expression, options), source, getStart(attribute.initializer.expression, options), allCodeFeatures3 ], `} satisfies { ref: (e: Parameters<typeof ${ctxMap.get(node)}.expose>[0]) => any }) as {}}` ); } } } // src/jsx-directive/v-bind.ts import { replaceSourceRange as replaceSourceRange5 } from "muggle-string"; function transformVBind(nodes, options) { if (nodes.length === 0) return; const { codes, source } = options; for (const { attribute } of nodes) { const attributeName = getText(attribute.name, options); const start = getStart(attribute.name, options); const end = attribute.name.end; if (attributeName.includes("_")) { replaceSourceRange5(codes, source, start + attributeName.indexOf("_"), end); } } } // src/jsx-directive/v-for.ts import { replaceSourceRange as replaceSourceRange6 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures4 } from "ts-macro"; function resolveVFor(attribute, options) { const { ts, ast, source } = options; const result = []; if (isJsxExpression(attribute.initializer) && attribute.initializer.expression && ts.isBinaryExpression(attribute.initializer.expression)) { let index; let objectIndex; let item = attribute.initializer.expression.left; const list = attribute.initializer.expression.right; if (ts.isParenthesizedExpression(item)) { if (ts.isBinaryExpression(item.expression)) { if (ts.isBinaryExpression(item.expression.left)) { index = item.expression.left.right; objectIndex = item.expression.right; item = item.expression.left.left; } else { index = item.expression.right; item = item.expression.left; } } else { item = item.expression; } } if (item && list) { result.push( "__VLS_getVForSourceType(", [ ast.text.slice(getStart(list, options), list.end), source, getStart(list, options), allCodeFeatures4 ], ").map(([", [ String(ast.text.slice(getStart(item, options), item.end)), source, getStart(item, options), allCodeFeatures4 ], ", ", index ? [ String(ast.text.slice(getStart(index, options), index.end)), source, getStart(index, options), allCodeFeatures4 ] : objectIndex ? "undefined" : "", ...objectIndex ? [ ", ", [ String( ast?.text.slice( getStart(objectIndex, options), objectIndex.end ) ), source, getStart(objectIndex, options), allCodeFeatures4 ] ] : "", "]) => " ); } } return result; } function transformVFor(nodes, options, hasVForAttribute) { if (!nodes.length && !hasVForAttribute) return; const { codes, source } = options; nodes.forEach(({ attribute, node, parent }) => { const result = resolveVFor(attribute, options); if (parent) { result.unshift("{"); } replaceSourceRange6(codes, source, node.pos, node.pos, ...result); replaceSourceRange6( codes, source, node.end - 1, node.end, `>)${parent ? "}" : ""}` ); replaceSourceRange6( codes, source, getStart(attribute, options), attribute.end ); }); codes.push(` 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, ][]; 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, ][]; `); } // src/jsx-directive/v-if.ts import { replaceSourceRange as replaceSourceRange7 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures5 } from "ts-macro"; function transformVIf(nodes, options) { const { codes, ts, source, prefix } = options; nodes.forEach(({ node, attribute, parent }, index) => { if (!ts.isIdentifier(attribute.name)) return; if ([`${prefix}if`, `${prefix}else-if`].includes( getText(attribute.name, options) ) && isJsxExpression(attribute.initializer) && attribute.initializer.expression) { const hasScope = parent && attribute.name.escapedText === `${prefix}if`; replaceSourceRange7( codes, source, node.pos, node.pos, `${hasScope ? "{" : " "}(`, [ getText(attribute.initializer.expression, options), source, getStart(attribute.initializer.expression, options), allCodeFeatures5 ], ") ? " ); const nextAttribute = nodes[index + 1]?.attribute; const nextNodeHasElse = nextAttribute && ts.isIdentifier(nextAttribute.name) ? String(nextAttribute.name.escapedText).startsWith(`${prefix}else`) : false; replaceSourceRange7( codes, source, node.end, node.end, nextNodeHasElse ? " : " : ` : null${parent ? "}" : ""}` ); } else if (attribute.name.escapedText === `${prefix}else`) { replaceSourceRange7(codes, source, node.end, node.end, parent ? "}" : ""); } replaceSourceRange7( codes, source, getStart(attribute, options), attribute.end ); }); } // src/jsx-directive/v-on.ts import { replaceSourceRange as replaceSourceRange8 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures6 } from "ts-macro"; function transformVOn(nodes, ctxMap, options) { if (nodes.length === 0) return; const { codes, source } = options; for (const { node, attribute } of nodes) { replaceSourceRange8( codes, source, getStart(attribute, options), attribute.name.end + 2, "{..." ); replaceSourceRange8( codes, source, attribute.end - 1, attribute.end - 1, ` satisfies __VLS_NormalizeEmits<typeof ${ctxMap.get(node)}.emit>` ); } codes.push(` 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]; } & {}; `); } function transformOnWithModifiers(nodes, options) { const { codes, source } = options; for (const { attribute } of nodes) { const attributeName = getText(attribute.name, options).split("_")[0]; const start = getStart(attribute.name, options); const end = attribute.name.end; replaceSourceRange8(codes, source, start, end, "{...{", [ attributeName, source, start, allCodeFeatures6 ]); if (!attribute.initializer) { replaceSourceRange8(codes, source, end, end, ": () => {}}}"); } else if (isJsxExpression(attribute.initializer) && attribute.initializer.expression) { replaceSourceRange8( codes, source, end, getStart(attribute.initializer.expression, options), ": " ); replaceSourceRange8(codes, source, attribute.end, attribute.end, "}"); } } } // src/jsx-directive/v-slot.ts import { replaceSourceRange as replaceSourceRange9 } from "muggle-string"; import { allCodeFeatures as allCodeFeatures7 } from "ts-macro"; function transformVSlot(nodeMap, ctxMap, options) { if (nodeMap.size === 0) return; const { codes, ts, ast, source, prefix } = options; nodeMap.forEach(({ attributeMap, vSlotAttribute }, node) => { const result = [" v-slots={{"]; const attributes = Array.from(attributeMap); attributes.forEach( ([attribute, { children, vIfAttribute, vForAttribute }], index) => { if (!attribute) return; let vIfAttributeName; if (vIfAttribute && (vIfAttributeName = getText(vIfAttribute.name, options))) { if (`${prefix}if` === vIfAttributeName) { result.push("..."); } if ([`${prefix}if`, `${prefix}else-if`].includes(vIfAttributeName) && isJsxExpression(vIfAttribute.initializer) && vIfAttribute.initializer.expression) { result.push( "(", [ getText(vIfAttribute.initializer.expression, options), source, getStart(vIfAttribute.initializer.expression, options), allCodeFeatures7 ], ") ? {" ); } else if (`${prefix}else` === vIfAttributeName) { result.push("{"); } } if (vForAttribute) { result.push("...", ...resolveVFor(vForAttribute, options), "({"); } let isDynamic = false; let attributeName = getText(attribute.name, options).slice(6).split(/\s/)[0].replace(/\$(.*)\$/, (_, $1) => { isDynamic = true; return $1.replaceAll("_", "."); }); const isNamespace = attributeName.startsWith(":"); attributeName = attributeName.slice(1); const wrapByQuotes = !attributeName || attributeName.includes("-"); result.push( isNamespace ? [ isDynamic ? `[${attributeName}]` : wrapByQuotes ? `'${attributeName}'` : attributeName, source, getStart(attribute.name, options) + (wrapByQuotes ? 6 : 7), allCodeFeatures7 ] : "default", `: (`, (!isNamespace || attributeName) && isJsxExpression(attribute.initializer) && attribute.initializer.expression ? [ getText(attribute.initializer.expression, options), source, getStart(attribute.initializer.expression, options), allCodeFeatures7 ] : "", isDynamic ? ": any" : "", ") => <>", ...children.map((child) => { replaceSourceRange9(codes, source, child.pos, child.end); const isSlotTemplate = getTagName(child, options) === "template" && !vSlotAttribute; const node2 = isSlotTemplate && ts.isJsxElement(child) ? child.children : child; return isSlotTemplate && ts.isJsxSelfClosingElement(child) ? "" : [ ast.text.slice(node2.pos, node2.end), source, node2.pos, allCodeFeatures7 ]; }), "</>," ); if (vForAttribute) { result.push("})) as any,"); } if (vIfAttribute && vIfAttributeName) { if ([`${prefix}if`, `${prefix}else-if`].includes(vIfAttributeName)) { const nextIndex = index + (attributes[index + 1]?.[0] ? 1 : 2); const nextAttribute = attributes[nextIndex]?.[1].vIfAttribute; result.push( "}", nextAttribute && getText(nextAttribute.name, options).startsWith(`${prefix}else`) ? " : " : " : null," ); } else if (`${prefix}else` === vIfAttributeName) { result.push("},"); } } } ); const slotType = `} satisfies typeof ${ctxMap.get(node)}.slots}`; if (attributeMap.has(null)) { result.push("default: () => <>"); } else { result.push(slotType); } if (vSlotAttribute) { replaceSourceRange9( codes, source, getStart(vSlotAttribute, options), vSlotAttribute.end, ...result ); } else if (ts.isJsxElement(node)) { replaceSourceRange9( codes, source, node.openingElement.end - 1, node.openingElement.end, ...result ); replaceSourceRange9( codes, source, node.closingElement.pos, node.closingElement.pos, attributeMap.has(null) ? `</>${slotType}>` : ">" ); } }); } function transformVSlots(nodes, ctxMap, options) { const { codes, source } = options; for (const { node, attribute: { initializer } } of nodes) { if (initializer && isJsxExpression(initializer) && initializer.expression) { replaceSourceRange9( codes, source, initializer.expression.end, initializer.expression.end, ` satisfies typeof ${ctxMap.get(node)}.slots` ); } } } // src/jsx-directive/index.ts function transformJsxDirective(options) { const { ast, ts, source, prefix, codes } = options; const resolvedPrefix = prefix.replaceAll("$", String.raw`\$`); const slotRegex = new RegExp(`^${resolvedPrefix}slot(?=:|$)`); const modelRegex = new RegExp(`^${resolvedPrefix}model(?=[:_]|$)`); const bindRegex = new RegExp(`^(?!${resolvedPrefix}|on[A-Z])\\S+_\\S+`); const onWithModifiersRegex = /^on[A-Z]\S*_\S+/; const vIfMap = /* @__PURE__ */ new Map(); const vForNodes = []; const vSlotMap = /* @__PURE__ */ new Map(); const vModelMap = /* @__PURE__ */ new Map(); const vOnNodes = []; const onWithModifiers = []; const vBindNodes = []; const refNodes = []; const vSlots = []; const customDirectives = []; const ctxNodeMap = /* @__PURE__ */ new Map(); let hasVForAttribute = false; function walkJsxDirective(node, parent, parents = []) { const tagName = getTagName(node, options); const properties = getOpeningElement(node, options); let ctxNode; let vIfAttribute; let vForAttribute; let vSlotAttribute; for (const attribute of properties?.attributes.properties || []) { if (!ts.isJsxAttribute(attribute)) continue; const attributeName = getText(attribute.name, options); if ([`${prefix}if`, `${prefix}else-if`, `${prefix}else`].includes( attributeName )) { vIfAttribute = attribute; } else if (attributeName === `${prefix}for`) { vForAttribute = attribute; hasVForAttribute = true; } else if (slotRegex.test(attributeName)) { vSlotAttribute = attribute; } else if (modelRegex.test(attributeName)) { vModelMap.has(node) || vModelMap.set(node, []); vModelMap.get(node).push({ node, attribute }); if (!isNativeFormElement(tagName)) { ctxNode = node; } } else if (attributeName === `${prefix}on`) { vOnNodes.push({ node, attribute }); ctxNode = node; } else if (onWithModifiersRegex.test(attributeName)) { onWithModifiers.push({ node, attribute }); } else if (bindRegex.test(attributeName)) { vBindNodes.push({ node, attribute }); } else if (attributeName === "ref") { refNodes.push({ node, attribute }); ctxNode = node; } else if (attributeName === `${prefix}slots`) { ctxNode = node; vSlots.push({ node, attribute }); } else if ([`${prefix}html`, `${prefix}memo`, `${prefix}once`].includes( attributeName )) { replaceSourceRange10(codes, source, attribute.pos, attribute.end); } else if (attributeName.startsWith("v-")) { customDirectives.push(attribute); } } if (isJsxExpression(node) && node.expression && ts.isObjectLiteralExpression(node.expression) && parent && ts.isJsxElement(parent) && parent.children.filter( (child) => ts.isJsxText(child) ? getText(child, options).trim() : true ).length === 1) { ctxNode = node; vSlots.push({ node: parent, attribute: { initializer: { kind: ts.SyntaxKind.JsxExpression, expression: node.expression } } }); } if (!vSlotAttribute || tagName !== "template") { if (vIfAttribute) { vIfMap.has(parent) || vIfMap.set(parent, []); vIfMap.get(parent).push({ node, attribute: vIfAttribute, parent }); } if (vForAttribute) { vForNodes.push({ node, attribute: vForAttribute, parent: vIfAttribute ? void 0 : parent }); } } if (vSlotAttribute) { const slotNode = tagName === "template" ? parent : node; if (!slotNode) return; ctxNode = slotNode; const attributeMap = vSlotMap.get(slotNode)?.attributeMap || vSlotMap.set(slotNode, { vSlotAttribute: tagName === "template" ? void 0 : vSlotAttribute, attributeMap: /* @__PURE__ */ new Map() }).get(slotNode).attributeMap; const children = attributeMap.get(vSlotAttribute)?.children || attributeMap.set(vSlotAttribute, { children: [], ...tagName === "template" ? { vIfAttribute, vForAttribute } : {} }).get(vSlotAttribute).children; if (slotNode === parent && ts.isJsxElement(parent)) { children.push(node); if (attributeMap.get(null)) return; for (const child of parent.children) { if (getTagName(child, options) === "template" || ts.isJsxText(child) && !getText(child, options).trim()) continue; const defaultNodes = attributeMap.get(null)?.children || attributeMap.set(null, { children: [] }).get(null).children; defaultNodes.push(child); } } else if (ts.isJsxElement(node)) { children.push(...node.children); } } if (ctxNode) { ctxNodeMap.set(ctxNode, parents.find(ts.isBlock)); } ts.forEachChild(node, (child) => { parents.unshift(node); walkJsxDirective( child, ts.isJsxElement(node) || ts.isJsxFragment(node) ? node : void 0, parents ); parents.shift(); }); } ts.forEachChild(ast, walkJsxDirective); const ctxMap = resolveCtxMap(ctxNodeMap, options); transformVSlot(vSlotMap, ctxMap, options); transformVFor(vForNodes, options, hasVForAttribute); vIfMap.forEach((nodes) => transformVIf(nodes, options)); transformVModel(vModelMap, ctxMap, options); transformVOn(vOnNodes, ctxMap, options); transformOnWithModifiers(onWithModifiers, options); transformVBind(vBindNodes, options); transformRef(refNodes, ctxMap, options); transformVSlots(vSlots, ctxMap, options); transformCustomDirective(customDirectives, options); } function getOpeningElement(node, options) { const { ts } = options; return ts.isJsxSelfClosingElement(node) ? node : ts.isJsxElement(node) ? node.openingElement : void 0; } function getTagName(node, options) { const openingElement = getOpeningElement(node, options); if (!openingElement) return ""; return getText(openingElement.tagName, options); } // src/jsx-directive.ts var plugin = createPlugin( ({ ts, vueCompilerOptions }, options = vueCompilerOptions?.vueMacros?.jsxDirective === true ? {} : vueCompilerOptions?.vueMacros?.jsxDirective ?? {}) => { if (!options) return []; const filter = createFilter(options); return { name: "vue-macros-jsx-directive", resolveVirtualCode({ filePath, ast, codes, source, languageId }) { if (!filter(filePath) || !["jsx", "tsx"].includes(languageId)) return; transformJsxDirective({ codes, ast, ts, source, prefix: options.prefix ?? "v-" }); } }; } ); var jsx_directive_default = plugin; export { plugin, jsx_directive_default };