From d091389a0f47796c70b0d1e6b26958c450c3e8a0 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 5 Dec 2023 17:39:59 -0600 Subject: [PATCH 1/5] fix(vue): remove `hasDefaultExport` from appEntrypoint logic --- .changeset/selfish-lamps-build.md | 5 +++++ packages/integrations/vue/src/index.ts | 27 +++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 .changeset/selfish-lamps-build.md 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 { From d9b4860e0d7f7729858a1fe58035581a7836b709 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 5 Dec 2023 17:40:07 -0600 Subject: [PATCH 2/5] chore: add dev server test --- .../vue/test/app-entrypoint.test.js | 75 +++++++++++++++++++ .../src/pages/_app.ts | 2 +- .../astro.config.mjs | 8 ++ .../app-entrypoint-src-absolute/package.json | 12 +++ .../src/components/Bar.vue | 3 + .../src/components/Circle.svg | 1 + .../src/components/Foo.vue | 11 +++ .../src/pages/index.astro | 12 +++ .../app-entrypoint-src-absolute/src/vue.ts | 1 + 9 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/astro.config.mjs create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Bar.vue create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Circle.svg create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/components/Foo.vue create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/pages/index.astro create mode 100644 packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/src/vue.ts diff --git a/packages/integrations/vue/test/app-entrypoint.test.js b/packages/integrations/vue/test/app-entrypoint.test.js index 308f10149fc1..681f2e95ea0f 100644 --- a/packages/integrations/vue/test/app-entrypoint.test.js +++ b/packages/integrations/vue/test/app-entrypoint.test.js @@ -52,6 +52,50 @@ 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('component not included on page', async () => { + const html = await fixture.fetch('/').then(res => res.text()); + const { document } = parseHTML(html); + const island = document.querySelector('astro-island'); + const client = island.getAttribute('renderer-url'); + expect(client).not.to.be.undefined; + + const js = await fixture.fetch(client); + expect(js).not.to.match(/\w+\.component\(\"Bar\"/gm); + }); + + 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 +165,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..80483c7c6ce8 --- /dev/null +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json @@ -0,0 +1,12 @@ +{ + "name": "@test/vue-app-entrypoint-relative", + "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 () => {} From 9895eaf221a630625a772512bf54318c88eeb069 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 5 Dec 2023 17:57:32 -0600 Subject: [PATCH 3/5] chore: update lockfile --- pnpm-lock.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) 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': From 7c044a9a4fd9475f381885872bd85b4deec37ce0 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 5 Dec 2023 18:04:38 -0600 Subject: [PATCH 4/5] chore: fixture names --- .../vue/test/fixtures/app-entrypoint-src-absolute/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 80483c7c6ce8..244f9120c825 100644 --- a/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json +++ b/packages/integrations/vue/test/fixtures/app-entrypoint-src-absolute/package.json @@ -1,5 +1,5 @@ { - "name": "@test/vue-app-entrypoint-relative", + "name": "@test/vue-app-entrypoint-src-absolute", "version": "0.0.0", "private": true, "scripts": { From 6678e01de87d02052cc2e1e3f7c6d9626f448c53 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 5 Dec 2023 18:16:23 -0600 Subject: [PATCH 5/5] chore: remove irrelevant test --- packages/integrations/vue/test/app-entrypoint.test.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/integrations/vue/test/app-entrypoint.test.js b/packages/integrations/vue/test/app-entrypoint.test.js index 681f2e95ea0f..7a40613544bd 100644 --- a/packages/integrations/vue/test/app-entrypoint.test.js +++ b/packages/integrations/vue/test/app-entrypoint.test.js @@ -76,17 +76,6 @@ describe('App Entrypoint no export default (dev)', () => { expect(bar.textContent).to.eq('works'); }); - it('component not included on page', async () => { - const html = await fixture.fetch('/').then(res => res.text()); - const { document } = parseHTML(html); - const island = document.querySelector('astro-island'); - const client = island.getAttribute('renderer-url'); - expect(client).not.to.be.undefined; - - const js = await fixture.fetch(client); - expect(js).not.to.match(/\w+\.component\(\"Bar\"/gm); - }); - it('loads svg components without transforming them to assets', async () => { const html = await fixture.fetch('/').then(res => res.text()); const { document } = parseHTML(html);