vue-jsx-vapor
Version:
Convert Vue JSX to Vapor
207 lines (202 loc) • 7.14 kB
JavaScript
import { transformVueJsxVapor } from "./core-CJgJmh2O.js";
import { transformSync } from "@babel/core";
import babelTypescript from "@babel/plugin-transform-typescript";
import macros from "@vue-jsx-vapor/macros/raw";
import { transformJsxDirective } from "@vue-macros/jsx-directive/api";
import { createFilter, normalizePath } from "unplugin-utils";
import { isFunctionalNode } from "@vue-jsx-vapor/macros/api";
import getHash from "hash-sum";
import { relative } from "pathe";
import jsx from "@vue/babel-plugin-jsx";
//#region src/core/ssr.ts
const ssrRegisterHelperId = "/__vue-jsx-ssr-register-helper";
const ssrRegisterHelperCode = `import { useSSRContext } from "vue"\nexport const ssrRegisterHelper = ${ssrRegisterHelper.toString()}`;
/**
* This function is serialized with toString() and evaluated as a virtual
* module during SSR
*/
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 injectSSR(id, hotComponents, root = "") {
const normalizedId = normalizePath(relative(root, id));
let ssrInjectCode = `\nimport { ssrRegisterHelper } from "${ssrRegisterHelperId}"\nconst __moduleId = ${JSON.stringify(normalizedId)}`;
for (const { local } of hotComponents) ssrInjectCode += `\nssrRegisterHelper(${local}, __moduleId)`;
return ssrInjectCode;
}
//#endregion
//#region src/core/hmr.ts
function injectHMRAndSSR(result, id, options) {
const ssr = options?.ssr;
const defineComponentNames = options?.defineComponentNames ?? ["defineComponent", "defineVaporComponent"];
const { ast } = result;
const declaredComponents = [];
const hotComponents = [];
let hasDefaultExport = false;
for (const node of ast.program.body) {
if (node.type === "VariableDeclaration") {
const names = parseComponentDecls(node, defineComponentNames);
if (names.length) declaredComponents.push(...names);
}
if (node.type === "ExportNamedDeclaration") {
if (node.declaration && node.declaration.type === "VariableDeclaration") hotComponents.push(...parseComponentDecls(node.declaration, defineComponentNames).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, defineComponentNames) || isFunctionalNode(node.declaration)) {
hasDefaultExport = true;
hotComponents.push({
local: "__default__",
exported: "default",
id: getHash(`${id}default`)
});
}
}
}
if (hotComponents.length) {
if (hasDefaultExport || ssr) result.code = `${result.code.replaceAll(`export default `, `const __default__ = `)}\nexport default __default__;`;
if (!ssr && !/\?vue&type=script/.test(id)) {
let code = result.code;
let callbackCode = ``;
for (const { local, exported, id: id$1 } of hotComponents) {
code += `\n${local}.__hmrId = "${id$1}";\n__VUE_HMR_RUNTIME__.createRecord("${id$1}", ${local});`;
callbackCode += `
__VUE_HMR_RUNTIME__[typeof mod['${exported}'] === 'function' ? 'rerender' : 'reload'](mod['${exported}'].__hmrId, mod['${exported}']);`;
}
code += `
if (import.meta.hot) {
import.meta.hot.accept(mod => {${callbackCode}\n });
}`;
result.code = code;
}
if (ssr) result.code += injectSSR(id, hotComponents, options?.root);
}
}
function parseComponentDecls(node, fnNames) {
const names = [];
for (const decl of node.declarations) if (decl.id.type === "Identifier" && (isDefineComponentCall(decl.init, fnNames) || isFunctionalNode(decl.init))) 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));
}
//#endregion
//#region src/core/vue-jsx.ts
function transformVueJsx(code, id, needSourceMap = false) {
const result = transformSync(code, {
plugins: [jsx, ...id.endsWith(".tsx") ? [[babelTypescript, {
isTSX: true,
allowExtensions: true
}]] : []],
filename: id,
sourceMaps: needSourceMap,
sourceFileName: id,
babelrc: false,
configFile: false
});
if (result?.code && result.code !== code) return {
code: result.code,
map: result.map
};
}
//#endregion
//#region src/raw.ts
const plugin = (options = {}) => {
const transformInclude = createFilter(options?.include || /\.[cm]?[jt]sx$/, options?.exclude || /node_modules/);
let root = "";
let needHMR = false;
let needSourceMap = options.sourceMap || false;
return [
{
name: "vue-jsx-vapor",
vite: {
config(config) {
return {
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) {
root = config.root;
needHMR = config.command === "serve";
needSourceMap ||= config.command === "serve" || !!config.build.sourcemap;
}
},
resolveId(id) {
if (id === ssrRegisterHelperId) return id;
},
loadInclude(id) {
if (id === ssrRegisterHelperId) return true;
},
load(id) {
if (id === ssrRegisterHelperId) return ssrRegisterHelperCode;
},
transformInclude,
transform(code, id, opt) {
const result = transformVueJsxVapor(code, id, options, needSourceMap);
if (result?.code) {
(needHMR || opt?.ssr) && injectHMRAndSSR(result, id, {
ssr: opt?.ssr,
root
});
return {
code: result.code,
map: result.map
};
}
}
},
{
name: "@vue-macros/jsx-directive",
transformInclude,
transform(code, id, opt) {
if (options.interop || opt?.ssr) return transformJsxDirective(code, id, {
lib: "vue",
prefix: "v-",
version: 3.6
});
}
},
{
name: "@vitejs/plugin-vue-jsx",
transformInclude,
transform(code, id, opt) {
if (options.interop || opt?.ssr) return transformVueJsx(code, id, needSourceMap);
}
},
...options.macros === false ? [] : options.macros ? [macros(options.macros === true ? void 0 : options.macros)] : []
];
};
var raw_default = plugin;
//#endregion
export { raw_default };