'use strict'; const crypto = require('node:crypto'); const path = require('node:path'); const babel = require('@babel/core'); const jsx = require('@vue/babel-plugin-jsx'); const vite = require('vite'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; const n = Object.create(null); if (e) { for (const k in e) { n[k] = e[k]; } } n.default = e; return n; } const crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto); const path__default = /*#__PURE__*/_interopDefaultCompat(path); const babel__namespace = /*#__PURE__*/_interopNamespaceCompat(babel); const jsx__default = /*#__PURE__*/_interopDefaultCompat(jsx); const ssrRegisterHelperId = "/__vue-jsx-ssr-register-helper"; const ssrRegisterHelperCode = `import { useSSRContext } from "vue" export const ssrRegisterHelper = ${ssrRegisterHelper.toString()}`; function ssrRegisterHelper(comp, filename) { const setup = comp.setup; comp.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add(filename); if (setup) { return setup(props, ctx); } }; } function vueJsxPlugin(options = {}) { let root = ""; let needHmr = false; let needSourceMap = true; const { include, exclude, babelPlugins = [], defineComponentName = ["defineComponent"], tsPluginOptions = {}, ...babelPluginOptions } = options; const filter = vite.createFilter(include || /\.[jt]sx$/, exclude); return { name: "vite:vue-jsx", config(config) { return { // only apply esbuild to ts files // since we are handling jsx and tsx now esbuild: { include: /\.ts$/ }, define: { __VUE_OPTIONS_API__: config.define?.__VUE_OPTIONS_API__ ?? true, __VUE_PROD_DEVTOOLS__: config.define?.__VUE_PROD_DEVTOOLS__ ?? false, __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: config.define?.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ ?? false } }; }, configResolved(config) { needHmr = config.command === "serve" && !config.isProduction; needSourceMap = config.command === "serve" || !!config.build.sourcemap; root = config.root; }, resolveId(id) { if (id === ssrRegisterHelperId) { return id; } }, load(id) { if (id === ssrRegisterHelperId) { return ssrRegisterHelperCode; } }, async transform(code, id, opt) { const ssr = opt?.ssr === true; const [filepath] = id.split("?"); if (filter(id) || filter(filepath)) { const plugins = [[jsx__default, babelPluginOptions], ...babelPlugins]; if (id.endsWith(".tsx") || filepath.endsWith(".tsx")) { plugins.push([ // @ts-ignore missing type await import('@babel/plugin-transform-typescript').then( (r) => r.default ), // @ts-ignore { ...tsPluginOptions, isTSX: true, allowExtensions: true } ]); } if (!ssr && !needHmr) { plugins.push(() => { return { visitor: { CallExpression: { enter(_path) { if (isDefineComponentCall(_path.node, defineComponentName)) { const callee = _path.node.callee; callee.name = `/* @__PURE__ */ ${callee.name}`; } } } } }; }); } const result = babel__namespace.transformSync(code, { babelrc: false, ast: true, plugins, sourceMaps: needSourceMap, sourceFileName: id, configFile: false }); if (!ssr && !needHmr) { if (!result.code) return; return { code: result.code, map: result.map }; } const declaredComponents = []; const hotComponents = []; let hasDefault = false; for (const node of result.ast.program.body) { if (node.type === "VariableDeclaration") { const names = parseComponentDecls(node, defineComponentName); if (names.length) { declaredComponents.push(...names); } } if (node.type === "ExportNamedDeclaration") { if (node.declaration && node.declaration.type === "VariableDeclaration") { hotComponents.push( ...parseComponentDecls( node.declaration, defineComponentName ).map((name) => ({ local: name, exported: name, id: getHash(id + name) })) ); } else if (node.specifiers.length) { for (const spec of node.specifiers) { if (spec.type === "ExportSpecifier" && spec.exported.type === "Identifier") { const matched = declaredComponents.find( (name) => name === spec.local.name ); if (matched) { hotComponents.push({ local: spec.local.name, exported: spec.exported.name, id: getHash(id + spec.exported.name) }); } } } } } if (node.type === "ExportDefaultDeclaration") { if (node.declaration.type === "Identifier") { const _name = node.declaration.name; const matched = declaredComponents.find((name) => name === _name); if (matched) { hotComponents.push({ local: _name, exported: "default", id: getHash(id + "default") }); } } else if (isDefineComponentCall(node.declaration, defineComponentName)) { hasDefault = true; hotComponents.push({ local: "__default__", exported: "default", id: getHash(id + "default") }); } } } if (hotComponents.length) { if (hasDefault && (needHmr || ssr)) { result.code = result.code.replace( /export default defineComponent/g, `const __default__ = defineComponent` ) + ` export default __default__`; } if (needHmr && !ssr && !/\?vue&type=script/.test(id)) { let code2 = result.code; let callbackCode = ``; for (const { local, exported, id: id2 } of hotComponents) { code2 += ` ${local}.__hmrId = "${id2}" __VUE_HMR_RUNTIME__.createRecord("${id2}", ${local})`; callbackCode += ` __VUE_HMR_RUNTIME__.reload("${id2}", __${exported})`; } const newCompNames = hotComponents.map((c) => `${c.exported}: __${c.exported}`).join(","); code2 += ` import.meta.hot.accept(({${newCompNames}}) => {${callbackCode} })`; result.code = code2; } if (ssr) { const normalizedId = vite.normalizePath(path__default.relative(root, id)); let ssrInjectCode = ` import { ssrRegisterHelper } from "${ssrRegisterHelperId}" const __moduleId = ${JSON.stringify(normalizedId)}`; for (const { local } of hotComponents) { ssrInjectCode += ` ssrRegisterHelper(${local}, __moduleId)`; } result.code += ssrInjectCode; } } if (!result.code) return; return { code: result.code, map: result.map }; } } }; } function parseComponentDecls(node, fnNames) { const names = []; for (const decl of node.declarations) { if (decl.id.type === "Identifier" && isDefineComponentCall(decl.init, fnNames)) { names.push(decl.id.name); } } return names; } function isDefineComponentCall(node, names) { return node && node.type === "CallExpression" && node.callee.type === "Identifier" && names.includes(node.callee.name); } const hash = ( // eslint-disable-next-line n/no-unsupported-features/node-builtins -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ crypto__default.hash ?? ((algorithm, data, outputEncoding) => crypto__default.createHash(algorithm).update(data).digest(outputEncoding)) ); function getHash(text) { return hash("sha256", text, "hex").substring(0, 8); } module.exports = vueJsxPlugin; module.exports.default = vueJsxPlugin;