diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index f1907004d6fa..ccdb3e8f089a 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/markdoc": "^0.1.2", + "@astrojs/markdoc": "^0.2.0", "astro": "^2.4.1", "kleur": "^4.1.5" } diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index e2f8db0b5c01..df863fc9459e 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,19 @@ # astro +## 2.5.1 + +### Patch Changes + +- [#7128](https://github.com/withastro/astro/pull/7128) [`72f686a68`](https://github.com/withastro/astro/commit/72f686a68930de52f9a274c13c98acad59925b31) Thanks [@johannesspohr](https://github.com/johannesspohr)! - Fix routes created by `injectRoute` for SSR + +- [#7132](https://github.com/withastro/astro/pull/7132) [`319a0a7a0`](https://github.com/withastro/astro/commit/319a0a7a0a6a950387c942b467746d590bb32fda) Thanks [@ematipico](https://github.com/ematipico)! - Emit middleware as an entrypoint during build + +- [#7036](https://github.com/withastro/astro/pull/7036) [`852d59a8d`](https://github.com/withastro/astro/commit/852d59a8d68e124f10852609e0f1619d5838ac76) Thanks [@ematipico](https://github.com/ematipico)! - Emit pages as dynamic import chunks during the build + +- [#7126](https://github.com/withastro/astro/pull/7126) [`530fb9ebe`](https://github.com/withastro/astro/commit/530fb9ebee77646921ec29d45d9b66484bdfb521) Thanks [@bluwy](https://github.com/bluwy)! - Add route information when warning of `getStaticPaths()` ignored + +- [#7118](https://github.com/withastro/astro/pull/7118) [`3257dd289`](https://github.com/withastro/astro/commit/3257dd28901c785a6a661211b98c5ef2cb3b9aa4) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Fix unnecessary warning showing on start when a collection folder was empty. The warning was also enhanced to add more information about possible causes. + ## 2.5.0 ### Minor Changes diff --git a/packages/astro/e2e/client-only.test.js b/packages/astro/e2e/client-only.test.js index 9483c7053841..08c5fb3ac575 100644 --- a/packages/astro/e2e/client-only.test.js +++ b/packages/astro/e2e/client-only.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Client only', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = await page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -33,7 +33,7 @@ test.describe('Client only', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = await page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -51,7 +51,7 @@ test.describe('Client only', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = await page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -69,7 +69,7 @@ test.describe('Client only', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = await page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -87,7 +87,7 @@ test.describe('Client only', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = await page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/errors.test.js b/packages/astro/e2e/errors.test.js index 8efcb40cfdb7..35599ce97109 100644 --- a/packages/astro/e2e/errors.test.js +++ b/packages/astro/e2e/errors.test.js @@ -18,7 +18,7 @@ test.afterAll(async ({ astro }) => { test.describe('Error display', () => { test('detect syntax errors in template', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/astro-syntax-error')); + await page.goto(astro.resolveUrl('/astro-syntax-error'), { waitUntil: 'networkidle' }); const message = (await getErrorOverlayContent(page)).message; expect(message).toMatch('Unexpected "}"'); @@ -37,7 +37,7 @@ test.describe('Error display', () => { }); test('shows useful error when frontmatter import is not found', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/import-not-found')); + await page.goto(astro.resolveUrl('/import-not-found'), { waitUntil: 'networkidle' }); const message = (await getErrorOverlayContent(page)).message; expect(message).toMatch('Could not import ../abc.astro'); @@ -53,7 +53,7 @@ test.describe('Error display', () => { }); test('shows correct file path when a page has an error', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/import-not-found')); + await page.goto(astro.resolveUrl('/import-not-found'), { waitUntil: 'networkidle' }); const { fileLocation, absoluteFileLocation } = await getErrorOverlayContent(page); const absoluteFileUrl = 'file://' + absoluteFileLocation.replace(/:\d+:\d+$/, ''); @@ -64,7 +64,7 @@ test.describe('Error display', () => { }); test('shows correct file path when a component has an error', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/preact-runtime-error')); + await page.goto(astro.resolveUrl('/preact-runtime-error'), { waitUntil: 'networkidle' }); const { fileLocation, absoluteFileLocation } = await getErrorOverlayContent(page); const absoluteFileUrl = 'file://' + absoluteFileLocation.replace(/:\d+:\d+$/, ''); @@ -75,7 +75,7 @@ test.describe('Error display', () => { }); test('framework errors recover when fixed', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/svelte-syntax-error')); + await page.goto(astro.resolveUrl('/svelte-syntax-error'), { waitUntil: 'networkidle' }); const message = (await getErrorOverlayContent(page)).message; expect(message).toMatch(' attempted to close an element that was not open'); diff --git a/packages/astro/e2e/hydration-race.test.js b/packages/astro/e2e/hydration-race.test.js index e516adfc8423..ffa78125fe54 100644 --- a/packages/astro/e2e/hydration-race.test.js +++ b/packages/astro/e2e/hydration-race.test.js @@ -17,7 +17,7 @@ test.afterEach(async () => { test.describe('Hydration race', () => { test('Islands inside of slots hydrate', async ({ page, astro }) => { - await page.goto('/slot'); + await page.goto(astro.resolveUrl('/slot')); const html = await page.content(); diff --git a/packages/astro/e2e/multiple-frameworks.test.js b/packages/astro/e2e/multiple-frameworks.test.js index 729bfe002168..b9bc85576bde 100644 --- a/packages/astro/e2e/multiple-frameworks.test.js +++ b/packages/astro/e2e/multiple-frameworks.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.skip('Multiple frameworks', () => { test.skip('React counter', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -30,7 +30,7 @@ test.skip('Multiple frameworks', () => { }); test('Preact counter', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -45,7 +45,7 @@ test.skip('Multiple frameworks', () => { }); test.skip('Solid counter', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -60,7 +60,7 @@ test.skip('Multiple frameworks', () => { }); test('Vue counter', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -75,7 +75,7 @@ test.skip('Multiple frameworks', () => { }); test('Svelte counter', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -90,7 +90,7 @@ test.skip('Multiple frameworks', () => { }); test('Astro components', async ({ page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const aComponent = page.locator('#astro-a'); await expect(aComponent, 'component is visible').toBeVisible(); @@ -103,7 +103,7 @@ test.skip('Multiple frameworks', () => { test.describe('HMR', () => { test('Page template', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const slot = page.locator('#preact-counter + .counter-message'); await expect(slot, 'initial slot content').toHaveText('Hello Preact!'); @@ -116,7 +116,7 @@ test.skip('Multiple frameworks', () => { }); test('React component', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const count = page.locator('#react-counter pre'); await expect(count, 'initial count updated to 0').toHaveText('0'); @@ -129,7 +129,7 @@ test.skip('Multiple frameworks', () => { }); test('Preact component', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const count = page.locator('#preact-counter pre'); await expect(count, 'initial count updated to 0').toHaveText('0'); @@ -142,7 +142,7 @@ test.skip('Multiple frameworks', () => { }); test('Solid component', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const count = page.locator('#solid-counter pre'); await expect(count, 'initial count updated to 0').toHaveText('0'); @@ -157,7 +157,7 @@ test.skip('Multiple frameworks', () => { // TODO: re-enable this test when #3559 is fixed // https://github.com/withastro/astro/issues/3559 test.skip('Vue component', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const count = page.locator('#vue-counter pre'); await expect(count, 'initial count updated to 0').toHaveText('0'); @@ -172,7 +172,7 @@ test.skip('Multiple frameworks', () => { // TODO: track down a reliability issue in this test // It seems to lost connection to the vite server in CI test.skip('Svelte component', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const count = page.locator('#svelte-counter pre'); await expect(count, 'initial count is 0').toHaveText('0'); diff --git a/packages/astro/e2e/namespaced-component.test.js b/packages/astro/e2e/namespaced-component.test.js index 8dc27c5d07ab..58c00713a4fb 100644 --- a/packages/astro/e2e/namespaced-component.test.js +++ b/packages/astro/e2e/namespaced-component.test.js @@ -16,8 +16,8 @@ test.afterAll(async () => { }); test.describe('Hydrating namespaced components', () => { - test('Preact Component', async ({ page }) => { - await page.goto('/'); + test('Preact Component', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/')); // Counter declared with: const namespacedCounter = page.locator('#preact-counter-namespace'); @@ -54,8 +54,8 @@ test.describe('Hydrating namespaced components', () => { await expect(namedCount, 'count incremented by 1').toHaveText('1'); }); - test('MDX', async ({ page }) => { - await page.goto('/mdx'); + test('MDX', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/mdx')); // Counter declared with: const namespacedCounter = page.locator('#preact-counter-namespace'); diff --git a/packages/astro/e2e/nested-in-preact.test.js b/packages/astro/e2e/nested-in-preact.test.js index 5b8105e4506b..f2bc4d7283f1 100644 --- a/packages/astro/e2e/nested-in-preact.test.js +++ b/packages/astro/e2e/nested-in-preact.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Nested Frameworks in Preact', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Nested Frameworks in Preact', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -49,7 +49,7 @@ test.describe('Nested Frameworks in Preact', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -66,7 +66,7 @@ test.describe('Nested Frameworks in Preact', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -83,7 +83,7 @@ test.describe('Nested Frameworks in Preact', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/nested-in-react.test.js b/packages/astro/e2e/nested-in-react.test.js index 57795d0a5173..cd9bc2879de4 100644 --- a/packages/astro/e2e/nested-in-react.test.js +++ b/packages/astro/e2e/nested-in-react.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Nested Frameworks in React', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Nested Frameworks in React', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -49,7 +49,7 @@ test.describe('Nested Frameworks in React', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -66,7 +66,7 @@ test.describe('Nested Frameworks in React', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -83,7 +83,7 @@ test.describe('Nested Frameworks in React', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/nested-in-solid.test.js b/packages/astro/e2e/nested-in-solid.test.js index 3e8acddd33c6..0fab174688ee 100644 --- a/packages/astro/e2e/nested-in-solid.test.js +++ b/packages/astro/e2e/nested-in-solid.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Nested Frameworks in Solid', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Nested Frameworks in Solid', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -49,7 +49,7 @@ test.describe('Nested Frameworks in Solid', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -66,7 +66,7 @@ test.describe('Nested Frameworks in Solid', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -83,7 +83,7 @@ test.describe('Nested Frameworks in Solid', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/nested-in-svelte.test.js b/packages/astro/e2e/nested-in-svelte.test.js index 8089221eb9e2..88aa826a87c7 100644 --- a/packages/astro/e2e/nested-in-svelte.test.js +++ b/packages/astro/e2e/nested-in-svelte.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Nested Frameworks in Svelte', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Nested Frameworks in Svelte', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -49,7 +49,7 @@ test.describe('Nested Frameworks in Svelte', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -66,7 +66,7 @@ test.describe('Nested Frameworks in Svelte', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -83,7 +83,7 @@ test.describe('Nested Frameworks in Svelte', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/nested-in-vue.test.js b/packages/astro/e2e/nested-in-vue.test.js index ae2631ee48e5..6e7c6a5c2197 100644 --- a/packages/astro/e2e/nested-in-vue.test.js +++ b/packages/astro/e2e/nested-in-vue.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Nested Frameworks in Vue', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Nested Frameworks in Vue', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -49,7 +49,7 @@ test.describe('Nested Frameworks in Vue', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -66,7 +66,7 @@ test.describe('Nested Frameworks in Vue', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -83,7 +83,7 @@ test.describe('Nested Frameworks in Vue', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/nested-recursive.test.js b/packages/astro/e2e/nested-recursive.test.js index efcefac53227..262cd67723e9 100644 --- a/packages/astro/e2e/nested-recursive.test.js +++ b/packages/astro/e2e/nested-recursive.test.js @@ -20,7 +20,7 @@ test.afterAll(async () => { test.describe('Recursive Nested Frameworks', () => { test('React counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#react-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -37,7 +37,7 @@ test.describe('Recursive Nested Frameworks', () => { }); test('Preact counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#preact-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -54,7 +54,7 @@ test.describe('Recursive Nested Frameworks', () => { }); test('Solid counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#solid-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -71,7 +71,7 @@ test.describe('Recursive Nested Frameworks', () => { }); test('Vue counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#vue-counter'); await expect(counter, 'component is visible').toBeVisible(); @@ -88,7 +88,7 @@ test.describe('Recursive Nested Frameworks', () => { }); test('Svelte counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const counter = page.locator('#svelte-counter'); await expect(counter, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/pass-js.test.js b/packages/astro/e2e/pass-js.test.js index 503f8274bfa3..9ff6e60479be 100644 --- a/packages/astro/e2e/pass-js.test.js +++ b/packages/astro/e2e/pass-js.test.js @@ -17,7 +17,7 @@ test.afterAll(async () => { test.describe('Passing JS into client components', () => { test('Complex nested objects', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const nestedDate = await page.locator('#nested-date'); await expect(nestedDate, 'component is visible').toBeVisible(); @@ -32,8 +32,8 @@ test.describe('Passing JS into client components', () => { await expect(regExpValue).toHaveText('ok'); }); - test('BigInts', async ({ page }) => { - await page.goto('/'); + test('BigInts', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/')); const bigIntType = await page.locator('#bigint-type'); await expect(bigIntType, 'is visible').toBeVisible(); @@ -44,8 +44,8 @@ test.describe('Passing JS into client components', () => { await expect(bigIntValue).toHaveText('11'); }); - test('Arrays that look like the serialization format', async ({ page }) => { - await page.goto('/'); + test('Arrays that look like the serialization format', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/')); const arrType = await page.locator('#arr-type'); await expect(arrType, 'is visible').toBeVisible(); @@ -56,8 +56,8 @@ test.describe('Passing JS into client components', () => { await expect(arrValue).toHaveText('0,foo'); }); - test('Maps and Sets', async ({ page }) => { - await page.goto('/'); + test('Maps and Sets', async ({ astro, page }) => { + await page.goto(astro.resolveUrl('/')); const mapType = page.locator('#map-type'); await expect(mapType, 'is visible').toBeVisible(); diff --git a/packages/astro/e2e/shared-component-tests.js b/packages/astro/e2e/shared-component-tests.js index ad392fc4a4ce..e8ec273fd475 100644 --- a/packages/astro/e2e/shared-component-tests.js +++ b/packages/astro/e2e/shared-component-tests.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { testFactory, waitForHydrate } from './test-utils.js'; +import { scrollToElement, testFactory, waitForHydrate } from './test-utils.js'; export function prepareTestFactory(opts) { const test = testFactory(opts); @@ -69,7 +69,11 @@ export function prepareTestFactory(opts) { // Make sure the component is on screen to trigger hydration const counter = page.locator('#client-visible'); - await counter.scrollIntoViewIfNeeded(); + // NOTE: Use custom implementation instead of `counter.scrollIntoViewIfNeeded` + // as Playwright's function doesn't take into account of `counter` being hydrated + // and losing the original DOM reference. + await scrollToElement(counter); + await expect(counter, 'component is visible').toBeVisible(); const count = counter.locator('pre'); diff --git a/packages/astro/e2e/solid-circular.test.js b/packages/astro/e2e/solid-circular.test.js index 57e3fc5aa36b..5dd0e8b80cca 100644 --- a/packages/astro/e2e/solid-circular.test.js +++ b/packages/astro/e2e/solid-circular.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Circular imports with Solid', () => { test('Context', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const wrapper = page.locator('#context'); await expect(wrapper, 'context should not be duplicated').toHaveText('fr'); diff --git a/packages/astro/e2e/solid-recurse.test.js b/packages/astro/e2e/solid-recurse.test.js index 2b03ed880102..de3759e9856b 100644 --- a/packages/astro/e2e/solid-recurse.test.js +++ b/packages/astro/e2e/solid-recurse.test.js @@ -15,7 +15,7 @@ test.afterAll(async () => { test.describe('Recursive elements with Solid', () => { test('Counter', async ({ astro, page }) => { - await page.goto('/'); + await page.goto(astro.resolveUrl('/')); const wrapper = page.locator('#case1'); await expect(wrapper, 'component is visible').toBeVisible(); diff --git a/packages/astro/e2e/test-utils.js b/packages/astro/e2e/test-utils.js index a11ba868b625..d025527d6e91 100644 --- a/packages/astro/e2e/test-utils.js +++ b/packages/astro/e2e/test-utils.js @@ -1,8 +1,20 @@ +import fs from 'fs/promises'; +import path from 'path'; import { test as testBase, expect } from '@playwright/test'; import { loadFixture as baseLoadFixture } from '../test/test-utils.js'; export const isWindows = process.platform === 'win32'; +// Get all test files in directory, assign unique port for each of them so they don't conflict +const testFiles = await fs.readdir(new URL('.', import.meta.url)); +const testFileToPort = new Map(); +for (let i = 0; i < testFiles.length; i++) { + const file = testFiles[i]; + if (file.endsWith('.test.js')) { + testFileToPort.set(file.slice(0, -8), 4000 + i); + } +} + export function loadFixture(inlineConfig) { if (!inlineConfig || !inlineConfig.root) throw new Error("Must provide { root: './fixtures/...' }"); @@ -12,6 +24,9 @@ export function loadFixture(inlineConfig) { return baseLoadFixture({ ...inlineConfig, root: new URL(inlineConfig.root, import.meta.url).toString(), + server: { + port: testFileToPort.get(path.basename(inlineConfig.root)), + }, }); } @@ -74,3 +89,13 @@ export async function waitForHydrate(page, el) { `astro-island[uid="${astroIslandId}"]` ); } + +/** + * Scroll to element manually without making sure the `el` is stable + * @param {import('@playwright/test').Locator} el + */ +export async function scrollToElement(el) { + await el.evaluate((node) => { + node.scrollIntoView({ behavior: 'auto' }); + }); +} diff --git a/packages/astro/package.json b/packages/astro/package.json index 7acfa77e64ff..ddfa988c4709 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "2.5.0", + "version": "2.5.1", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "type": "module", "author": "withastro", diff --git a/packages/astro/playwright.config.js b/packages/astro/playwright.config.js index 06ce8ff33658..1f6386f6ca41 100644 --- a/packages/astro/playwright.config.js +++ b/packages/astro/playwright.config.js @@ -24,9 +24,6 @@ const config = { use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3000', - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', }, diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index aa8c7369d616..ee4c9907735c 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -101,15 +101,21 @@ export async function createContentTypesGenerator({ readdir: fs.readdir.bind(fs), readdirSync: fs.readdirSync.bind(fs), }, + onlyFiles: false, + objectMode: true, }); - const entries = globResult - .map((e) => new URL(e, contentPaths.contentDir)) - .filter( - // Config loading handled first. Avoid running twice. - (e) => !e.href.startsWith(contentPaths.config.url.href) - ); - for (const entry of entries) { - events.push({ type: { name: 'add', entry }, opts: { logLevel: 'warn' } }); + + for (const entry of globResult) { + const entryURL = new URL(entry.path, contentPaths.contentDir); + if (entryURL.href.startsWith(contentPaths.config.url.href)) continue; + if (entry.dirent.isFile()) { + events.push({ + type: { name: 'add', entry: entryURL }, + opts: { logLevel: 'warn' }, + }); + } else if (entry.dirent.isDirectory()) { + events.push({ type: { name: 'addDir', entry: entryURL }, opts: { logLevel: 'warn' } }); + } } await runEvents(); return { typesGenerated: true }; @@ -503,9 +509,9 @@ function warnNonexistentCollections({ warn( logging, 'content', - `${JSON.stringify( + `The ${JSON.stringify( configuredCollection - )} is not a collection. Check your content config for typos.` + )} collection does not have an associated folder in your \`content\` directory. Make sure the folder exists, or check your content config for typos.` ); } } diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 546d45975f21..078d43e5012c 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -139,7 +139,7 @@ export class App { defaultStatus = 404; } - let mod = this.#manifest.pageMap.get(routeData.component)!; + let mod = await this.#manifest.pageMap.get(routeData.component)!(); if (routeData.type === 'page') { let response = await this.#renderPage(request, routeData, mod, defaultStatus); @@ -148,7 +148,7 @@ export class App { if (response.status === 500) { const fiveHundredRouteData = matchRoute('/500', this.#manifestData); if (fiveHundredRouteData) { - mod = this.#manifest.pageMap.get(fiveHundredRouteData.component)!; + mod = await this.#manifest.pageMap.get(fiveHundredRouteData.component)!(); try { let fiveHundredResponse = await this.#renderPage( request, diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 89c5bad37a4d..3747e96e3247 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -31,6 +31,7 @@ export interface RouteInfo { export type SerializedRouteInfo = Omit & { routeData: SerializedRouteData; }; +type ImportComponentInstance = () => Promise; export interface SSRManifest { adapterName: string; @@ -39,7 +40,7 @@ export interface SSRManifest { base?: string; assetsPrefix?: string; markdown: MarkdownRenderingOptions; - pageMap: Map; + pageMap: Map; renderers: SSRLoadedRenderer[]; /** * Map of directive name (e.g. `load`) to the directive script code diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 452a902caf69..5a9f075c4c8e 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -169,15 +169,15 @@ async function generatePage( .map(({ sheet }) => sheet) .reduce(mergeInlineCss, []); - const pageModule = ssrEntry.pageMap?.get(pageData.component); + const pageModulePromise = ssrEntry.pageMap?.get(pageData.component); const middleware = ssrEntry.middleware; - if (!pageModule) { + if (!pageModulePromise) { throw new Error( `Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.` ); } - + const pageModule = await pageModulePromise(); if (shouldSkipDraft(pageModule, opts.settings)) { info(opts.logging, null, `${magenta('⚠️')} Skipping draft ${pageData.route.component}`); return; diff --git a/packages/astro/src/core/build/graph.ts b/packages/astro/src/core/build/graph.ts index 6d7335c0955a..68d264b10304 100644 --- a/packages/astro/src/core/build/graph.ts +++ b/packages/astro/src/core/build/graph.ts @@ -42,7 +42,10 @@ export function* walkParentInfos( // Returns true if a module is a top-level page. We determine this based on whether // it is imported by the top-level virtual module. export function moduleIsTopLevelPage(info: ModuleInfo): boolean { - return info.importers[0] === resolvedPagesVirtualModuleId; + return ( + info.importers[0] === resolvedPagesVirtualModuleId || + info.dynamicImporters[0] == resolvedPagesVirtualModuleId + ); } // This function walks the dependency graph, going up until it finds a page component. diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts index 982a90ce348e..d601a9342b42 100644 --- a/packages/astro/src/core/build/plugins/index.ts +++ b/packages/astro/src/core/build/plugins/index.ts @@ -7,6 +7,7 @@ import { pluginComponentEntry } from './plugin-component-entry.js'; import { pluginCSS } from './plugin-css.js'; import { pluginHoistedScripts } from './plugin-hoisted-scripts.js'; import { pluginInternals } from './plugin-internals.js'; +import { pluginMiddleware } from './plugin-middleware.js'; import { pluginPages } from './plugin-pages.js'; import { pluginPrerender } from './plugin-prerender.js'; import { pluginSSR } from './plugin-ssr.js'; @@ -16,6 +17,7 @@ export function registerAllPlugins({ internals, options, register }: AstroBuildP register(pluginAliasResolve(internals)); register(pluginAnalyzer(internals)); register(pluginInternals(internals)); + register(pluginMiddleware(options, internals)); register(pluginPages(options, internals)); register(pluginCSS(options, internals)); register(astroHeadBuildPlugin(options, internals)); diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts new file mode 100644 index 000000000000..507c4ae710a0 --- /dev/null +++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts @@ -0,0 +1,63 @@ +import type { Plugin as VitePlugin } from 'vite'; +import { MIDDLEWARE_PATH_SEGMENT_NAME } from '../../constants.js'; +import { addRollupInput } from '../add-rollup-input.js'; +import type { BuildInternals } from '../internal.js'; +import type { AstroBuildPlugin } from '../plugin'; +import type { StaticBuildOptions } from '../types'; + +export const MIDDLEWARE_MODULE_ID = '@astro-middleware'; +export const RESOLVED_MIDDLEWARE_MODULE_ID = '\0@astro-middleware'; + +let inputs: Set = new Set(); +export function vitePluginMiddleware( + opts: StaticBuildOptions, + _internals: BuildInternals +): VitePlugin { + return { + name: '@astro/plugin-middleware', + options(options) { + if (opts.settings.config.experimental.middleware) { + return addRollupInput(options, [MIDDLEWARE_MODULE_ID]); + } + }, + + resolveId(id) { + if (id === MIDDLEWARE_MODULE_ID && opts.settings.config.experimental.middleware) { + return RESOLVED_MIDDLEWARE_MODULE_ID; + } + }, + + async load(id) { + if (id === RESOLVED_MIDDLEWARE_MODULE_ID && opts.settings.config.experimental.middleware) { + const imports: string[] = []; + const exports: string[] = []; + let middlewareId = await this.resolve( + `${opts.settings.config.srcDir.pathname}/${MIDDLEWARE_PATH_SEGMENT_NAME}` + ); + if (middlewareId) { + imports.push(`import { onRequest } from "${middlewareId.id}"`); + exports.push(`export { onRequest }`); + } + const result = [imports.join('\n'), exports.join('\n')]; + + return result.join('\n'); + } + }, + }; +} + +export function pluginMiddleware( + opts: StaticBuildOptions, + internals: BuildInternals +): AstroBuildPlugin { + return { + build: 'ssr', + hooks: { + 'build:before': () => { + return { + vitePlugin: vitePluginMiddleware(opts, internals), + }; + }, + }, + }; +} diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index ba4f0df32e50..80e28b0eee95 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -1,10 +1,10 @@ import type { Plugin as VitePlugin } from 'vite'; import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../../app/index.js'; -import { MIDDLEWARE_PATH_SEGMENT_NAME } from '../../constants.js'; import { addRollupInput } from '../add-rollup-input.js'; import { eachPageData, type BuildInternals } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin'; import type { StaticBuildOptions } from '../types'; +import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin { return { @@ -24,19 +24,14 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V async load(id) { if (id === resolvedPagesVirtualModuleId) { - let middlewareId = null; - if (opts.settings.config.experimental.middleware) { - middlewareId = await this.resolve( - `${opts.settings.config.srcDir.pathname}/${MIDDLEWARE_PATH_SEGMENT_NAME}` - ); - } - let importMap = ''; let imports = []; let i = 0; for (const pageData of eachPageData(internals)) { const variable = `_page${i}`; - imports.push(`import * as ${variable} from ${JSON.stringify(pageData.moduleSpecifier)};`); + imports.push( + `const ${variable} = () => import(${JSON.stringify(pageData.moduleSpecifier)});` + ); importMap += `[${JSON.stringify(pageData.component)}, ${variable}],`; i++; } @@ -54,11 +49,15 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V const def = `${imports.join('\n')} -${middlewareId ? `import * as _middleware from "${middlewareId.id}";` : ''} +${ + opts.settings.config.experimental.middleware + ? `import * as _middleware from "${MIDDLEWARE_MODULE_ID}";` + : '' +} export const pageMap = new Map([${importMap}]); export const renderers = [${rendererItems}]; -${middlewareId ? `export const middleware = _middleware;` : ''} +${opts.settings.config.experimental.middleware ? `export const middleware = _middleware;` : ''} `; return def; diff --git a/packages/astro/src/core/build/plugins/plugin-prerender.ts b/packages/astro/src/core/build/plugins/plugin-prerender.ts index 7c9f3f7848ae..e787b17d6691 100644 --- a/packages/astro/src/core/build/plugins/plugin-prerender.ts +++ b/packages/astro/src/core/build/plugins/plugin-prerender.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import type { Plugin as VitePlugin } from 'vite'; import type { BuildInternals } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin.js'; @@ -25,7 +26,7 @@ function vitePluginPrerender(opts: StaticBuildOptions, internals: BuildInternals } pageInfo.route.prerender = false; // dynamic pages should all go in their own chunk in the pages/* directory - return `pages/all`; + return `pages/${path.basename(pageInfo.component)}`; } }, }); diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index ca186a727da2..1ee0feb63e4b 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -13,7 +13,7 @@ import { joinPaths, prependForwardSlash } from '../../path.js'; import { serializeRouteData } from '../../routing/index.js'; import { addRollupInput } from '../add-rollup-input.js'; import { getOutFile, getOutFolder } from '../common.js'; -import { cssOrder, eachPageData, mergeInlineCss, type BuildInternals } from '../internal.js'; +import { cssOrder, mergeInlineCss, type BuildInternals } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin'; export const virtualModuleId = '@astrojs-ssr-virtual-entry'; @@ -151,34 +151,26 @@ function buildManifest( } }; - for (const pageData of eachPageData(internals)) { - if (!pageData.route.prerender) continue; - if (!pageData.route.pathname) continue; + for (const route of opts.manifest.routes) { + if (!route.prerender) continue; + if (!route.pathname) continue; - const outFolder = getOutFolder( - opts.settings.config, - pageData.route.pathname!, - pageData.route.type - ); - const outFile = getOutFile( - opts.settings.config, - outFolder, - pageData.route.pathname!, - pageData.route.type - ); + const outFolder = getOutFolder(opts.settings.config, route.pathname!, route.type); + const outFile = getOutFile(opts.settings.config, outFolder, route.pathname!, route.type); const file = outFile.toString().replace(opts.settings.config.build.client.toString(), ''); routes.push({ file, links: [], scripts: [], styles: [], - routeData: serializeRouteData(pageData.route, settings.config.trailingSlash), + routeData: serializeRouteData(route, settings.config.trailingSlash), }); staticFiles.push(file); } - for (const pageData of eachPageData(internals)) { - if (pageData.route.prerender) continue; + for (const route of opts.manifest.routes) { + const pageData = internals.pagesByComponent.get(route.component); + if (route.prerender || !pageData) continue; const scripts: SerializedRouteInfo['scripts'] = []; if (pageData.hoistedScript) { const hoistedValue = pageData.hoistedScript.value; @@ -217,7 +209,7 @@ function buildManifest( .map(({ stage, content }) => ({ stage, children: content })), ], styles, - routeData: serializeRouteData(pageData.route, settings.config.trailingSlash), + routeData: serializeRouteData(route, settings.config.trailingSlash), }); } diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 7d5424eb459e..111c471898ad 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -25,6 +25,7 @@ import { generatePages } from './generate.js'; import { trackPageData } from './internal.js'; import { createPluginContainer, type AstroBuildPluginContainer } from './plugin.js'; import { registerAllPlugins } from './plugins/index.js'; +import { RESOLVED_MIDDLEWARE_MODULE_ID } from './plugins/plugin-middleware.js'; import type { PageBuildData, StaticBuildOptions } from './types'; import { getTimeStat } from './util.js'; @@ -172,6 +173,8 @@ async function ssrBuild( entryFileNames(chunkInfo) { if (chunkInfo.facadeModuleId === resolvedPagesVirtualModuleId) { return opts.buildConfig.serverEntry; + } else if (chunkInfo.facadeModuleId === RESOLVED_MIDDLEWARE_MODULE_ID) { + return 'middleware.mjs'; } else { return '[name].mjs'; } diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts index d04f7476c61c..c0f38de45b43 100644 --- a/packages/astro/src/core/build/types.ts +++ b/packages/astro/src/core/build/types.ts @@ -47,8 +47,10 @@ export interface StaticBuildOptions { teardownCompiler: boolean; } +type ImportComponentInstance = () => Promise; + export interface SingleFileBuiltModule { - pageMap: Map; + pageMap: Map; middleware: AstroMiddlewareInstance; renderers: SSRLoadedRenderer[]; } diff --git a/packages/astro/src/core/routing/validation.ts b/packages/astro/src/core/routing/validation.ts index 047a5b8923ef..911a97a9e4a9 100644 --- a/packages/astro/src/core/routing/validation.ts +++ b/packages/astro/src/core/routing/validation.ts @@ -1,3 +1,4 @@ +import { bold } from 'kleur/colors'; import type { ComponentInstance, GetStaticPathsResult, RouteData } from '../../@types/astro'; import { AstroError, AstroErrorData } from '../errors/index.js'; import type { LogOptions } from '../logger/core'; @@ -32,7 +33,11 @@ export function validateDynamicRouteModule( } ) { if (ssr && mod.getStaticPaths && !mod.prerender) { - warn(logging, 'getStaticPaths', 'getStaticPaths() is ignored when "output: server" is set.'); + warn( + logging, + 'getStaticPaths', + `getStaticPaths() in ${bold(route.component)} is ignored when "output: server" is set.` + ); } if ((!ssr || mod.prerender) && !mod.getStaticPaths) { throw new AstroError({ diff --git a/packages/astro/test/ssr-dynamic.test.js b/packages/astro/test/ssr-dynamic.test.js index a75ace7e3c2a..e4bb61b4a7fb 100644 --- a/packages/astro/test/ssr-dynamic.test.js +++ b/packages/astro/test/ssr-dynamic.test.js @@ -11,6 +11,19 @@ describe('Dynamic pages in SSR', () => { fixture = await loadFixture({ root: './fixtures/ssr-dynamic/', output: 'server', + integrations: [ + { + name: 'inject-routes', + hooks: { + 'astro:config:setup': ({ injectRoute }) => { + injectRoute({ + pattern: '/path-alias/[id]', + entryPoint: './src/pages/api/products/[id].js', + }); + }, + }, + }, + ], adapter: testAdapter(), }); await fixture.build(); @@ -55,6 +68,11 @@ describe('Dynamic pages in SSR', () => { expect(json.id).to.equal('33'); }); + it('Injected route work', async () => { + const json = await fetchJSON('/path-alias/33'); + expect(json.id).to.equal('33'); + }); + it('Public assets take priority', async () => { const favicon = await matchRoute('/favicon.ico'); expect(favicon).to.equal(undefined); diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index e26ab8c6d508..b618f8593a42 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -151,7 +151,7 @@ export async function loadFixture(inlineConfig) { }; const resolveUrl = (url) => - `http://${config.server.host}:${config.server.port}${url.replace(/^\/?/, '/')}`; + `http://${config.server.host || 'localhost'}:${config.server.port}${url.replace(/^\/?/, '/')}`; // A map of files that have been edited. let fileEdits = new Map(); diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index 5541200ba640..6a0081fe9b90 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -42,7 +42,7 @@ "tiny-glob": "^0.2.9" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "astro": "workspace:*", diff --git a/packages/integrations/deno/package.json b/packages/integrations/deno/package.json index f5f9657d8bb6..e58e3549141c 100644 --- a/packages/integrations/deno/package.json +++ b/packages/integrations/deno/package.json @@ -36,7 +36,7 @@ "esbuild": "^0.15.18" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "astro": "workspace:*", diff --git a/packages/integrations/image/package.json b/packages/integrations/image/package.json index a685dcd74e01..f3cd9f772abe 100644 --- a/packages/integrations/image/package.json +++ b/packages/integrations/image/package.json @@ -62,7 +62,7 @@ "vite": "^4.3.1" }, "peerDependencies": { - "astro": "workspace:^2.5.0", + "astro": "workspace:^2.5.1", "sharp": ">=0.31.0" }, "peerDependenciesMeta": { diff --git a/packages/integrations/markdoc/CHANGELOG.md b/packages/integrations/markdoc/CHANGELOG.md index 4728f1863785..f39c514c468d 100644 --- a/packages/integrations/markdoc/CHANGELOG.md +++ b/packages/integrations/markdoc/CHANGELOG.md @@ -1,5 +1,14 @@ # @astrojs/markdoc +## 0.2.1 + +### Patch Changes + +- [#7141](https://github.com/withastro/astro/pull/7141) [`a9e1cd7e5`](https://github.com/withastro/astro/commit/a9e1cd7e58794fe220539c2ed935c9eb96bab55a) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Fix inconsistent Markdoc heading IDs for documents with the same headings. + +- Updated dependencies [[`72f686a68`](https://github.com/withastro/astro/commit/72f686a68930de52f9a274c13c98acad59925b31), [`319a0a7a0`](https://github.com/withastro/astro/commit/319a0a7a0a6a950387c942b467746d590bb32fda), [`852d59a8d`](https://github.com/withastro/astro/commit/852d59a8d68e124f10852609e0f1619d5838ac76), [`530fb9ebe`](https://github.com/withastro/astro/commit/530fb9ebee77646921ec29d45d9b66484bdfb521), [`3257dd289`](https://github.com/withastro/astro/commit/3257dd28901c785a6a661211b98c5ef2cb3b9aa4)]: + - astro@2.5.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json index acda2e24001b..e67ff83e0458 100644 --- a/packages/integrations/markdoc/package.json +++ b/packages/integrations/markdoc/package.json @@ -1,7 +1,7 @@ { "name": "@astrojs/markdoc", "description": "Add support for Markdoc pages in your Astro site", - "version": "0.2.0", + "version": "0.2.1", "type": "module", "types": "./dist/index.d.ts", "author": "withastro", @@ -47,7 +47,7 @@ "zod": "^3.17.3" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "@astrojs/markdown-remark": "^2.2.1", diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts index 4360800a09d3..627f08c778fe 100644 --- a/packages/integrations/markdoc/src/index.ts +++ b/packages/integrations/markdoc/src/index.ts @@ -10,7 +10,7 @@ import { emitESMImage } from 'astro/assets'; import { bold, red, yellow } from 'kleur/colors'; import type * as rollup from 'rollup'; import { loadMarkdocConfig, type MarkdocConfigResult } from './load-config.js'; -import { applyDefaultConfig } from './runtime.js'; +import { setupConfig } from './runtime.js'; type SetupHookParams = HookParameters<'astro:config:setup'> & { // `contentEntryType` is not a public API @@ -52,7 +52,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration async getRenderModule({ entry, viteId }) { const ast = Markdoc.parse(entry.body); const pluginContext = this; - const markdocConfig = applyDefaultConfig(userMarkdocConfig, entry); + const markdocConfig = setupConfig(userMarkdocConfig, entry); const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => { return ( @@ -90,7 +90,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration const res = `import { jsx as h } from 'astro/jsx-runtime'; import { Renderer } from '@astrojs/markdoc/components'; - import { collectHeadings, applyDefaultConfig, Markdoc, headingSlugger } from '@astrojs/markdoc/runtime'; + import { collectHeadings, setupConfig, Markdoc } from '@astrojs/markdoc/runtime'; import * as entry from ${JSON.stringify(viteId + '?astroContentCollectionEntry')}; ${ markdocConfigResult @@ -113,16 +113,14 @@ export function getHeadings() { instead of the Content component. Would remove double-transform and unlock variable resolution in heading slugs. */ '' } - headingSlugger.reset(); const headingConfig = userConfig.nodes?.heading; - const config = applyDefaultConfig(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); + const config = setupConfig(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); const ast = Markdoc.Ast.fromJSON(stringifiedAst); const content = Markdoc.transform(ast, config); return collectHeadings(Array.isArray(content) ? content : content.children); } export async function Content (props) { - headingSlugger.reset(); - const config = applyDefaultConfig({ + const config = setupConfig({ ...userConfig, variables: { ...userConfig.variables, ...props }, }, entry); diff --git a/packages/integrations/markdoc/src/nodes/heading.ts b/packages/integrations/markdoc/src/nodes/heading.ts index 8adf57612ae3..0210e9b9080e 100644 --- a/packages/integrations/markdoc/src/nodes/heading.ts +++ b/packages/integrations/markdoc/src/nodes/heading.ts @@ -1,10 +1,19 @@ -import Markdoc, { type RenderableTreeNode, type Schema } from '@markdoc/markdoc'; +import Markdoc, { type ConfigType, type RenderableTreeNode, type Schema } from '@markdoc/markdoc'; import Slugger from 'github-slugger'; import { getTextContent } from '../runtime.js'; -export const headingSlugger = new Slugger(); +type ConfigTypeWithCtx = ConfigType & { + // TODO: decide on `ctx` as a convention for config merging + ctx: { + headingSlugger: Slugger; + }; +}; -function getSlug(attributes: Record, children: RenderableTreeNode[]): string { +function getSlug( + attributes: Record, + children: RenderableTreeNode[], + headingSlugger: Slugger +): string { if (attributes.id && typeof attributes.id === 'string') { return attributes.id; } @@ -21,11 +30,11 @@ export const heading: Schema = { id: { type: String }, level: { type: Number, required: true, default: 1 }, }, - transform(node, config) { + transform(node, config: ConfigTypeWithCtx) { const { level, ...attributes } = node.transformAttributes(config); const children = node.transformChildren(config); - const slug = getSlug(attributes, children); + const slug = getSlug(attributes, children, config.ctx.headingSlugger); const render = config.nodes?.heading?.render ?? `h${level}`; const tagProps = @@ -39,3 +48,16 @@ export const heading: Schema = { return new Markdoc.Tag(render, tagProps, children); }, }; + +export function setupHeadingConfig(): ConfigTypeWithCtx { + const headingSlugger = new Slugger(); + + return { + ctx: { + headingSlugger, + }, + nodes: { + heading, + }, + }; +} diff --git a/packages/integrations/markdoc/src/nodes/index.ts b/packages/integrations/markdoc/src/nodes/index.ts index c25b03f27563..4cd7e3667bd8 100644 --- a/packages/integrations/markdoc/src/nodes/index.ts +++ b/packages/integrations/markdoc/src/nodes/index.ts @@ -1,4 +1,4 @@ import { heading } from './heading.js'; -export { headingSlugger } from './heading.js'; +export { setupHeadingConfig } from './heading.js'; export const nodes = { heading }; diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts index 61b38fd02978..3164cda1356c 100644 --- a/packages/integrations/markdoc/src/runtime.ts +++ b/packages/integrations/markdoc/src/runtime.ts @@ -4,27 +4,45 @@ import Markdoc, { type RenderableTreeNode, } from '@markdoc/markdoc'; import type { ContentEntryModule } from 'astro'; -import { nodes as astroNodes } from './nodes/index.js'; +import { setupHeadingConfig } from './nodes/index.js'; -/** Used to reset Slugger cache on each build at runtime */ +/** Used to call `Markdoc.transform()` and `Markdoc.Ast` in runtime modules */ export { default as Markdoc } from '@markdoc/markdoc'; -export { headingSlugger } from './nodes/index.js'; -export function applyDefaultConfig( - config: MarkdocConfig, - entry: ContentEntryModule -): MarkdocConfig { +/** + * Merge user config with default config and set up context (ex. heading ID slugger) + * Called on each file's individual transform + */ +export function setupConfig(userConfig: MarkdocConfig, entry: ContentEntryModule): MarkdocConfig { + const defaultConfig: MarkdocConfig = { + // `setupXConfig()` could become a "plugin" convention as well? + ...setupHeadingConfig(), + variables: { entry }, + }; + return mergeConfig(defaultConfig, userConfig); +} + +/** Merge function from `@markdoc/markdoc` internals */ +function mergeConfig(configA: MarkdocConfig, configB: MarkdocConfig): MarkdocConfig { return { - ...config, - variables: { - entry, - ...config.variables, + ...configA, + ...configB, + tags: { + ...configA.tags, + ...configB.tags, }, nodes: { - ...astroNodes, - ...config.nodes, + ...configA.nodes, + ...configB.nodes, + }, + functions: { + ...configA.functions, + ...configB.functions, + }, + variables: { + ...configA.variables, + ...configB.variables, }, - // TODO: Syntax highlighting }; } diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings-stale-cache-check.mdoc b/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings-stale-cache-check.mdoc new file mode 100644 index 000000000000..75cd52884dc7 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings-stale-cache-check.mdoc @@ -0,0 +1,13 @@ +Our heading ID generator can have a stale cache for duplicates. Let's check for those! + +# Level 1 heading + +## Level **2 heading** + +### Level _3 heading_ + +#### Level [4 heading](/with-a-link) + +##### Level 5 heading with override {% #id-override %} + +###### Level 6 heading diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/[slug].astro similarity index 58% rename from packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro rename to packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/[slug].astro index 5880be0e3db7..2baef9d69637 100644 --- a/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/[slug].astro @@ -1,8 +1,14 @@ --- -import { getEntryBySlug } from "astro:content"; +import { getCollection, CollectionEntry } from "astro:content"; -const post = await getEntryBySlug('docs', 'headings'); -const { Content, headings } = await post.render(); +export async function getStaticPaths() { + const docs = await getCollection('docs'); + return docs.map(doc => ({ params: { slug: doc.slug }, props: doc })); +} + +type Props = CollectionEntry<'docs'>; + +const { Content, headings } = await Astro.props.render(); --- diff --git a/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings-stale-cache-check.mdoc b/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings-stale-cache-check.mdoc new file mode 100644 index 000000000000..75cd52884dc7 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings-stale-cache-check.mdoc @@ -0,0 +1,13 @@ +Our heading ID generator can have a stale cache for duplicates. Let's check for those! + +# Level 1 heading + +## Level **2 heading** + +### Level _3 heading_ + +#### Level [4 heading](/with-a-link) + +##### Level 5 heading with override {% #id-override %} + +###### Level 6 heading diff --git a/packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/headings/src/pages/[slug].astro similarity index 58% rename from packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro rename to packages/integrations/markdoc/test/fixtures/headings/src/pages/[slug].astro index 5880be0e3db7..2baef9d69637 100644 --- a/packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro +++ b/packages/integrations/markdoc/test/fixtures/headings/src/pages/[slug].astro @@ -1,8 +1,14 @@ --- -import { getEntryBySlug } from "astro:content"; +import { getCollection, CollectionEntry } from "astro:content"; -const post = await getEntryBySlug('docs', 'headings'); -const { Content, headings } = await post.render(); +export async function getStaticPaths() { + const docs = await getCollection('docs'); + return docs.map(doc => ({ params: { slug: doc.slug }, props: doc })); +} + +type Props = CollectionEntry<'docs'>; + +const { Content, headings } = await Astro.props.render(); --- diff --git a/packages/integrations/markdoc/test/headings.test.js b/packages/integrations/markdoc/test/headings.test.js index 5db50065cb62..5468e8c6b3d4 100644 --- a/packages/integrations/markdoc/test/headings.test.js +++ b/packages/integrations/markdoc/test/headings.test.js @@ -27,7 +27,15 @@ describe('Markdoc - Headings', () => { }); it('applies IDs to headings', async () => { - const res = await fixture.fetch('/'); + const res = await fixture.fetch('/headings'); + const html = await res.text(); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates the same IDs for other documents with the same headings', async () => { + const res = await fixture.fetch('/headings-stale-cache-check'); const html = await res.text(); const { document } = parseHTML(html); @@ -35,7 +43,7 @@ describe('Markdoc - Headings', () => { }); it('generates a TOC with correct info', async () => { - const res = await fixture.fetch('/'); + const res = await fixture.fetch('/headings'); const html = await res.text(); const { document } = parseHTML(html); @@ -49,14 +57,21 @@ describe('Markdoc - Headings', () => { }); it('applies IDs to headings', async () => { - const html = await fixture.readFile('/index.html'); + const html = await fixture.readFile('/headings/index.html'); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates the same IDs for other documents with the same headings', async () => { + const html = await fixture.readFile('/headings-stale-cache-check/index.html'); const { document } = parseHTML(html); idTest(document); }); it('generates a TOC with correct info', async () => { - const html = await fixture.readFile('/index.html'); + const html = await fixture.readFile('/headings/index.html'); const { document } = parseHTML(html); tocTest(document); @@ -83,7 +98,15 @@ describe('Markdoc - Headings with custom Astro renderer', () => { }); it('applies IDs to headings', async () => { - const res = await fixture.fetch('/'); + const res = await fixture.fetch('/headings'); + const html = await res.text(); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates the same IDs for other documents with the same headings', async () => { + const res = await fixture.fetch('/headings-stale-cache-check'); const html = await res.text(); const { document } = parseHTML(html); @@ -91,7 +114,7 @@ describe('Markdoc - Headings with custom Astro renderer', () => { }); it('generates a TOC with correct info', async () => { - const res = await fixture.fetch('/'); + const res = await fixture.fetch('/headings'); const html = await res.text(); const { document } = parseHTML(html); @@ -99,7 +122,7 @@ describe('Markdoc - Headings with custom Astro renderer', () => { }); it('renders Astro component for each heading', async () => { - const res = await fixture.fetch('/'); + const res = await fixture.fetch('/headings'); const html = await res.text(); const { document } = parseHTML(html); @@ -113,21 +136,28 @@ describe('Markdoc - Headings with custom Astro renderer', () => { }); it('applies IDs to headings', async () => { - const html = await fixture.readFile('/index.html'); + const html = await fixture.readFile('/headings/index.html'); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates the same IDs for other documents with the same headings', async () => { + const html = await fixture.readFile('/headings-stale-cache-check/index.html'); const { document } = parseHTML(html); idTest(document); }); it('generates a TOC with correct info', async () => { - const html = await fixture.readFile('/index.html'); + const html = await fixture.readFile('/headings/index.html'); const { document } = parseHTML(html); tocTest(document); }); it('renders Astro component for each heading', async () => { - const html = await fixture.readFile('/index.html'); + const html = await fixture.readFile('/headings/index.html'); const { document } = parseHTML(html); astroComponentTest(document); diff --git a/packages/integrations/netlify/package.json b/packages/integrations/netlify/package.json index fce39c646a7d..09dc06567b16 100644 --- a/packages/integrations/netlify/package.json +++ b/packages/integrations/netlify/package.json @@ -42,7 +42,7 @@ "esbuild": "^0.15.18" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "@netlify/edge-functions": "^2.0.0", diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index f98b58f5927d..980614b15859 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -38,7 +38,7 @@ "server-destroy": "^1.0.1" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "@types/send": "^0.17.1", diff --git a/packages/integrations/prefetch/CHANGELOG.md b/packages/integrations/prefetch/CHANGELOG.md index 96cd904bb605..aef697362467 100644 --- a/packages/integrations/prefetch/CHANGELOG.md +++ b/packages/integrations/prefetch/CHANGELOG.md @@ -1,5 +1,11 @@ # @astrojs/prefetch +## 0.2.3 + +### Patch Changes + +- [#7123](https://github.com/withastro/astro/pull/7123) [`147373722`](https://github.com/withastro/astro/commit/147373722b37126af949bb054a1cdfb0aed6c2ff) Thanks [@connor-baer](https://github.com/connor-baer)! - Fix the inclusion of `@types/network-information`. + ## 0.2.2 ### Patch Changes diff --git a/packages/integrations/prefetch/package.json b/packages/integrations/prefetch/package.json index 03c7508e91dd..899d41d41567 100644 --- a/packages/integrations/prefetch/package.json +++ b/packages/integrations/prefetch/package.json @@ -1,7 +1,7 @@ { "name": "@astrojs/prefetch", "description": "Prefetch page navigations in your Astro site", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "types": "./dist/index.d.ts", "author": "withastro", diff --git a/packages/integrations/prefetch/src/client.ts b/packages/integrations/prefetch/src/client.ts index 40a226cc5623..a8b88d85c308 100644 --- a/packages/integrations/prefetch/src/client.ts +++ b/packages/integrations/prefetch/src/client.ts @@ -1,5 +1,5 @@ +/// import throttles from 'throttles'; -import '../@types/network-information.d.ts'; import requestIdleCallback from './requestIdleCallback.js'; const events = ['mouseenter', 'touchstart', 'focus']; diff --git a/packages/integrations/svelte/package.json b/packages/integrations/svelte/package.json index 8f646974527e..bb3b0e7c35b3 100644 --- a/packages/integrations/svelte/package.json +++ b/packages/integrations/svelte/package.json @@ -48,7 +48,7 @@ "vite": "^4.3.1" }, "peerDependencies": { - "astro": "workspace:^2.5.0", + "astro": "workspace:^2.5.1", "svelte": "^3.54.0" }, "engines": { diff --git a/packages/integrations/tailwind/package.json b/packages/integrations/tailwind/package.json index ad8ee6b37956..236e42bd5af8 100644 --- a/packages/integrations/tailwind/package.json +++ b/packages/integrations/tailwind/package.json @@ -44,7 +44,7 @@ "vite": "^4.3.1" }, "peerDependencies": { - "astro": "workspace:^2.5.0", + "astro": "workspace:^2.5.1", "tailwindcss": "^3.0.24" }, "pnpm": { diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json index 012eb264a29b..eab3cd41fb2a 100644 --- a/packages/integrations/vercel/package.json +++ b/packages/integrations/vercel/package.json @@ -59,7 +59,7 @@ "web-vitals": "^3.1.1" }, "peerDependencies": { - "astro": "workspace:^2.5.0" + "astro": "workspace:^2.5.1" }, "devDependencies": { "@types/set-cookie-parser": "^2.4.2", diff --git a/packages/integrations/vue/package.json b/packages/integrations/vue/package.json index b49456f5ec3c..3c226be405c5 100644 --- a/packages/integrations/vue/package.json +++ b/packages/integrations/vue/package.json @@ -56,7 +56,7 @@ "vue": "^3.2.37" }, "peerDependencies": { - "astro": "workspace:^2.5.0", + "astro": "workspace:^2.5.1", "vue": "^3.2.30" }, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e38d5fba4423..d75e43cba08b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -409,8 +409,8 @@ importers: examples/with-markdoc: dependencies: '@astrojs/markdoc': - specifier: ^0.1.2 - version: 0.1.2(astro@packages+astro) + specifier: ^0.2.0 + version: link:../../packages/integrations/markdoc astro: specifier: ^2.4.1 version: link:../../packages/astro @@ -5521,23 +5521,6 @@ packages: vscode-uri: 3.0.7 dev: false - /@astrojs/markdoc@0.1.2(astro@packages+astro): - resolution: {integrity: sha512-pOHMGQ/az9UOw+3X21cViPlZBwtxYXZpF8bv9pBQquVJGsfrBv4VZhdJkiXkwp2sq+SXa/Bxk02EXhevUVogCQ==} - engines: {node: '>=16.12.0'} - peerDependencies: - astro: '*' - dependencies: - '@markdoc/markdoc': 0.2.2 - astro: link:packages/astro - esbuild: 0.17.18 - gray-matter: 4.0.3 - kleur: 4.1.5 - zod: 3.20.6 - transitivePeerDependencies: - - '@types/react' - - react - dev: false - /@babel/code-frame@7.21.4: resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} engines: {node: '>=6.9.0'}