index.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import path from 'node:path';
  2. import type { Options as VueOptions } from '@vitejs/plugin-vue';
  3. import vue from '@vitejs/plugin-vue';
  4. import type { Options as VueJsxOptions } from '@vitejs/plugin-vue-jsx';
  5. import type { AstroIntegration, AstroRenderer } from 'astro';
  6. import type { Plugin, UserConfig } from 'vite';
  7. import { MagicString } from '@vue/compiler-sfc';
  8. interface Options extends VueOptions {
  9. jsx?: boolean | VueJsxOptions;
  10. appEntrypoint?: string;
  11. }
  12. function getRenderer(): AstroRenderer {
  13. return {
  14. name: '@astrojs/vue',
  15. clientEntrypoint: '@astrojs/vue/client.js',
  16. serverEntrypoint: '@astrojs/vue/server.js',
  17. };
  18. }
  19. function getJsxRenderer(): AstroRenderer {
  20. return {
  21. name: '@astrojs/vue (jsx)',
  22. clientEntrypoint: '@astrojs/vue/client.js',
  23. serverEntrypoint: '@astrojs/vue/server.js',
  24. jsxImportSource: 'vue',
  25. jsxTransformOptions: async () => {
  26. const jsxPlugin = (await import('@vue/babel-plugin-jsx')).default;
  27. return {
  28. plugins: [jsxPlugin],
  29. };
  30. },
  31. };
  32. }
  33. function virtualAppEntrypoint(options?: Options): Plugin {
  34. const virtualModuleId = 'virtual:@astrojs/vue/app';
  35. const resolvedVirtualModuleId = '\0' + virtualModuleId;
  36. let isBuild: boolean;
  37. let root: string;
  38. let appEntrypoint: string | undefined;
  39. return {
  40. name: '@astrojs/vue/virtual-app',
  41. config(_, { command }) {
  42. isBuild = command === 'build';
  43. },
  44. configResolved(config) {
  45. root = config.root;
  46. if (options?.appEntrypoint) {
  47. appEntrypoint = options.appEntrypoint.startsWith('.')
  48. ? path.resolve(root, options.appEntrypoint)
  49. : options.appEntrypoint;
  50. }
  51. },
  52. resolveId(id: string) {
  53. if (id == virtualModuleId) {
  54. return resolvedVirtualModuleId;
  55. }
  56. },
  57. load(id: string) {
  58. if (id === resolvedVirtualModuleId) {
  59. if (appEntrypoint) {
  60. return `\
  61. import * as mod from ${JSON.stringify(appEntrypoint)};
  62. export const setup = async (app) => {
  63. if ('default' in mod) {
  64. await mod.default(app);
  65. } else {
  66. ${
  67. !isBuild
  68. ? `console.warn("[@astrojs/vue] appEntrypoint \`" + ${JSON.stringify(
  69. appEntrypoint
  70. )} + "\` does not export a default function. Check out https://docs.astro.build/en/guides/integrations-guide/vue/#appentrypoint.");`
  71. : ''
  72. }
  73. }
  74. }`;
  75. }
  76. return `export const setup = () => {};`;
  77. }
  78. },
  79. // Ensure that Vue components reference appEntrypoint directly
  80. // This allows Astro to assosciate global styles imported in this file
  81. // with the pages they should be injected to
  82. transform(code, id) {
  83. if (!appEntrypoint) return;
  84. if (id.endsWith('.vue')) {
  85. const s = new MagicString(code);
  86. s.prepend(`import ${JSON.stringify(appEntrypoint)};\n`);
  87. return {
  88. code: s.toString(),
  89. map: s.generateMap({ hires: 'boundary' }),
  90. };
  91. }
  92. },
  93. };
  94. }
  95. async function getViteConfiguration(options?: Options): Promise<UserConfig> {
  96. const config: UserConfig = {
  97. optimizeDeps: {
  98. include: ['@astrojs/vue/client.js', 'vue'],
  99. exclude: ['@astrojs/vue/server.js', 'virtual:@astrojs/vue/app'],
  100. },
  101. plugins: [vue(options), virtualAppEntrypoint(options)],
  102. ssr: {
  103. external: ['@vue/server-renderer'],
  104. noExternal: ['vuetify', 'vueperslides', 'primevue'],
  105. },
  106. };
  107. if (options?.jsx) {
  108. const vueJsx = (await import('@vitejs/plugin-vue-jsx')).default;
  109. const jsxOptions = typeof options.jsx === 'object' ? options.jsx : undefined;
  110. config.plugins?.push(vueJsx(jsxOptions));
  111. }
  112. return config;
  113. }
  114. export default function (options?: Options): AstroIntegration {
  115. return {
  116. name: '@astrojs/vue',
  117. hooks: {
  118. 'astro:config:setup': async ({ addRenderer, updateConfig }) => {
  119. addRenderer(getRenderer());
  120. if (options?.jsx) {
  121. addRenderer(getJsxRenderer());
  122. }
  123. updateConfig({ vite: await getViteConfiguration(options) });
  124. },
  125. },
  126. };
  127. }