@astrojs/vue
Version:
Use Vue components within Astro
143 lines (141 loc) • 4.74 kB
JavaScript
import path from "node:path";
import vue from "@vitejs/plugin-vue";
import { MagicString } from "@vue/compiler-sfc";
const VIRTUAL_MODULE_ID = "virtual:@astrojs/vue/app";
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
function getRenderer() {
return {
name: "@astrojs/vue",
clientEntrypoint: "@astrojs/vue/client.js",
serverEntrypoint: "@astrojs/vue/server.js"
};
}
function getJsxRenderer() {
return {
name: "@astrojs/vue (jsx)",
clientEntrypoint: "@astrojs/vue/client.js",
serverEntrypoint: "@astrojs/vue/server.js"
};
}
function virtualAppEntrypoint(options) {
let isBuild;
let root;
let appEntrypoint;
return {
name: "@astrojs/vue/virtual-app",
config(_, { command }) {
isBuild = command === "build";
},
configResolved(config) {
root = config.root;
if (options?.appEntrypoint) {
appEntrypoint = options.appEntrypoint.startsWith(".") ? path.resolve(root, options.appEntrypoint) : options.appEntrypoint;
}
},
resolveId(id) {
if (id == VIRTUAL_MODULE_ID) {
return RESOLVED_VIRTUAL_MODULE_ID;
}
},
load(id) {
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
if (appEntrypoint) {
return `import * as mod from ${JSON.stringify(appEntrypoint)};
export const setup = async (app) => {
if ('default' in mod) {
await mod.default(app);
} else {
${!isBuild ? `console.warn("[@astrojs/vue] appEntrypoint \`" + ${JSON.stringify(
appEntrypoint
)} + "\` does not export a default function. Check out https://docs.astro.build/en/guides/integrations-guide/vue/#appentrypoint.");` : ""}
}
}`;
}
return `export const setup = () => {};`;
}
},
// Ensure that Vue components reference appEntrypoint directly
// This allows Astro to associate global styles imported in this file
// with the pages they should be injected to
transform(code, id) {
if (!appEntrypoint) return;
if (id.endsWith(".vue")) {
const s = new MagicString(code);
s.prepend(`import ${JSON.stringify(appEntrypoint)};
`);
return {
code: s.toString(),
map: s.generateMap({ hires: "boundary" })
};
}
}
};
}
async function getViteConfiguration(command, options) {
const vueOptions = {
...options,
template: {
...options?.template,
transformAssetUrls: false
}
};
vueOptions.compiler ??= await import("vue/compiler-sfc");
const config = {
optimizeDeps: {
// We add `vue` here as `@vitejs/plugin-vue` doesn't add it and we want to prevent
// re-optimization if the `vue` import is only encountered later.
include: ["@astrojs/vue/client.js", "vue"],
exclude: ["@astrojs/vue/server.js", VIRTUAL_MODULE_ID]
},
plugins: [vue(vueOptions), virtualAppEntrypoint(vueOptions)],
ssr: {
noExternal: ["vuetify", "vueperslides", "primevue"]
}
};
if (options?.jsx) {
const vueJsx = (await import("@vitejs/plugin-vue-jsx")).default;
const jsxOptions = typeof options.jsx === "object" ? options.jsx : void 0;
config.plugins?.push(vueJsx(jsxOptions));
}
if (command === "dev" && options?.devtools) {
const vueDevTools = (await import("vite-plugin-vue-devtools")).default;
const devToolsOptions = typeof options.devtools === "object" ? options.devtools : {};
config.plugins?.push(
vueDevTools({
...devToolsOptions,
appendTo: VIRTUAL_MODULE_ID
})
);
}
return config;
}
function index_default(options) {
return {
name: "@astrojs/vue",
hooks: {
"astro:config:setup": async ({ addRenderer, updateConfig, command }) => {
addRenderer(getRenderer());
if (options?.jsx) {
addRenderer(getJsxRenderer());
}
updateConfig({ vite: await getViteConfiguration(command, options) });
},
"astro:config:done": ({ logger, config }) => {
if (!options?.jsx) return;
const knownJsxRenderers = ["@astrojs/react", "@astrojs/preact", "@astrojs/solid-js"];
const enabledKnownJsxRenderers = config.integrations.filter(
(renderer) => knownJsxRenderers.includes(renderer.name)
);
if (enabledKnownJsxRenderers.length > 1 && !options?.include && !options?.exclude) {
logger.warn(
"More than one JSX renderer is enabled. This will lead to unexpected behavior unless you set the `include` or `exclude` option. See https://docs.astro.build/en/guides/integrations-guide/solid-js/#combining-multiple-jsx-frameworks for more information."
);
}
}
}
};
}
export {
index_default as default,
getRenderer as getContainerRenderer
};