Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify vue appEntrypoint handling #9362

Merged
merged 6 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rotten-olives-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/vue': patch
---

Simplifies `appEntrypoint` handling
100 changes: 38 additions & 62 deletions packages/integrations/vue/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import path from 'node:path';
import type { Options as VueOptions } from '@vitejs/plugin-vue';
import type { Options as VueJsxOptions } from '@vitejs/plugin-vue-jsx';
import type { AstroIntegration, AstroIntegrationLogger, AstroRenderer } from 'astro';
import type { UserConfig, Plugin } from 'vite';

import { fileURLToPath } from 'node:url';
import vue from '@vitejs/plugin-vue';
import type { Options as VueJsxOptions } from '@vitejs/plugin-vue-jsx';
import type { AstroIntegration, AstroRenderer } from 'astro';
import type { Plugin, UserConfig } from 'vite';

interface Options extends VueOptions {
jsx?: boolean | VueJsxOptions;
appEntrypoint?: string;
}

interface ViteOptions extends Options {
root: URL;
logger: AstroIntegrationLogger;
}

function getRenderer(): AstroRenderer {
return {
name: '@astrojs/vue',
Expand All @@ -39,71 +33,55 @@ function getJsxRenderer(): AstroRenderer {
};
}

function virtualAppEntrypoint(options: ViteOptions) {
function virtualAppEntrypoint(options?: Options): Plugin {
const virtualModuleId = 'virtual:@astrojs/vue/app';
const resolvedVirtualModuleId = '\0' + virtualModuleId;
let getExports: (id: string) => Promise<string[]>;

let isBuild: boolean;
let root: string;

return {
name: '@astrojs/vue/virtual-app',
buildStart() {
if (!getExports) {
getExports = async (id: string) => {
const info = await this.load.call(this, { id });
return info.exports ?? [];
};
}
config(_, { command }) {
isBuild = command === 'build';
},
configureServer(server) {
if (!getExports) {
getExports = async (id: string) => {
const mod = await server.ssrLoadModule(id);
return Object.keys(mod) ?? [];
};
}
configResolved(config) {
root = config.root;
},
resolveId(id: string) {
if (id == virtualModuleId) {
return resolvedVirtualModuleId;
}
},
async load(id: string) {
const noop = `export const setup = (app) => app;`;
load(id: string) {
if (id === resolvedVirtualModuleId) {
if (options.appEntrypoint) {
try {
let resolved;
if (options.appEntrypoint.startsWith('.')) {
resolved = await this.resolve(
fileURLToPath(new URL(options.appEntrypoint, options.root))
);
} else {
resolved = await this.resolve(options.appEntrypoint, fileURLToPath(options.root));
}
if (!resolved) {
// This error is handled below, the message isn't shown to the user
throw new Error('Unable to resolve appEntrypoint');
}
const exports = await getExports(resolved.id);
if (!exports.includes('default')) {
options.logger.warn(
`appEntrypoint \`${options.appEntrypoint}\` does not export a default function. Check out https://docs.astro.build/en/guides/integrations-guide/vue/#appentrypoint.`
);
return noop;
}
return `export { default as setup } from "${resolved.id}";`;
} catch {
options.logger.warn(
`Unable to resolve appEntrypoint \`${options.appEntrypoint}\`. Does the file exist?`
);
}
if (options?.appEntrypoint) {
const appEntrypoint = options.appEntrypoint.startsWith('.')
? path.resolve(root, options.appEntrypoint)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

root is a URL, does path.resolve automatically convert it to a file path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently retrieving the root from Vite's configResolved hook (which Astro passes), so it should already be a string path.

: options.appEntrypoint;

return `\
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
import * as mod from ${JSON.stringify(appEntrypoint)};

export const setup = (app) => {
if ('default' in mod) {
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.");`
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
: ''
}
}
}`;
}
return noop;
return `export const setup = () => {};`;
}
},
} satisfies Plugin;
};
}

async function getViteConfiguration(options: ViteOptions): Promise<UserConfig> {
async function getViteConfiguration(options?: Options): Promise<UserConfig> {
const config: UserConfig = {
optimizeDeps: {
include: ['@astrojs/vue/client.js', 'vue'],
Expand All @@ -129,14 +107,12 @@ export default function (options?: Options): AstroIntegration {
return {
name: '@astrojs/vue',
hooks: {
'astro:config:setup': async ({ addRenderer, updateConfig, config, logger }) => {
'astro:config:setup': async ({ addRenderer, updateConfig }) => {
addRenderer(getRenderer());
if (options?.jsx) {
addRenderer(getJsxRenderer());
}
updateConfig({
vite: await getViteConfiguration({ ...options, root: config.root, logger }),
});
updateConfig({ vite: await getViteConfiguration(options) });
},
},
};
Expand Down
Loading