diff --git a/.changeset/selfish-lamps-build.md b/.changeset/selfish-lamps-build.md new file mode 100644 index 000000000000..155c326270f5 --- /dev/null +++ b/.changeset/selfish-lamps-build.md @@ -0,0 +1,5 @@ +--- +'@astrojs/vue': patch +--- + +Fixes issue with `appEntrypoint` when running `astro dev` diff --git a/packages/integrations/vue/src/index.ts b/packages/integrations/vue/src/index.ts index 109ca58ef4fc..636dfba6a7d8 100644 --- a/packages/integrations/vue/src/index.ts +++ b/packages/integrations/vue/src/index.ts @@ -1,7 +1,7 @@ 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, Rollup } from 'vite'; +import type { UserConfig, Plugin } from 'vite'; import { fileURLToPath } from 'node:url'; import vue from '@vitejs/plugin-vue'; @@ -42,15 +42,32 @@ function getJsxRenderer(): AstroRenderer { function virtualAppEntrypoint(options: ViteOptions) { const virtualModuleId = 'virtual:@astrojs/vue/app'; const resolvedVirtualModuleId = '\0' + virtualModuleId; + let getExports: (id: string) => Promise; return { name: '@astrojs/vue/virtual-app', + buildStart() { + if (!getExports) { + getExports = async (id: string) => { + const info = await this.load.call(this, { id }); + return info.exports ?? []; + } + } + }, + configureServer(server) { + if (!getExports) { + getExports = async (id: string) => { + const mod = await server.ssrLoadModule(id); + return Object.keys(mod) ?? []; + } + } + }, resolveId(id: string) { if (id == virtualModuleId) { return resolvedVirtualModuleId; } }, async load(id: string) { - const noop = `export const setup = () => {}`; + const noop = `export const setup = (app) => app;`; if (id === resolvedVirtualModuleId) { if (options.appEntrypoint) { try { @@ -66,8 +83,8 @@ function virtualAppEntrypoint(options: ViteOptions) { // This error is handled below, the message isn't shown to the user throw new Error('Unable to resolve appEntrypoint'); } - const loaded = await this.load(resolved); - if (!loaded.hasDefaultExport) { + 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.` ); @@ -83,7 +100,7 @@ function virtualAppEntrypoint(options: ViteOptions) { return noop; } }, - } satisfies Rollup.Plugin; + } satisfies Plugin; } async function getViteConfiguration(options: ViteOptions): Promise { diff --git a/packages/integrations/vue/test/app-entrypoint.test.js b/packages/integrations/vue/test/app-entrypoint.test.js index 308f10149fc1..7a40613544bd 100644 --- a/packages/integrations/vue/test/app-entrypoint.test.js +++ b/packages/integrations/vue/test/app-entrypoint.test.js @@ -52,6 +52,39 @@ describe('App Entrypoint', () => { }); }); +describe('App Entrypoint no export default (dev)', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let devServer; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/app-entrypoint-no-export-default/', + }); + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('loads during SSR', async () => { + const html = await fixture.fetch('/').then(res => res.text()); + const { document } = parseHTML(html); + const bar = document.querySelector('#foo > #bar'); + expect(bar).not.to.be.undefined; + expect(bar.textContent).to.eq('works'); + }); + + it('loads svg components without transforming them to assets', async () => { + const html = await fixture.fetch('/').then(res => res.text()); + const { document } = parseHTML(html); + const client = document.querySelector('astro-island svg'); + + expect(client).not.to.be.undefined; + }); +}); + describe('App Entrypoint no export default', () => { /** @type {import('./test-utils').Fixture} */ let fixture; @@ -121,3 +154,34 @@ describe('App Entrypoint relative', () => { expect(js).not.to.match(/\w+\.component\(\"Bar\"/gm); }); }); + +describe('App Entrypoint /src/absolute', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/app-entrypoint-src-absolute/', + }); + await fixture.build(); + }); + + it('loads during SSR', async () => { + const data = await fixture.readFile('/index.html'); + const { document } = parseHTML(data); + const bar = document.querySelector('#foo > #bar'); + expect(bar).not.to.be.undefined; + expect(bar.textContent).to.eq('works'); + }); + + it('component not included in renderer bundle', async () => { + const data = await fixture.readFile('/index.html'); + const { document } = parseHTML(data); + const island = document.querySelector('astro-island'); + const client = island.getAttribute('renderer-url'); + expect(client).not.to.be.undefined; + + const js = await fixture.readFile(client); + expect(js).not.to.match(/\w+\.component\(\"Bar\"/gm); + }); +}); diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-no-export-default/src/pages/_app.ts b/packages/integrations/vue/test/fixtures/app-entrypoint-no-export-default/src/pages/_app.ts index 808620814b9b..2fc8bde52d68 100644 --- a/packages/integrations/vue/test/fixtures/app-entrypoint-no-export-default/src/pages/_app.ts +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-no-export-default/src/pages/_app.ts @@ -1,3 +1,3 @@ -console.log(123); +export const setup = () => {} // no default export diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/astro.config.mjs b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/astro.config.mjs new file mode 100644 index 000000000000..6eae67892051 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/astro.config.mjs @@ -0,0 +1,8 @@ +import { defineConfig } from 'astro/config'; +import vue from '@astrojs/vue'; + +export default defineConfig({ + integrations: [vue({ + appEntrypoint: '/src/vue.ts' + })] +}) diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json new file mode 100644 index 000000000000..244f9120c825 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json @@ -0,0 +1,12 @@ +{ + "name": "@test/vue-app-entrypoint-src-absolute", + "version": "0.0.0", + "private": true, + "scripts": { + "astro": "astro" + }, + "dependencies": { + "@astrojs/vue": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Bar.vue b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Bar.vue new file mode 100644 index 000000000000..9e690ea06adc --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Bar.vue @@ -0,0 +1,3 @@ + diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Circle.svg b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Circle.svg new file mode 100644 index 000000000000..cf2bd92fc135 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Foo.vue b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Foo.vue new file mode 100644 index 000000000000..7f6808477f18 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Foo.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/pages/index.astro b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/pages/index.astro new file mode 100644 index 000000000000..3240cbe0fd73 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/pages/index.astro @@ -0,0 +1,12 @@ +--- +import Foo from '../components/Foo.vue'; +--- + + + + Vue App Entrypoint + + + + + diff --git a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/vue.ts b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/vue.ts new file mode 100644 index 000000000000..ead516c976e9 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/vue.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b63728e06c6..d8f8ff2ad6ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4860,6 +4860,15 @@ importers: specifier: workspace:* version: link:../../../../../astro + packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute: + dependencies: + '@astrojs/vue': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/vue/test/fixtures/basics: dependencies: '@astrojs/vue':