index.cjs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. 'use strict';
  2. const node_crypto = require('node:crypto');
  3. const path = require('node:path');
  4. const babel = require('@babel/core');
  5. const jsx = require('@vue/babel-plugin-jsx');
  6. const vite = require('vite');
  7. function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
  8. function _interopNamespaceCompat(e) {
  9. if (e && typeof e === 'object' && 'default' in e) return e;
  10. const n = Object.create(null);
  11. if (e) {
  12. for (const k in e) {
  13. n[k] = e[k];
  14. }
  15. }
  16. n.default = e;
  17. return n;
  18. }
  19. const path__default = /*#__PURE__*/_interopDefaultCompat(path);
  20. const babel__namespace = /*#__PURE__*/_interopNamespaceCompat(babel);
  21. const jsx__default = /*#__PURE__*/_interopDefaultCompat(jsx);
  22. const ssrRegisterHelperId = "/__vue-jsx-ssr-register-helper";
  23. const ssrRegisterHelperCode = `import { useSSRContext } from "vue"
  24. export ${ssrRegisterHelper.toString()}`;
  25. function ssrRegisterHelper(comp, filename) {
  26. const setup = comp.setup;
  27. comp.setup = (props, ctx) => {
  28. const ssrContext = useSSRContext();
  29. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add(filename);
  30. if (setup) {
  31. return setup(props, ctx);
  32. }
  33. };
  34. }
  35. function vueJsxPlugin(options = {}) {
  36. let root = "";
  37. let needHmr = false;
  38. let needSourceMap = true;
  39. const { include, exclude, babelPlugins = [], ...babelPluginOptions } = options;
  40. const filter = vite.createFilter(include || /\.[jt]sx$/, exclude);
  41. return {
  42. name: "vite:vue-jsx",
  43. config(config) {
  44. return {
  45. // only apply esbuild to ts files
  46. // since we are handling jsx and tsx now
  47. esbuild: {
  48. include: /\.ts$/
  49. },
  50. define: {
  51. __VUE_OPTIONS_API__: config.define?.__VUE_OPTIONS_API__ ?? true,
  52. __VUE_PROD_DEVTOOLS__: config.define?.__VUE_PROD_DEVTOOLS__ ?? false
  53. }
  54. };
  55. },
  56. configResolved(config) {
  57. needHmr = config.command === "serve" && !config.isProduction;
  58. needSourceMap = config.command === "serve" || !!config.build.sourcemap;
  59. root = config.root;
  60. },
  61. resolveId(id) {
  62. if (id === ssrRegisterHelperId) {
  63. return id;
  64. }
  65. },
  66. load(id) {
  67. if (id === ssrRegisterHelperId) {
  68. return ssrRegisterHelperCode;
  69. }
  70. },
  71. async transform(code, id, opt) {
  72. const ssr = opt?.ssr === true;
  73. const [filepath] = id.split("?");
  74. if (filter(id) || filter(filepath)) {
  75. const plugins = [[jsx__default, babelPluginOptions], ...babelPlugins];
  76. if (id.endsWith(".tsx") || filepath.endsWith(".tsx")) {
  77. plugins.push([
  78. // @ts-ignore missing type
  79. await import('@babel/plugin-transform-typescript').then(
  80. (r) => r.default
  81. ),
  82. // @ts-ignore
  83. { isTSX: true, allowExtensions: true }
  84. ]);
  85. }
  86. if (!ssr && !needHmr) {
  87. plugins.push(() => {
  88. return {
  89. visitor: {
  90. CallExpression: {
  91. enter(_path) {
  92. if (isDefineComponentCall(_path.node)) {
  93. const callee = _path.node.callee;
  94. callee.name = `/* @__PURE__ */ ${callee.name}`;
  95. }
  96. }
  97. }
  98. }
  99. };
  100. });
  101. }
  102. const result = babel__namespace.transformSync(code, {
  103. babelrc: false,
  104. ast: true,
  105. plugins,
  106. sourceMaps: needSourceMap,
  107. sourceFileName: id,
  108. configFile: false
  109. });
  110. if (!ssr && !needHmr) {
  111. if (!result.code)
  112. return;
  113. return {
  114. code: result.code,
  115. map: result.map
  116. };
  117. }
  118. const declaredComponents = [];
  119. const hotComponents = [];
  120. let hasDefault = false;
  121. for (const node of result.ast.program.body) {
  122. if (node.type === "VariableDeclaration") {
  123. const names = parseComponentDecls(node);
  124. if (names.length) {
  125. declaredComponents.push(...names);
  126. }
  127. }
  128. if (node.type === "ExportNamedDeclaration") {
  129. if (node.declaration && node.declaration.type === "VariableDeclaration") {
  130. hotComponents.push(
  131. ...parseComponentDecls(node.declaration).map((name) => ({
  132. local: name,
  133. exported: name,
  134. id: getHash(id + name)
  135. }))
  136. );
  137. } else if (node.specifiers.length) {
  138. for (const spec of node.specifiers) {
  139. if (spec.type === "ExportSpecifier" && spec.exported.type === "Identifier") {
  140. const matched = declaredComponents.find(
  141. (name) => name === spec.local.name
  142. );
  143. if (matched) {
  144. hotComponents.push({
  145. local: spec.local.name,
  146. exported: spec.exported.name,
  147. id: getHash(id + spec.exported.name)
  148. });
  149. }
  150. }
  151. }
  152. }
  153. }
  154. if (node.type === "ExportDefaultDeclaration") {
  155. if (node.declaration.type === "Identifier") {
  156. const _name = node.declaration.name;
  157. const matched = declaredComponents.find((name) => name === _name);
  158. if (matched) {
  159. hotComponents.push({
  160. local: _name,
  161. exported: "default",
  162. id: getHash(id + "default")
  163. });
  164. }
  165. } else if (isDefineComponentCall(node.declaration)) {
  166. hasDefault = true;
  167. hotComponents.push({
  168. local: "__default__",
  169. exported: "default",
  170. id: getHash(id + "default")
  171. });
  172. }
  173. }
  174. }
  175. if (hotComponents.length) {
  176. if (hasDefault && (needHmr || ssr)) {
  177. result.code = result.code.replace(
  178. /export default defineComponent/g,
  179. `const __default__ = defineComponent`
  180. ) + `
  181. export default __default__`;
  182. }
  183. if (needHmr && !ssr && !/\?vue&type=script/.test(id)) {
  184. let code2 = result.code;
  185. let callbackCode = ``;
  186. for (const { local, exported, id: id2 } of hotComponents) {
  187. code2 += `
  188. ${local}.__hmrId = "${id2}"
  189. __VUE_HMR_RUNTIME__.createRecord("${id2}", ${local})`;
  190. callbackCode += `
  191. __VUE_HMR_RUNTIME__.reload("${id2}", __${exported})`;
  192. }
  193. const newCompNames = hotComponents.map((c) => `${c.exported}: __${c.exported}`).join(",");
  194. code2 += `
  195. import.meta.hot.accept(({${newCompNames}}) => {${callbackCode}
  196. })`;
  197. result.code = code2;
  198. }
  199. if (ssr) {
  200. const normalizedId = vite.normalizePath(path__default.relative(root, id));
  201. let ssrInjectCode = `
  202. import { ssrRegisterHelper } from "${ssrRegisterHelperId}"
  203. const __moduleId = ${JSON.stringify(normalizedId)}`;
  204. for (const { local } of hotComponents) {
  205. ssrInjectCode += `
  206. ssrRegisterHelper(${local}, __moduleId)`;
  207. }
  208. result.code += ssrInjectCode;
  209. }
  210. }
  211. if (!result.code)
  212. return;
  213. return {
  214. code: result.code,
  215. map: result.map
  216. };
  217. }
  218. }
  219. };
  220. }
  221. function parseComponentDecls(node) {
  222. const names = [];
  223. for (const decl of node.declarations) {
  224. if (decl.id.type === "Identifier" && isDefineComponentCall(decl.init)) {
  225. names.push(decl.id.name);
  226. }
  227. }
  228. return names;
  229. }
  230. function isDefineComponentCall(node) {
  231. return node && node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "defineComponent";
  232. }
  233. function getHash(text) {
  234. return node_crypto.createHash("sha256").update(text).digest("hex").substring(0, 8);
  235. }
  236. module.exports = vueJsxPlugin;
  237. module.exports.default = vueJsxPlugin;