summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/chilled-pandas-confess.md5
-rw-r--r--packages/astro/e2e/fixtures/lit-component/src/components/Counter.js6
-rw-r--r--packages/astro/e2e/fixtures/lit-component/src/pages/index.astro14
-rw-r--r--packages/astro/e2e/fixtures/lit-component/src/pages/media.astro6
-rw-r--r--packages/astro/e2e/fixtures/lit-component/src/pages/solo.astro18
-rw-r--r--packages/astro/e2e/lit-component.test.js168
-rw-r--r--packages/astro/src/core/build/static-build.ts2
-rw-r--r--packages/astro/src/core/build/vite-plugin-analyzer.ts1
-rw-r--r--packages/astro/src/core/build/vite-plugin-ssr.ts4
-rw-r--r--packages/astro/src/vite-plugin-scripts/index.ts19
10 files changed, 151 insertions, 92 deletions
diff --git a/.changeset/chilled-pandas-confess.md b/.changeset/chilled-pandas-confess.md
new file mode 100644
index 000000000..2f77aac40
--- /dev/null
+++ b/.changeset/chilled-pandas-confess.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Ensure the before-hydration scripts are built
diff --git a/packages/astro/e2e/fixtures/lit-component/src/components/Counter.js b/packages/astro/e2e/fixtures/lit-component/src/components/Counter.js
index 3316a7342..72843f8ef 100644
--- a/packages/astro/e2e/fixtures/lit-component/src/components/Counter.js
+++ b/packages/astro/e2e/fixtures/lit-component/src/components/Counter.js
@@ -1,8 +1,6 @@
import { LitElement, html } from 'lit';
-export const tagName = 'my-counter';
-
-class Counter extends LitElement {
+export default class Counter extends LitElement {
static get properties() {
return {
count: {
@@ -33,4 +31,4 @@ class Counter extends LitElement {
}
}
-customElements.define(tagName, Counter);
+customElements.define('my-counter', Counter);
diff --git a/packages/astro/e2e/fixtures/lit-component/src/pages/index.astro b/packages/astro/e2e/fixtures/lit-component/src/pages/index.astro
index 48eb7d2f9..ef86839d6 100644
--- a/packages/astro/e2e/fixtures/lit-component/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/lit-component/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-import '../components/Counter.js';
+import MyCounter from '../components/Counter.js';
const someProps = {
count: 0,
@@ -11,16 +11,16 @@ const someProps = {
<!-- Head Stuff -->
</head>
<body>
- <my-counter id="client-idle" {...someProps} client:idle>
+ <MyCounter id="client-idle" {...someProps} client:idle>
<h1>Hello, client:idle!</h1>
- </my-counter>
+ </MyCounter>
- <my-counter id="client-load" {...someProps} client:load>
+ <MyCounter id="client-load" {...someProps} client:load>
<h1>Hello, client:load!</h1>
- </my-counter>
+ </MyCounter>
- <my-counter id="client-visible" {...someProps} client:visible>
+ <MyCounter id="client-visible" {...someProps} client:visible>
<h1>Hello, client:visible!</h1>
- </my-counter>
+ </MyCounter>
</body>
</html>
diff --git a/packages/astro/e2e/fixtures/lit-component/src/pages/media.astro b/packages/astro/e2e/fixtures/lit-component/src/pages/media.astro
index e54cec071..a05d52863 100644
--- a/packages/astro/e2e/fixtures/lit-component/src/pages/media.astro
+++ b/packages/astro/e2e/fixtures/lit-component/src/pages/media.astro
@@ -1,5 +1,5 @@
---
-import '../components/Counter.js';
+import MyCounter from '../components/Counter.js';
const someProps = {
count: 0,
@@ -11,8 +11,8 @@ const someProps = {
<!-- Head Stuff -->
</head>
<body>
- <my-counter id="client-media" {...someProps} client:media="(max-width: 50em)">
+ <MyCounter id="client-media" {...someProps} client:media="(max-width: 50em)">
<h1>Hello, client:media!</h1>
- </my-counter>
+ </MyCounter>
</body>
</html>
diff --git a/packages/astro/e2e/fixtures/lit-component/src/pages/solo.astro b/packages/astro/e2e/fixtures/lit-component/src/pages/solo.astro
new file mode 100644
index 000000000..1d2745e47
--- /dev/null
+++ b/packages/astro/e2e/fixtures/lit-component/src/pages/solo.astro
@@ -0,0 +1,18 @@
+---
+import MyCounter from '../components/Counter.js';
+
+const someProps = {
+ count: 0,
+};
+---
+
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <MyCounter {...someProps} client:idle>
+ <h1>Hello, client:idle!</h1>
+ </MyCounter>
+ </body>
+</html>
diff --git a/packages/astro/e2e/lit-component.test.js b/packages/astro/e2e/lit-component.test.js
index acf07f9d9..66355af17 100644
--- a/packages/astro/e2e/lit-component.test.js
+++ b/packages/astro/e2e/lit-component.test.js
@@ -1,100 +1,138 @@
import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js';
-const test = testFactory({ root: './fixtures/lit-component/' });
-
-let devServer;
-
-test.beforeEach(async ({ astro }) => {
- devServer = await astro.startDevServer();
-});
-
-test.afterEach(async () => {
- await devServer.stop();
+const test = testFactory({
+ root: './fixtures/lit-component/',
});
// TODO: configure playwright to handle web component APIs
// https://github.com/microsoft/playwright/issues/14241
-test.describe.skip('Lit components', () => {
- test('client:idle', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/'));
+test.describe('Lit components', () => {
+ test.beforeEach(() => {
+ delete globalThis.window;
+ });
+
+ test.describe('Development', () => {
+ let devServer;
+ const t = test.extend({});
- const counter = page.locator('#client-idle');
- await expect(counter, 'component is visible').toBeVisible();
+ t.beforeEach(async ({ astro }) => {
+ devServer = await astro.startDevServer();
+ });
+
+ t.afterEach(async () => {
+ await devServer.stop();
+ });
- const count = counter.locator('p');
- await expect(count, 'initial count is 0').toHaveText('Count: 0');
+ t('client:idle', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/'));
- const inc = counter.locator('button');
- await inc.click();
+ const counter = page.locator('#client-idle');
+ await expect(counter, 'component is visible').toBeVisible();
+ await expect(counter).toHaveCount(1);
- await expect(count, 'count incremented by 1').toHaveText('Count: 1');
- });
+ const count = counter.locator('p');
+ await expect(count, 'initial count is 0').toHaveText('Count: 0');
- test('client:load', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/'));
+ const inc = counter.locator('button');
+ await inc.click();
- const counter = page.locator('#client-load');
- await expect(counter, 'component is visible').toBeVisible();
+ await expect(count, 'count incremented by 1').toHaveText('Count: 1');
+ });
- const count = counter.locator('p');
- await expect(count, 'initial count is 0').toHaveText('Count: 0');
+ t('client:load', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/'));
- const inc = counter.locator('button');
- await inc.click();
+ const counter = page.locator('#client-load');
+ await expect(counter, 'component is visible').toBeVisible();
- await expect(count, 'count incremented by 1').toHaveText('Count: 1');
- });
+ const count = counter.locator('p');
+ await expect(count, 'initial count is 0').toHaveText('Count: 0');
- test('client:visible', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/'));
+ const inc = counter.locator('button');
+ await inc.click();
- // Make sure the component is on screen to trigger hydration
- const counter = page.locator('#client-visible');
- await counter.scrollIntoViewIfNeeded();
- await expect(counter, 'component is visible').toBeVisible();
+ await expect(count, 'count incremented by 1').toHaveText('Count: 1');
+ });
- const count = counter.locator('p');
- await expect(count, 'initial count is 0').toHaveText('Count: 0');
+ t('client:visible', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/'));
- const inc = counter.locator('button');
- await inc.click();
+ // Make sure the component is on screen to trigger hydration
+ const counter = page.locator('#client-visible');
+ await counter.scrollIntoViewIfNeeded();
+ await expect(counter, 'component is visible').toBeVisible();
- await expect(count, 'count incremented by 1').toHaveText('Count: 1');
- });
+ const count = counter.locator('p');
+ await expect(count, 'initial count is 0').toHaveText('Count: 0');
- test('client:media', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/media'));
+ const inc = counter.locator('button');
+ await inc.click();
- const counter = page.locator('#client-media');
- await expect(counter, 'component is visible').toBeVisible();
+ await expect(count, 'count incremented by 1').toHaveText('Count: 1');
+ });
- const count = counter.locator('p');
- await expect(count, 'initial count is 0').toHaveText('Count: 0');
+ t('client:media', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/media'));
- const inc = counter.locator('button');
- await inc.click();
+ const counter = page.locator('#client-media');
+ await expect(counter, 'component is visible').toBeVisible();
- await expect(count, 'component not hydrated yet').toHaveText('Count: 0');
+ const count = counter.locator('p');
+ await expect(count, 'initial count is 0').toHaveText('Count: 0');
- // Reset the viewport to hydrate the component (max-width: 50rem)
- await page.setViewportSize({ width: 414, height: 1124 });
+ const inc = counter.locator('button');
+ await inc.click();
- await inc.click();
- await expect(count, 'count incremented by 1').toHaveText('Count: 1');
+ await expect(count, 'component not hydrated yet').toHaveText('Count: 0');
+
+ // Reset the viewport to hydrate the component (max-width: 50rem)
+ await page.setViewportSize({ width: 414, height: 1124 });
+
+ await inc.click();
+ await expect(count, 'count incremented by 1').toHaveText('Count: 1');
+ });
+
+ t.skip('HMR', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/'));
+
+ const counter = page.locator('#client-idle');
+ const label = counter.locator('h1');
+
+ await astro.editFile('./src/pages/index.astro', (original) =>
+ original.replace('Hello, client:idle!', 'Hello, updated client:idle!')
+ );
+
+ await expect(label, 'slot text updated').toHaveText('Hello, updated client:idle!');
+ await expect(counter, 'component styles persisted').toHaveCSS('display', 'grid');
+ });
});
- test('HMR', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/'));
+ test.describe('Production', () => {
+ let previewServer;
+ const t = test.extend({});
+
+ t.beforeAll(async ({ astro }) => {
+ // Playwright's Node version doesn't have these functions, so stub them.
+ process.stdout.clearLine = () => {};
+ process.stdout.cursorTo = () => {};
+ await astro.build();
+ });
+
+ t.beforeEach(async ({ astro }) => {
+ previewServer = await astro.preview();
+ });
- const counter = page.locator('#client-idle');
- const label = counter.locator('h1');
+ t.afterEach(async () => {
+ await previewServer.stop();
+ });
- await astro.editFile('./src/pages/index.astro', (original) =>
- original.replace('Hello, client:idle!', 'Hello, updated client:idle!')
- );
+ t('Only one component in prod', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/solo'));
- await expect(label, 'slot text updated').toHaveText('Hello, updated client:idle!');
- await expect(counter, 'component styles persisted').toHaveCSS('display', 'grid');
+ const counter = page.locator('my-counter');
+ await expect(counter, 'component is visible').toBeVisible();
+ await expect(counter, 'there is only one counter').toHaveCount(1);
+ });
});
});
diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts
index 64430da6e..110a85d67 100644
--- a/packages/astro/src/core/build/static-build.ts
+++ b/packages/astro/src/core/build/static-build.ts
@@ -154,7 +154,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
// SSR needs to be last
opts.astroConfig.output === 'server' &&
vitePluginSSR(internals, opts.astroConfig._ctx.adapter!),
- vitePluginAnalyzer(opts.astroConfig, internals),
+ vitePluginAnalyzer(internals),
],
publicDir: ssr ? false : viteConfig.publicDir,
root: viteConfig.root,
diff --git a/packages/astro/src/core/build/vite-plugin-analyzer.ts b/packages/astro/src/core/build/vite-plugin-analyzer.ts
index 8b9950ff1..1f84dffe3 100644
--- a/packages/astro/src/core/build/vite-plugin-analyzer.ts
+++ b/packages/astro/src/core/build/vite-plugin-analyzer.ts
@@ -10,7 +10,6 @@ import { getTopLevelPages } from './graph.js';
import { getPageDataByViteID, trackClientOnlyPageDatas } from './internal.js';
export function vitePluginAnalyzer(
- astroConfig: AstroConfig,
internals: BuildInternals
): VitePlugin {
function hoistedScriptScanner() {
diff --git a/packages/astro/src/core/build/vite-plugin-ssr.ts b/packages/astro/src/core/build/vite-plugin-ssr.ts
index d8e6ff728..bf46fc5d6 100644
--- a/packages/astro/src/core/build/vite-plugin-ssr.ts
+++ b/packages/astro/src/core/build/vite-plugin-ssr.ts
@@ -145,8 +145,10 @@ function buildManifest(
// HACK! Patch this special one.
const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
- entryModules[BEFORE_HYDRATION_SCRIPT_ID] =
+ if(!(BEFORE_HYDRATION_SCRIPT_ID in entryModules)) {
+ entryModules[BEFORE_HYDRATION_SCRIPT_ID] =
'data:text/javascript;charset=utf-8,//[no before-hydration script]';
+ }
const ssrManifest: SerializedSSRManifest = {
adapterName: opts.astroConfig._ctx.adapter!.name,
diff --git a/packages/astro/src/vite-plugin-scripts/index.ts b/packages/astro/src/vite-plugin-scripts/index.ts
index 20f4fdafe..a722d3534 100644
--- a/packages/astro/src/vite-plugin-scripts/index.ts
+++ b/packages/astro/src/vite-plugin-scripts/index.ts
@@ -1,4 +1,4 @@
-import { Plugin as VitePlugin } from 'vite';
+import { Plugin as VitePlugin, ConfigEnv } from 'vite';
import { AstroConfig, InjectedScriptStage } from '../@types/astro.js';
// NOTE: We can't use the virtual "\0" ID convention because we need to
@@ -12,8 +12,14 @@ export const PAGE_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page' as InjectedScriptStag
export const PAGE_SSR_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page-ssr' as InjectedScriptStage}.js`;
export default function astroScriptsPlugin({ config }: { config: AstroConfig }): VitePlugin {
+ let env: ConfigEnv | undefined = undefined;
return {
name: 'astro:scripts',
+
+ config(_config, _env) {
+ env = _env;
+ },
+
async resolveId(id) {
if (id.startsWith(SCRIPT_ID_PREFIX)) {
return id;
@@ -43,21 +49,14 @@ export default function astroScriptsPlugin({ config }: { config: AstroConfig }):
return null;
},
buildStart(options) {
- // We only want to inject this script if we are building
- // for the frontend AND some hydrated components exist in
- // the final build. We can detect this by looking for a
- // `astro/client/*` input, which signifies both conditions are met.
- const hasHydratedComponents =
- Array.isArray(options.input) &&
- options.input.some((input) => input.startsWith('astro/client'));
const hasHydrationScripts = config._ctx.scripts.some((s) => s.stage === 'before-hydration');
- if (hasHydratedComponents && hasHydrationScripts) {
+ if (hasHydrationScripts && env?.command === 'build' && !env?.ssrBuild) {
this.emitFile({
type: 'chunk',
id: BEFORE_HYDRATION_SCRIPT_ID,
name: BEFORE_HYDRATION_SCRIPT_ID,
});
}
- },
+ }
};
}