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