diff --git a/.changeset/fifty-weeks-bake.md b/.changeset/fifty-weeks-bake.md deleted file mode 100644 index 714be802e..000000000 --- a/.changeset/fifty-weeks-bake.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'@astrojs/cloudflare': minor ---- - -Add support for the following Node.js Runtime APIs, which are availabe in [Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/nodejs) using the `node:` syntax. - -- assert -- AsyncLocalStorage -- Buffer -- Diagnostics Channel -- EventEmitter -- path -- process -- Streams -- StringDecoder -- util - -```js -import { Buffer } from 'node:buffer'; -``` diff --git a/.github/assets/banner.png b/.github/assets/banner.png index 91dfc11e3..7696fafb7 100644 Binary files a/.github/assets/banner.png and b/.github/assets/banner.png differ diff --git a/.github/assets/deepgram-dark.svg b/.github/assets/deepgram-dark.svg index 3531c9554..9b66066ff 100644 --- a/.github/assets/deepgram-dark.svg +++ b/.github/assets/deepgram-dark.svg @@ -1,35 +1 @@ - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/.github/assets/deepgram.svg b/.github/assets/deepgram.svg index dd3090044..ec55ad3fa 100644 --- a/.github/assets/deepgram.svg +++ b/.github/assets/deepgram.svg @@ -1,35 +1 @@ - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/.github/assets/monogram-dark.svg b/.github/assets/monogram-dark.svg index ab2d43ac2..feccf6fe8 100644 --- a/.github/assets/monogram-dark.svg +++ b/.github/assets/monogram-dark.svg @@ -1,11 +1 @@ - - - - - - - - - - - + \ No newline at end of file diff --git a/.github/assets/monogram.svg b/.github/assets/monogram.svg index 35a377e5e..e60aa428e 100644 --- a/.github/assets/monogram.svg +++ b/.github/assets/monogram.svg @@ -1,11 +1 @@ - - - - - - - - - - - + \ No newline at end of file diff --git a/.github/assets/netlify-dark.svg b/.github/assets/netlify-dark.svg index d974206de..2fcff1dde 100644 --- a/.github/assets/netlify-dark.svg +++ b/.github/assets/netlify-dark.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/.github/assets/netlify.svg b/.github/assets/netlify.svg index 7a068f244..aa07cbcab 100644 --- a/.github/assets/netlify.svg +++ b/.github/assets/netlify.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/.github/assets/qoddi-dark.png b/.github/assets/qoddi-dark.png index 623654acf..b666799d9 100644 Binary files a/.github/assets/qoddi-dark.png and b/.github/assets/qoddi-dark.png differ diff --git a/.github/assets/qoddi.png b/.github/assets/qoddi.png index e664cfd28..e7a1c3c43 100644 Binary files a/.github/assets/qoddi.png and b/.github/assets/qoddi.png differ diff --git a/.github/assets/sentry-dark.svg b/.github/assets/sentry-dark.svg index f194ec36e..1e09e141b 100644 --- a/.github/assets/sentry-dark.svg +++ b/.github/assets/sentry-dark.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/.github/assets/sentry.svg b/.github/assets/sentry.svg index a4470b601..56e122e04 100644 --- a/.github/assets/sentry.svg +++ b/.github/assets/sentry.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/.github/assets/shipshape-dark.svg b/.github/assets/shipshape-dark.svg index d270d2748..038534ec9 100644 --- a/.github/assets/shipshape-dark.svg +++ b/.github/assets/shipshape-dark.svg @@ -1,20 +1 @@ - - - outline-logo - - + \ No newline at end of file diff --git a/.github/assets/shipshape.svg b/.github/assets/shipshape.svg index f2d1a8dc3..6c2e2ee70 100644 --- a/.github/assets/shipshape.svg +++ b/.github/assets/shipshape.svg @@ -1,20 +1 @@ - - - outline-logo - - + \ No newline at end of file diff --git a/.github/assets/stackup-dark.svg b/.github/assets/stackup-dark.svg index 3b8ed2746..b7a458410 100644 --- a/.github/assets/stackup-dark.svg +++ b/.github/assets/stackup-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/.github/assets/stackup.svg b/.github/assets/stackup.svg index 207f28b2a..6faa178f5 100644 --- a/.github/assets/stackup.svg +++ b/.github/assets/stackup.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/.github/assets/storyblok-dark.svg b/.github/assets/storyblok-dark.svg index d9917a453..9df58c4bb 100644 --- a/.github/assets/storyblok-dark.svg +++ b/.github/assets/storyblok-dark.svg @@ -1,22 +1 @@ - - - sb-logo-full-color - - - - + \ No newline at end of file diff --git a/.github/assets/storyblok.svg b/.github/assets/storyblok.svg index 7270bb296..ec7759356 100644 --- a/.github/assets/storyblok.svg +++ b/.github/assets/storyblok.svg @@ -1,15 +1 @@ - - - sb-logo-full-color - - - - + \ No newline at end of file diff --git a/.github/assets/vercel-dark.svg b/.github/assets/vercel-dark.svg index 31c2d5b88..1fbf0317d 100644 --- a/.github/assets/vercel-dark.svg +++ b/.github/assets/vercel-dark.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/.github/assets/vercel.svg b/.github/assets/vercel.svg index d2b7685c5..6711aa56f 100644 --- a/.github/assets/vercel.svg +++ b/.github/assets/vercel.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/examples/basics/package.json b/examples/basics/package.json index 85b886d4b..311cb2931 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/blog/package.json b/examples/blog/package.json index b2ad50da8..32741a476 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -14,6 +14,6 @@ "@astrojs/mdx": "^1.1.0", "@astrojs/rss": "^3.0.0", "@astrojs/sitemap": "^3.0.0", - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/blog/public/blog-placeholder-1.jpg b/examples/blog/public/blog-placeholder-1.jpg index b4c20543e..74d4009b5 100644 Binary files a/examples/blog/public/blog-placeholder-1.jpg and b/examples/blog/public/blog-placeholder-1.jpg differ diff --git a/examples/blog/public/blog-placeholder-2.jpg b/examples/blog/public/blog-placeholder-2.jpg index a819b59c2..c4214b0e6 100644 Binary files a/examples/blog/public/blog-placeholder-2.jpg and b/examples/blog/public/blog-placeholder-2.jpg differ diff --git a/examples/blog/public/blog-placeholder-3.jpg b/examples/blog/public/blog-placeholder-3.jpg index 067802c0f..fbe2ac0cb 100644 Binary files a/examples/blog/public/blog-placeholder-3.jpg and b/examples/blog/public/blog-placeholder-3.jpg differ diff --git a/examples/blog/public/blog-placeholder-4.jpg b/examples/blog/public/blog-placeholder-4.jpg index 947e7eaab..f4fc88e29 100644 Binary files a/examples/blog/public/blog-placeholder-4.jpg and b/examples/blog/public/blog-placeholder-4.jpg differ diff --git a/examples/blog/public/blog-placeholder-about.jpg b/examples/blog/public/blog-placeholder-about.jpg index 7b4aafbec..cf5f68532 100644 Binary files a/examples/blog/public/blog-placeholder-about.jpg and b/examples/blog/public/blog-placeholder-about.jpg differ diff --git a/examples/component/package.json b/examples/component/package.json index fc0aa1e19..16b5ddf92 100644 --- a/examples/component/package.json +++ b/examples/component/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" }, "peerDependencies": { "astro": "^2.0.0-beta.0" diff --git a/examples/deno/package.json b/examples/deno/package.json index dbf19159e..d1f6b0fef 100644 --- a/examples/deno/package.json +++ b/examples/deno/package.json @@ -10,7 +10,7 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" }, "devDependencies": { "@astrojs/deno": "^5.0.0" diff --git a/examples/framework-alpine/package.json b/examples/framework-alpine/package.json index 34dfd5bb1..3c04a77e9 100644 --- a/examples/framework-alpine/package.json +++ b/examples/framework-alpine/package.json @@ -14,6 +14,6 @@ "@astrojs/alpinejs": "^0.3.0", "@types/alpinejs": "^3.7.2", "alpinejs": "^3.12.3", - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/framework-lit/package.json b/examples/framework-lit/package.json index dc3b50539..ae14bd1f9 100644 --- a/examples/framework-lit/package.json +++ b/examples/framework-lit/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/lit": "^3.0.0", "@webcomponents/template-shadowroot": "^0.2.1", - "astro": "^3.1.1", + "astro": "^3.1.2", "lit": "^2.8.0" } } diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json index c00d687e5..0ea2307fc 100644 --- a/examples/framework-multiple/package.json +++ b/examples/framework-multiple/package.json @@ -16,7 +16,7 @@ "@astrojs/solid-js": "^3.0.1", "@astrojs/svelte": "^4.0.2", "@astrojs/vue": "^3.0.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "preact": "^10.17.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/examples/framework-preact/package.json b/examples/framework-preact/package.json index 941a20c78..3cfa824f9 100644 --- a/examples/framework-preact/package.json +++ b/examples/framework-preact/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.0.0", "@preact/signals": "^1.2.1", - "astro": "^3.1.1", + "astro": "^3.1.2", "preact": "^10.17.1" } } diff --git a/examples/framework-react/package.json b/examples/framework-react/package.json index f258a4ee6..ce8c0f8d9 100644 --- a/examples/framework-react/package.json +++ b/examples/framework-react/package.json @@ -14,7 +14,7 @@ "@astrojs/react": "^3.0.2", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", - "astro": "^3.1.1", + "astro": "^3.1.2", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/examples/framework-solid/package.json b/examples/framework-solid/package.json index 101ce6424..587d086c1 100644 --- a/examples/framework-solid/package.json +++ b/examples/framework-solid/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/solid-js": "^3.0.1", - "astro": "^3.1.1", + "astro": "^3.1.2", "solid-js": "^1.7.11" } } diff --git a/examples/framework-svelte/package.json b/examples/framework-svelte/package.json index 5b8426439..ffb7c047f 100644 --- a/examples/framework-svelte/package.json +++ b/examples/framework-svelte/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/svelte": "^4.0.2", - "astro": "^3.1.1", + "astro": "^3.1.2", "svelte": "^4.2.0" } } diff --git a/examples/framework-vue/package.json b/examples/framework-vue/package.json index 13807d5aa..e1d7e60a3 100644 --- a/examples/framework-vue/package.json +++ b/examples/framework-vue/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/vue": "^3.0.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "vue": "^3.3.4" } } diff --git a/examples/hackernews/package.json b/examples/hackernews/package.json index 1624fca82..966db9df1 100644 --- a/examples/hackernews/package.json +++ b/examples/hackernews/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/node": "^6.0.0", - "astro": "^3.1.1" + "@astrojs/node": "^6.0.1", + "astro": "^3.1.2" } } diff --git a/examples/integration/package.json b/examples/integration/package.json index 918b3ee79..0c5134723 100644 --- a/examples/integration/package.json +++ b/examples/integration/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" }, "peerDependencies": { "astro": "^2.0.0-beta.0" diff --git a/examples/middleware/package.json b/examples/middleware/package.json index aadcb936e..29476419a 100644 --- a/examples/middleware/package.json +++ b/examples/middleware/package.json @@ -12,8 +12,8 @@ "server": "node dist/server/entry.mjs" }, "dependencies": { - "@astrojs/node": "^6.0.0", - "astro": "^3.1.1", + "@astrojs/node": "^6.0.1", + "astro": "^3.1.2", "html-minifier": "^4.0.0" } } diff --git a/examples/minimal/package.json b/examples/minimal/package.json index d0afec388..f6509d12e 100644 --- a/examples/minimal/package.json +++ b/examples/minimal/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/non-html-pages/package.json b/examples/non-html-pages/package.json index 866aed7d4..2cc266069 100644 --- a/examples/non-html-pages/package.json +++ b/examples/non-html-pages/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/portfolio/package.json b/examples/portfolio/package.json index c03444410..5bd0c74a3 100644 --- a/examples/portfolio/package.json +++ b/examples/portfolio/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/portfolio/public/assets/portrait.jpg b/examples/portfolio/public/assets/portrait.jpg index fce4bd355..f1c8984bd 100644 Binary files a/examples/portfolio/public/assets/portrait.jpg and b/examples/portfolio/public/assets/portrait.jpg differ diff --git a/examples/portfolio/public/assets/stock-1.jpg b/examples/portfolio/public/assets/stock-1.jpg index 5835b1464..c8dec6b96 100644 Binary files a/examples/portfolio/public/assets/stock-1.jpg and b/examples/portfolio/public/assets/stock-1.jpg differ diff --git a/examples/portfolio/public/assets/stock-2.jpg b/examples/portfolio/public/assets/stock-2.jpg index 0d49a725f..3ad4b7150 100644 Binary files a/examples/portfolio/public/assets/stock-2.jpg and b/examples/portfolio/public/assets/stock-2.jpg differ diff --git a/examples/portfolio/public/assets/stock-3.jpg b/examples/portfolio/public/assets/stock-3.jpg index b2bdff1b7..27068541c 100644 Binary files a/examples/portfolio/public/assets/stock-3.jpg and b/examples/portfolio/public/assets/stock-3.jpg differ diff --git a/examples/ssr/package.json b/examples/ssr/package.json index db4bedd88..4fe4216bd 100644 --- a/examples/ssr/package.json +++ b/examples/ssr/package.json @@ -12,9 +12,9 @@ "server": "node dist/server/entry.mjs" }, "dependencies": { - "@astrojs/node": "^6.0.0", + "@astrojs/node": "^6.0.1", "@astrojs/svelte": "^4.0.2", - "astro": "^3.1.1", + "astro": "^3.1.2", "svelte": "^4.2.0" } } diff --git a/examples/ssr/public/images/products/cereal.jpg b/examples/ssr/public/images/products/cereal.jpg index c1f4cce4a..35601a789 100644 Binary files a/examples/ssr/public/images/products/cereal.jpg and b/examples/ssr/public/images/products/cereal.jpg differ diff --git a/examples/ssr/public/images/products/muffins.jpg b/examples/ssr/public/images/products/muffins.jpg index 897733ee8..ced2d9a91 100644 Binary files a/examples/ssr/public/images/products/muffins.jpg and b/examples/ssr/public/images/products/muffins.jpg differ diff --git a/examples/ssr/public/images/products/oats.jpg b/examples/ssr/public/images/products/oats.jpg index b8db72ae0..54ae1ebdb 100644 Binary files a/examples/ssr/public/images/products/oats.jpg and b/examples/ssr/public/images/products/oats.jpg differ diff --git a/examples/ssr/public/images/products/yogurt.jpg b/examples/ssr/public/images/products/yogurt.jpg index 9cd39666d..73c1b9a85 100644 Binary files a/examples/ssr/public/images/products/yogurt.jpg and b/examples/ssr/public/images/products/yogurt.jpg differ diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index 4fec42e94..91d86bbcc 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/markdoc": "^0.5.0", - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/with-markdown-plugins/package.json b/examples/with-markdown-plugins/package.json index fc8c83334..89ce661a9 100644 --- a/examples/with-markdown-plugins/package.json +++ b/examples/with-markdown-plugins/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/markdown-remark": "^3.2.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "hast-util-select": "^5.0.5", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.1.0", diff --git a/examples/with-markdown-shiki/package.json b/examples/with-markdown-shiki/package.json index 3d7f11cca..abfe2fa13 100644 --- a/examples/with-markdown-shiki/package.json +++ b/examples/with-markdown-shiki/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1" + "astro": "^3.1.2" } } diff --git a/examples/with-mdx/package.json b/examples/with-mdx/package.json index facaf432e..b43afb47e 100644 --- a/examples/with-mdx/package.json +++ b/examples/with-mdx/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/mdx": "^1.1.0", "@astrojs/preact": "^3.0.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "preact": "^10.17.1" } } diff --git a/examples/with-nanostores/package.json b/examples/with-nanostores/package.json index 9efbc21cf..0889b23ba 100644 --- a/examples/with-nanostores/package.json +++ b/examples/with-nanostores/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.0.0", "@nanostores/preact": "^0.5.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "nanostores": "^0.9.3", "preact": "^10.17.1" } diff --git a/examples/with-nanostores/public/images/astronaut-figurine.png b/examples/with-nanostores/public/images/astronaut-figurine.png index f5a278b9c..aac9b445e 100644 Binary files a/examples/with-nanostores/public/images/astronaut-figurine.png and b/examples/with-nanostores/public/images/astronaut-figurine.png differ diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json index 76b0d02e5..e2195bbd9 100644 --- a/examples/with-tailwindcss/package.json +++ b/examples/with-tailwindcss/package.json @@ -14,7 +14,7 @@ "@astrojs/mdx": "^1.1.0", "@astrojs/tailwind": "^5.0.0", "@types/canvas-confetti": "^1.6.0", - "astro": "^3.1.1", + "astro": "^3.1.2", "autoprefixer": "^10.4.15", "canvas-confetti": "^1.6.0", "postcss": "^8.4.28", diff --git a/examples/with-vite-plugin-pwa/package.json b/examples/with-vite-plugin-pwa/package.json index d8f176dc3..75c3916f4 100644 --- a/examples/with-vite-plugin-pwa/package.json +++ b/examples/with-vite-plugin-pwa/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "astro": "^3.1.1", + "astro": "^3.1.2", "vite-plugin-pwa": "0.16.4", "workbox-window": "^7.0.0" } diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json index d4fd8a86e..48ca02f98 100644 --- a/examples/with-vitest/package.json +++ b/examples/with-vitest/package.json @@ -12,7 +12,7 @@ "test": "vitest" }, "dependencies": { - "astro": "^3.1.1", + "astro": "^3.1.2", "vitest": "^0.34.2" } } diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 1a9537e78..f4caccd22 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,28 @@ # astro +## 3.1.2 + +### Patch Changes + +- [#8612](https://github.com/withastro/astro/pull/8612) [`bcad715ce`](https://github.com/withastro/astro/commit/bcad715ce67bc73a7927c941d1e7f02a82d638c2) Thanks [@matthewp](https://github.com/matthewp)! - Ensure cookies are attached when middleware changes the Response + +- [#8598](https://github.com/withastro/astro/pull/8598) [`bdd267d08`](https://github.com/withastro/astro/commit/bdd267d08937611984d074a2872af11ecf3e1a12) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Fix relative images in Markdown breaking the build process in certain circumstances + +- [#8382](https://github.com/withastro/astro/pull/8382) [`e522a5eb4`](https://github.com/withastro/astro/commit/e522a5eb41c7df1e62c307c84cd14d53777439ff) Thanks [@DerTimonius](https://github.com/DerTimonius)! - Do not throw an error for an empty collection directory. + +- [#8600](https://github.com/withastro/astro/pull/8600) [`ed54d4644`](https://github.com/withastro/astro/commit/ed54d46449accc99ad117d6b0d50a8905e4d65d7) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Improve config info telemetry + +- [#8592](https://github.com/withastro/astro/pull/8592) [`70f2a8003`](https://github.com/withastro/astro/commit/70f2a80039d232731f63ea735e896997ec0eac7a) Thanks [@bluwy](https://github.com/bluwy)! - Fix alias plugin causing CSS ordering issue + +- [#8614](https://github.com/withastro/astro/pull/8614) [`4398e9298`](https://github.com/withastro/astro/commit/4398e929877dfadd2067af28413284afdfde9d8b) Thanks [@lilnasy](https://github.com/lilnasy)! - Fixed an issue where spaces and unicode characters in project path prevented middleware from running. + +- [#8603](https://github.com/withastro/astro/pull/8603) [`8f8b9069d`](https://github.com/withastro/astro/commit/8f8b9069ddd21cf57d37955ab3a92710492226f5) Thanks [@matthewp](https://github.com/matthewp)! - Prevent body scripts from re-executing on navigation + +- [#8609](https://github.com/withastro/astro/pull/8609) [`5a988eaf6`](https://github.com/withastro/astro/commit/5a988eaf609ddc1b9609acb0cdc2dda43d10a5c2) Thanks [@bluwy](https://github.com/bluwy)! - Fix Astro HMR from a CSS dependency + +- Updated dependencies [[`ed54d4644`](https://github.com/withastro/astro/commit/ed54d46449accc99ad117d6b0d50a8905e4d65d7)]: + - @astrojs/telemetry@3.0.2 + ## 3.1.1 ### Patch Changes diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 9c0a9dfdd..aa266af13 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -17,18 +17,26 @@ const { fallback = 'animate' } = Astro.props as Props; index: number; scrollX: number; scrollY: number; + intraPage?: boolean; }; type Events = 'astro:page-load' | 'astro:after-swap'; // only update history entries that are managed by us // leave other entries alone and do not accidently add state. const persistState = (state: State) => history.state && history.replaceState(state, ''); + // @ts-expect-error: startViewTransition might exist const supportsViewTransitions = !!document.startViewTransition; const transitionEnabledOnThisPage = () => !!document.querySelector('[name="astro-view-transitions-enabled"]'); const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name)); const onPageLoad = () => triggerEvent('astro:page-load'); const PERSIST_ATTR = 'data-astro-transition-persist'; + const parser = new DOMParser(); + // explained at its usage + let noopEl: HTMLDivElement; + if (import.meta.env.DEV) { + noopEl = document.createElement('div'); + } // The History API does not tell you if navigation is forward or back, so // you can figure it using an index. On pushState the index is incremented so you @@ -40,7 +48,7 @@ const { fallback = 'animate' } = Astro.props as Props; currentHistoryIndex = history.state.index; scrollTo({ left: history.state.scrollX, top: history.state.scrollY }); } else if (transitionEnabledOnThisPage()) { - history.replaceState({ index: currentHistoryIndex, scrollX, scrollY }, ''); + history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, ''); } const throttle = (cb: (...args: any[]) => any, delay: number) => { let wait = false; @@ -64,19 +72,28 @@ const { fallback = 'animate' } = Astro.props as Props; }; }; - async function getHTML(href: string) { + // returns the contents of the page or null if the router can't deal with it. + async function fetchHTML( + href: string + ): Promise { try { const res = await fetch(href); + // drop potential charset (+ other name/value pairs) as parser needs the mediaType + const mediaType = res.headers.get('content-type')?.replace(/;.*$/, ''); + // the DOMParser can handle two types of HTML + if (mediaType !== 'text/html' && mediaType !== 'application/xhtml+xml') { + // everything else (e.g. audio/mp3) will be handled by the browser but not by us + return null; + } const html = await res.text(); return { - ok: res.ok, html, redirected: res.redirected ? res.url : undefined, - // drop potential charset (+ other name/value pairs) as parser needs the mediaType - mediaType: res.headers.get('content-type')?.replace(/;.*$/, ''), + mediaType, }; } catch (err) { - return { ok: false }; + // can't fetch, let someone else deal with it. + return null; } } @@ -98,19 +115,19 @@ const { fallback = 'animate' } = Astro.props as Props; let wait = Promise.resolve(); for (const script of Array.from(document.scripts)) { if (script.dataset.astroExec === '') continue; - const s = document.createElement('script'); - s.innerHTML = script.innerHTML; + const newScript = document.createElement('script'); + newScript.innerHTML = script.innerHTML; for (const attr of script.attributes) { if (attr.name === 'src') { const p = new Promise((r) => { - s.onload = r; + newScript.onload = r; }); wait = wait.then(() => p as any); } - s.setAttribute(attr.name, attr.value); + newScript.setAttribute(attr.name, attr.value); } - s.dataset.astroExec = ''; - script.replaceWith(s); + newScript.dataset.astroExec = ''; + script.replaceWith(newScript); } return wait; } @@ -122,46 +139,60 @@ const { fallback = 'animate' } = Astro.props as Props; return style.animationIterationCount === 'infinite'; } - const parser = new DOMParser(); + const updateHistoryAndScrollPosition = (toLocation) => { + if (toLocation.href !== location.href) { + history.pushState( + { index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 }, + '', + toLocation.href + ); + // now we are on the new page for non-history navigations! + // (with history navigation page change happens before popstate is fired) + } + // freshly loaded pages start from the top + scrollTo({ left: 0, top: 0, behavior: 'instant' }); - // A noop element used to prevent styles from being removed - if (import.meta.env.DEV) { - var noopEl = document.createElement('div'); - } + if (toLocation.hash) { + // because we are already on the target page ... + // ... what comes next is a intra-page navigation + // that won't reload the page but instead scroll to the fragment + location.href = toLocation.href; + } + }; - async function updateDOM(doc: Document, loc: URL, state?: State, fallback?: Fallback) { - // Check for a head element that should persist, either because it has the data - // attribute or is a link el. + // replace head and body of the windows document with contents from newDocument + // if !popstate, update the history entry and scroll position according to toLocation + // if popState is given, this holds the scroll position for history navigation + // if fallback === "animate" then simulate view transitions + async function updateDOM( + newDocument: Document, + toLocation: URL, + popState?: State, + fallback?: Fallback + ) { + // Check for a head element that should persist and returns it, + // either because it has the data attribute or is a link el. const persistedHeadElement = (el: HTMLElement): Element | null => { const id = el.getAttribute(PERSIST_ATTR); - const newEl = id && doc.head.querySelector(`[${PERSIST_ATTR}="${id}"]`); + const newEl = id && newDocument.head.querySelector(`[${PERSIST_ATTR}="${id}"]`); if (newEl) { return newEl; } if (el.matches('link[rel=stylesheet]')) { const href = el.getAttribute('href'); - return doc.head.querySelector(`link[rel=stylesheet][href="${href}"]`); + return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`); } - if (el.tagName === 'SCRIPT') { - let s1 = el as HTMLScriptElement; - for (const s2 of doc.scripts) { - if ( - // Inline - (s1.textContent && s1.textContent === s2.textContent) || - // External - (s1.type === s2.type && s1.src === s2.src) - ) { - return s2; - } - } - } - // Only run this in dev. This will get stripped from production builds and is not needed. + // What follows is a fix for an issue (#8472) with missing client:only styles after transition. + // That problem exists only in dev mode where styles are injected into the page by Vite. + // Returning a noop element ensures that the styles are not removed from the old document. + // Guarding the code below with the dev mode check + // allows tree shaking to remove this code in production. if (import.meta.env.DEV) { if (el.tagName === 'STYLE' && el.dataset.viteDevId) { const devId = el.dataset.viteDevId; // If this same style tag exists, remove it from the new page return ( - doc.querySelector(`style[data-astro-dev-id="${devId}"]`) || + newDocument.querySelector(`style[data-astro-dev-id="${devId}"]`) || // Otherwise, keep it anyways. This is client:only styles. noopEl ); @@ -171,10 +202,6 @@ const { fallback = 'animate' } = Astro.props as Props; }; const swap = () => { - // noscript tags inside head element are not honored on swap (#7969). - // Remove them before swapping. - doc.querySelectorAll('head noscript').forEach((el) => el.remove()); - // swap attributes of the html element // - delete all attributes from the current document // - insert all attributes from doc @@ -183,10 +210,26 @@ const { fallback = 'animate' } = Astro.props as Props; const astro = [...html.attributes].filter( ({ name }) => (html.removeAttribute(name), name.startsWith('data-astro-')) ); - [...doc.documentElement.attributes, ...astro].forEach(({ name, value }) => + [...newDocument.documentElement.attributes, ...astro].forEach(({ name, value }) => html.setAttribute(name, value) ); + // Replace scripts in both the head and body. + for (const s1 of document.scripts) { + for (const s2 of newDocument.scripts) { + if ( + // Inline + (s1.textContent && s1.textContent === s2.textContent) || + // External + (s1.type === s2.type && s1.src === s2.src) + ) { + s2.remove(); + } else { + s1.remove(); + } + } + } + // Swap head for (const el of Array.from(document.head.children)) { const newEl = persistedHeadElement(el as HTMLElement); @@ -199,12 +242,15 @@ const { fallback = 'animate' } = Astro.props as Props; el.remove(); } } + // Everything left in the new head is new, append it all. - document.head.append(...doc.head.children); + document.head.append(...newDocument.head.children); // Persist elements in the existing body const oldBody = document.body; - document.body.replaceWith(doc.body); + + // this will reset scroll Position + document.body.replaceWith(newDocument.body); for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) { const id = el.getAttribute(PERSIST_ATTR); const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`); @@ -215,39 +261,18 @@ const { fallback = 'animate' } = Astro.props as Props; } } - // Simulate scroll behavior of Safari and - // Chromium based browsers (Chrome, Edge, Opera, ...) - scrollTo({ left: 0, top: 0, behavior: 'instant' }); - - let initialScrollX = 0; - let initialScrollY = 0; - if (!state && loc.hash) { - const id = decodeURIComponent(loc.hash.slice(1)); - const elem = document.getElementById(id); - // prefer scrollIntoView() over scrollTo() because it takes scroll-padding into account - if (elem) { - elem.scrollIntoView(); - initialScrollX = Math.max( - 0, - elem.offsetLeft + elem.offsetWidth - document.documentElement.clientWidth - ); - initialScrollY = elem.offsetTop; - } - } else if (state) { - scrollTo(state.scrollX, state.scrollY); // usings default scrollBehavior + if (popState) { + scrollTo(popState.scrollX, popState.scrollY); // usings 'auto' scrollBehavior + } else { + updateHistoryAndScrollPosition(toLocation); } - !state && - history.pushState( - { index: ++currentHistoryIndex, scrollX: initialScrollX, scrollY: initialScrollY }, - '', - loc.href - ); + triggerEvent('astro:after-swap'); }; // Wait on links to finish, to prevent FOUC const links: Promise[] = []; - for (const el of doc.querySelectorAll('head link[rel=stylesheet]')) { + for (const el of newDocument.querySelectorAll('head link[rel=stylesheet]')) { // Do not preload links that are already on the page. if ( !document.querySelector( @@ -287,32 +312,44 @@ const { fallback = 'animate' } = Astro.props as Props; } } - async function navigate(dir: Direction, loc: URL, state?: State) { + async function transition(direction: Direction, toLocation: URL, popState?: State) { let finished: Promise; - const href = loc.href; - const { html, ok, mediaType, redirected } = await getHTML(href); - // if there was a redirection, show the final URL in the browser's address bar - redirected && (loc = new URL(redirected)); + const href = toLocation.href; + const response = await fetchHTML(href); // If there is a problem fetching the new page, just do an MPA navigation to it. - if (!ok || !(mediaType === 'text/html' || mediaType === 'application/xhtml+xml')) { + if (response === null) { + location.href = href; + return; + } + // if there was a redirection, show the final URL in the browser's address bar + if (response.redirected) { + toLocation = new URL(response.redirected); + } + + const newDocument = parser.parseFromString(response.html, response.mediaType); + // The next line might look like a hack, + // but it is actually necessary as noscript elements + // and their contents are returned as markup by the parser, + // see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString + newDocument.querySelectorAll('noscript').forEach((el) => el.remove()); + + if (!newDocument.querySelector('[name="astro-view-transitions-enabled"]')) { location.href = href; return; } - const doc = parser.parseFromString(html, mediaType); - if (!doc.querySelector('[name="astro-view-transitions-enabled"]')) { - location.href = href; - return; + if (!popState) { + // save the current scroll position before we change the DOM and transition to the new page + history.replaceState({ ...history.state, scrollX, scrollY }, ''); } - - // Now we are sure that we will push state, and it is time to create a state if it is still missing. - !state && history.replaceState({ index: currentHistoryIndex, scrollX, scrollY }, ''); - - document.documentElement.dataset.astroTransition = dir; + document.documentElement.dataset.astroTransition = direction; if (supportsViewTransitions) { - finished = document.startViewTransition(() => updateDOM(doc, loc, state)).finished; + // @ts-expect-error: startViewTransition exist + finished = document.startViewTransition(() => + updateDOM(newDocument, toLocation, popState) + ).finished; } else { - finished = updateDOM(doc, loc, state, getFallback()); + finished = updateDOM(newDocument, toLocation, popState, getFallback()); } try { await finished; @@ -328,7 +365,9 @@ const { fallback = 'animate' } = Astro.props as Props; // Prefetching function maybePrefetch(pathname: string) { if (document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return; + // @ts-expect-error: connection might exist if (navigator.connection) { + // @ts-expect-error: connection does exist let conn = navigator.connection; if (conn.saveData || /(2|3)g/.test(conn.effectiveType || '')) return; } @@ -339,8 +378,6 @@ const { fallback = 'animate' } = Astro.props as Props; } if (supportsViewTransitions || getFallback() !== 'none') { - markScriptsExec(); - document.addEventListener('click', (ev) => { let link = ev.target; if (link instanceof Element && link.tagName !== 'A') { @@ -362,51 +399,59 @@ const { fallback = 'animate' } = Astro.props as Props; ev.ctrlKey || // new tab (windows) ev.altKey || // download ev.shiftKey || // new window - ev.defaultPrevented || - !transitionEnabledOnThisPage() + ev.defaultPrevented ) { // No page transitions in these cases, // Let the browser standard action handle this return; } - // We do not need to handle same page links because there are no page transitions - // Same page means same path and same query params (but different hash) - if (location.pathname === link.pathname && location.search === link.search) { - if (link.hash) { - // The browser default action will handle navigations with hash fragments - return; - } else { - // Special case: self link without hash - // If handed to the browser it will reload the page - // But we want to handle it like any other same page navigation - // So we scroll to the top of the page but do not start page transitions - ev.preventDefault(); - // push state on the first navigation but not if we were here already - if (location.hash) { - history.replaceState( - { index: currentHistoryIndex, scrollX, scrollY: -(scrollY + 1) }, - '' - ); - const newState: State = { index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 }; - history.pushState(newState, '', link.href); - } - scrollTo({ left: 0, top: 0, behavior: 'instant' }); - return; - } - } - - // these are the cases we will handle: same origin, different page ev.preventDefault(); - navigate('forward', new URL(link.href)); + navigate(link.href); }); + function navigate(href) { + // not ours + if (!transitionEnabledOnThisPage()) { + location.href = href; + return; + } + const toLocation = new URL(href, location.href); + // We do not have page transitions on navigations to the same page (intra-page navigation) + // but we want to handle prevent reload on navigation to the same page + // Same page means same origin, path and query params (but maybe different hash) + if ( + location.origin === toLocation.origin && + location.pathname === toLocation.pathname && + location.search === toLocation.search + ) { + // mark current position as non transition intra-page scrolling + if (location.href !== toLocation.href) { + history.replaceState({ ...history.state, intraPage: true }, ''); + history.pushState( + { index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 }, + '', + toLocation.href + ); + } + if (toLocation.hash) { + location.href = toLocation.href; + } else { + scrollTo({ left: 0, top: 0, behavior: 'instant' }); + } + } else { + transition('forward', toLocation); + } + } + addEventListener('popstate', (ev) => { if (!transitionEnabledOnThisPage() && ev.state) { // The current page doesn't have View Transitions enabled // but the page we navigate to does (because it set the state). // Do a full page refresh to reload the client-side router from the new page. // Scroll restauration will then happen during the reload when the router's code is re-executed - history.scrollRestoration && (history.scrollRestoration = 'manual'); + if (history.scrollRestoration) { + history.scrollRestoration = 'manual'; + } location.reload(); return; } @@ -429,13 +474,14 @@ const { fallback = 'animate' } = Astro.props as Props; } const state: State = history.state; - const nextIndex = state.index; - const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back'; - currentHistoryIndex = nextIndex; - if (state.scrollY < 0) { - scrollTo(state.scrollX, -(state.scrollY + 1)); + if (state.intraPage) { + // this is non transition intra-page scrolling + scrollTo(state.scrollX, state.scrollY); } else { - navigate(direction, new URL(location.href), state); + const nextIndex = state.index; + const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back'; + currentHistoryIndex = nextIndex; + transition(direction, new URL(location.href), state); } }); @@ -457,6 +503,7 @@ const { fallback = 'animate' } = Astro.props as Props; { passive: true, capture: true } ); }); + addEventListener('load', onPageLoad); // There's not a good way to record scroll position before a back button. // So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position. @@ -466,5 +513,7 @@ const { fallback = 'animate' } = Astro.props as Props; if ('onscrollend' in window) addEventListener('scrollend', updateState); else addEventListener('scroll', throttle(updateState, 300)); + + markScriptsExec(); } diff --git a/packages/astro/e2e/astro-component.test.js b/packages/astro/e2e/astro-component.test.js index 7308ea292..c96e9b1c4 100644 --- a/packages/astro/e2e/astro-component.test.js +++ b/packages/astro/e2e/astro-component.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { getColor, testFactory } from './test-utils.js'; +import { testFactory } from './test-utils.js'; const test = testFactory({ root: './fixtures/astro-component/' }); @@ -99,7 +99,7 @@ test.describe('Astro component HMR', () => { test('update linked dep Astro style', async ({ page, astro }) => { await page.goto(astro.resolveUrl('/')); let h1 = page.locator('#astro-linked-lib'); - expect(await getColor(h1)).toBe('rgb(255, 0, 0)'); + await expect(h1).toHaveCSS('color', 'rgb(255, 0, 0)'); await Promise.all([ page.waitForLoadState('networkidle'), await astro.editFile('../_deps/astro-linked-lib/Component.astro', (content) => @@ -107,6 +107,6 @@ test.describe('Astro component HMR', () => { ), ]); h1 = page.locator('#astro-linked-lib'); - expect(await getColor(h1)).toBe('rgb(0, 128, 0)'); + await expect(h1).toHaveCSS('color', 'rgb(0, 128, 0)'); }); }); diff --git a/packages/astro/e2e/css.test.js b/packages/astro/e2e/css.test.js index 184e5dba3..b302d9d90 100644 --- a/packages/astro/e2e/css.test.js +++ b/packages/astro/e2e/css.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { getColor, testFactory } from './test-utils.js'; +import { testFactory } from './test-utils.js'; const test = testFactory({ root: './fixtures/css/', @@ -20,13 +20,13 @@ test.describe('CSS HMR', () => { await page.goto(astro.resolveUrl('/')); const h = page.locator('h1'); - expect(await getColor(h)).toBe('rgb(255, 0, 0)'); + await expect(h).toHaveCSS('color', 'rgb(255, 0, 0)'); await astro.editFile('./src/styles/main.css', (original) => original.replace('--h1-color: red;', '--h1-color: green;') ); - expect(await getColor(h)).toBe('rgb(0, 128, 0)'); + await expect(h).toHaveCSS('color', 'rgb(0, 128, 0)'); }); test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => { diff --git a/packages/astro/e2e/fixtures/invalidate-script-deps/package.json b/packages/astro/e2e/fixtures/hmr/package.json similarity index 50% rename from packages/astro/e2e/fixtures/invalidate-script-deps/package.json rename to packages/astro/e2e/fixtures/hmr/package.json index 4b45ad505..f5aa41460 100644 --- a/packages/astro/e2e/fixtures/invalidate-script-deps/package.json +++ b/packages/astro/e2e/fixtures/hmr/package.json @@ -1,8 +1,9 @@ { - "name": "@e2e/invalidate-script-deps", + "name": "@e2e/hmr", "version": "0.0.0", "private": true, "devDependencies": { - "astro": "workspace:*" + "astro": "workspace:*", + "sass": "^1.66.1" } } diff --git a/packages/astro/e2e/fixtures/hmr/src/pages/css-dep.astro b/packages/astro/e2e/fixtures/hmr/src/pages/css-dep.astro new file mode 100644 index 000000000..38f4e7409 --- /dev/null +++ b/packages/astro/e2e/fixtures/hmr/src/pages/css-dep.astro @@ -0,0 +1,14 @@ + + + Test + + +

This is blue

+ + + diff --git a/packages/astro/e2e/fixtures/invalidate-script-deps/src/pages/index.astro b/packages/astro/e2e/fixtures/hmr/src/pages/script-dep.astro similarity index 100% rename from packages/astro/e2e/fixtures/invalidate-script-deps/src/pages/index.astro rename to packages/astro/e2e/fixtures/hmr/src/pages/script-dep.astro diff --git a/packages/astro/e2e/fixtures/invalidate-script-deps/src/scripts/heading.js b/packages/astro/e2e/fixtures/hmr/src/scripts/heading.js similarity index 100% rename from packages/astro/e2e/fixtures/invalidate-script-deps/src/scripts/heading.js rename to packages/astro/e2e/fixtures/hmr/src/scripts/heading.js diff --git a/packages/astro/e2e/fixtures/hmr/src/styles/vars.scss b/packages/astro/e2e/fixtures/hmr/src/styles/vars.scss new file mode 100644 index 000000000..5deae109f --- /dev/null +++ b/packages/astro/e2e/fixtures/hmr/src/styles/vars.scss @@ -0,0 +1 @@ +$color: blue; \ No newline at end of file diff --git a/packages/astro/e2e/fixtures/view-transitions/public/logo.svg b/packages/astro/e2e/fixtures/view-transitions/public/logo.svg index e9c63b295..8a5f0edd8 100644 --- a/packages/astro/e2e/fixtures/view-transitions/public/logo.svg +++ b/packages/astro/e2e/fixtures/view-transitions/public/logo.svg @@ -1,13 +1 @@ - + \ No newline at end of file diff --git a/packages/astro/e2e/fixtures/view-transitions/src/assets/penguin.jpg b/packages/astro/e2e/fixtures/view-transitions/src/assets/penguin.jpg index 9e859269c..b292daad5 100644 Binary files a/packages/astro/e2e/fixtures/view-transitions/src/assets/penguin.jpg and b/packages/astro/e2e/fixtures/view-transitions/src/assets/penguin.jpg differ diff --git a/packages/astro/e2e/fixtures/view-transitions/src/components/InlineScript.astro b/packages/astro/e2e/fixtures/view-transitions/src/components/InlineScript.astro new file mode 100644 index 000000000..5418c5a64 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/components/InlineScript.astro @@ -0,0 +1,9 @@ +
Count
+ diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro index c6547dc20..82dda9475 100644 --- a/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro @@ -11,4 +11,5 @@ import Layout from '../components/Layout.astro'; load page / no navigation + load page / no navigation diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-one.astro new file mode 100644 index 000000000..e887fe6a5 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-one.astro @@ -0,0 +1,8 @@ +--- +import Layout from '../components/Layout.astro'; +import InlineScript from '../components/InlineScript.astro'; +--- + + + Go to 2 + diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-two.astro new file mode 100644 index 000000000..430ad9465 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/inline-script-two.astro @@ -0,0 +1,8 @@ +--- +import Layout from '../components/Layout.astro'; +import InlineScript from '../components/InlineScript.astro'; +--- + + + Go to 1 + diff --git a/packages/astro/e2e/invalidate-script-deps.test.js b/packages/astro/e2e/hmr.test.js similarity index 57% rename from packages/astro/e2e/invalidate-script-deps.test.js rename to packages/astro/e2e/hmr.test.js index fe37ece8f..091aa716d 100644 --- a/packages/astro/e2e/invalidate-script-deps.test.js +++ b/packages/astro/e2e/hmr.test.js @@ -2,7 +2,7 @@ import { expect } from '@playwright/test'; import { testFactory } from './test-utils.js'; const test = testFactory({ - root: './fixtures/invalidate-script-deps/', + root: './fixtures/hmr/', }); let devServer; @@ -17,7 +17,7 @@ test.afterAll(async () => { test.describe('Scripts with dependencies', () => { test('refresh with HMR', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/')); + await page.goto(astro.resolveUrl('/script-dep')); const h = page.locator('h1'); await expect(h, 'original text set').toHaveText('before'); @@ -29,3 +29,16 @@ test.describe('Scripts with dependencies', () => { await expect(h, 'text changed').toHaveText('after'); }); }); + +test.describe('Styles with dependencies', () => { + test('refresh with HMR', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/css-dep')); + + const h = page.locator('h1'); + await expect(h).toHaveCSS('color', 'rgb(0, 0, 255)'); + + await astro.editFile('./src/styles/vars.scss', (original) => original.replace('blue', 'red')); + + await expect(h).toHaveCSS('color', 'rgb(255, 0, 0)'); + }); +}); diff --git a/packages/astro/e2e/test-utils.js b/packages/astro/e2e/test-utils.js index 0768bff81..79b7601b7 100644 --- a/packages/astro/e2e/test-utils.js +++ b/packages/astro/e2e/test-utils.js @@ -71,13 +71,6 @@ export async function getErrorOverlayContent(page) { return { message, hint, absoluteFileLocation, fileLocation }; } -/** - * @returns {Promise} - */ -export async function getColor(el) { - return await el.evaluate((e) => getComputedStyle(e).color); -} - /** * Wait for `astro-island` that contains the `el` to hydrate * @param {import('@playwright/test').Page} page diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 868660f9f..b06d5a988 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -293,12 +293,12 @@ test.describe('View Transitions', () => { locator = page.locator('#click-one-again'); await expect(locator).toBeInViewport(); - // Scroll up to top fragment + // goto page 1 await page.click('#click-one-again'); locator = page.locator('#one'); await expect(locator).toHaveText('Page 1'); - // Back to middle of the page + // Back to middle of the previous page await page.goBack(); locator = page.locator('#click-one-again'); await expect(locator).toBeInViewport(); @@ -520,6 +520,22 @@ test.describe('View Transitions', () => { await downloadPromise; }); + test('data-astro-reload not required for non-html content', async ({ page, astro }) => { + const loads = []; + page.addListener('load', (p) => { + loads.push(p.title()); + }); + // Go to page 4 + await page.goto(astro.resolveUrl('/four')); + let p = page.locator('#four'); + await expect(p, 'should have content').toHaveText('Page 4'); + + await page.click('#click-svg'); + p = page.locator('svg'); + await expect(p).toBeVisible(); + expect(loads.length, 'There should be 2 page load').toEqual(2); + }); + test('Scroll position is restored on back navigation from page w/o ViewTransitions', async ({ page, astro, @@ -663,4 +679,22 @@ test.describe('View Transitions', () => { locator = page.locator('#click-one'); await expect(locator).not.toBeInViewport(); }); + + test('body inline scripts do not re-execute on navigation', async ({ page, astro }) => { + const errors = []; + page.addListener('pageerror', (err) => { + errors.push(err); + }); + + await page.goto(astro.resolveUrl('/inline-script-one')); + let article = page.locator('#counter'); + await expect(article, 'should have script content').toBeVisible('exists'); + + await page.click('#click-one'); + + article = page.locator('#counter'); + await expect(article, 'should have script content').toHaveText('Count: 3'); + + expect(errors).toHaveLength(0); + }); }); diff --git a/packages/astro/package.json b/packages/astro/package.json index f3fa8cf52..667dcf9bb 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "3.1.1", + "version": "3.1.2", "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/src/assets/services/squoosh.ts b/packages/astro/src/assets/services/squoosh.ts index cbee58336..32aee874d 100644 --- a/packages/astro/src/assets/services/squoosh.ts +++ b/packages/astro/src/assets/services/squoosh.ts @@ -29,8 +29,11 @@ const qualityTable: Record< // Squoosh's PNG encoder does not support a quality setting, so we can skip that here }; -async function getRotationForEXIF(inputBuffer: Buffer): Promise { - const meta = await imageMetadata(inputBuffer); +async function getRotationForEXIF( + inputBuffer: Buffer, + src?: string +): Promise { + const meta = await imageMetadata(inputBuffer, src); if (!meta) return undefined; // EXIF orientations are a bit hard to read, but the numbers are actually standard. See https://exiftool.org/TagNames/EXIF.html for a list. @@ -64,7 +67,7 @@ const service: LocalImageService = { const operations: Operation[] = []; - const rotation = await getRotationForEXIF(inputBuffer); + const rotation = await getRotationForEXIF(inputBuffer, transform.src); if (rotation) { operations.push(rotation); diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts index 9b83a020a..b9ca146b7 100644 --- a/packages/astro/src/assets/utils/emitAsset.ts +++ b/packages/astro/src/assets/utils/emitAsset.ts @@ -22,11 +22,7 @@ export async function emitESMImage( return undefined; } - const fileMetadata = await imageMetadata(fileData); - - if (!fileMetadata) { - return undefined; - } + const fileMetadata = await imageMetadata(fileData, id); const emittedImage: ImageMetadata = { src: '', diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts index 7d7ee7457..fc89ca1ca 100644 --- a/packages/astro/src/assets/utils/metadata.ts +++ b/packages/astro/src/assets/utils/metadata.ts @@ -1,19 +1,23 @@ import probe from 'probe-image-size'; +import { AstroError, AstroErrorData } from '../../core/errors/index.js'; import type { ImageInputFormat, ImageMetadata } from '../types.js'; -export async function imageMetadata(data: Buffer): Promise | undefined> { +export async function imageMetadata( + data: Buffer, + src?: string +): Promise> { const result = probe.sync(data); + if (result === null) { - throw new Error('Failed to probe image size.'); + throw new AstroError({ + ...AstroErrorData.NoImageMetadata, + message: AstroErrorData.NoImageMetadata.message(src), + }); } const { width, height, type, orientation } = result; const isPortrait = (orientation || 0) >= 5; - if (!width || !height || !type) { - return undefined; - } - return { width: isPortrait ? height : width, height: isPortrait ? width : height, diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index cea8e068c..2be2f5586 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -2,6 +2,8 @@ import MagicString from 'magic-string'; import type * as vite from 'vite'; import { normalizePath } from 'vite'; import type { AstroPluginOptions, ImageTransform } from '../@types/astro.js'; +import { extendManualChunks } from '../core/build/plugins/util.js'; +import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { appendForwardSlash, joinPaths, @@ -28,6 +30,18 @@ export default function assets({ // Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev { name: 'astro:assets', + outputOptions(outputOptions) { + // Specifically split out chunk for asset files to prevent TLA deadlock + // caused by `getImage()` for markdown components. + // https://github.com/rollup/rollup/issues/4708 + extendManualChunks(outputOptions, { + after(id) { + if (id.includes('astro/dist/assets/services/')) { + return `astro-assets-services`; + } + }, + }); + }, async resolveId(id) { if (id === VIRTUAL_SERVICE_ID) { return await this.resolve(settings.config.image.service.entrypoint); @@ -126,6 +140,14 @@ export default function assets({ } if (assetRegex.test(id)) { const meta = await emitESMImage(id, this.meta.watchMode, this.emitFile); + + if (!meta) { + throw new AstroError({ + ...AstroErrorData.ImageNotFound, + message: AstroErrorData.ImageNotFound.message(id), + }); + } + return `export default ${JSON.stringify(meta)}`; } }, diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index 62cec7a71..afd63716b 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -9,7 +9,12 @@ import ora from 'ora'; import preferredPM from 'preferred-pm'; import prompts from 'prompts'; import type yargs from 'yargs-parser'; -import { loadTSConfig, resolveConfigPath, resolveRoot } from '../../core/config/index.js'; +import { + loadTSConfig, + resolveConfig, + resolveConfigPath, + resolveRoot, +} from '../../core/config/index.js'; import { defaultTSConfig, presets, @@ -23,7 +28,7 @@ import { appendForwardSlash } from '../../core/path.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; import { parseNpmName } from '../../core/util.js'; import { eventCliSession, telemetry } from '../../events/index.js'; -import { createLoggerFromFlags } from '../flags.js'; +import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; import { generate, parse, t, visit } from './babel.js'; import { ensureImport } from './imports.js'; import { wrapDefaultExport } from './wrapper.js'; @@ -87,7 +92,9 @@ async function getRegistry(): Promise { } export async function add(names: string[], { flags }: AddOptions) { - telemetry.record(eventCliSession('add')); + const inlineConfig = flagsToAstroInlineConfig(flags); + const { userConfig } = await resolveConfig(inlineConfig, 'add'); + telemetry.record(eventCliSession('add', userConfig)); applyPolyfill(); if (flags.help || names.length === 0) { printHelp({ diff --git a/packages/astro/src/content/runtime.ts b/packages/astro/src/content/runtime.ts index 7b8654ee7..eeaa60e6c 100644 --- a/packages/astro/src/content/runtime.ts +++ b/packages/astro/src/content/runtime.ts @@ -1,5 +1,6 @@ import type { MarkdownHeading } from '@astrojs/markdown-remark'; import { ZodIssueCode, string as zodString } from 'zod'; +import type { AstroIntegration } from '../@types/astro.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { prependForwardSlash } from '../core/path.js'; import { @@ -55,10 +56,7 @@ export function createGetCollection({ } else if (collection in dataCollectionToEntryMap) { type = 'data'; } else { - throw new AstroError({ - ...AstroErrorData.CollectionDoesNotExistError, - message: AstroErrorData.CollectionDoesNotExistError.message(collection), - }); + return warnOfEmptyCollection(collection); } const lazyImports = Object.values( type === 'content' @@ -392,3 +390,16 @@ type PropagatedAssetsModule = { function isPropagatedAssetsModule(module: any): module is PropagatedAssetsModule { return typeof module === 'object' && module != null && '__astroPropagation' in module; } + +function warnOfEmptyCollection(collection: string): AstroIntegration { + return { + name: 'astro-collection', + hooks: { + 'astro:server:start': ({ logger }) => { + logger.warn( + `The collection **${collection}** does not exist or is empty. Ensure a collection directory with this name exists.` + ); + }, + }, + }; +} diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts index 47ff4b863..22d3f795b 100644 --- a/packages/astro/src/core/build/plugins/plugin-middleware.ts +++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts @@ -25,7 +25,7 @@ export function vitePluginMiddleware( async resolveId(id) { if (id === MIDDLEWARE_MODULE_ID) { const middlewareId = await this.resolve( - `${opts.settings.config.srcDir.pathname}/${MIDDLEWARE_PATH_SEGMENT_NAME}` + `${decodeURI(opts.settings.config.srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}` ); if (middlewareId) { resolvedMiddlewareId = middlewareId.id; diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index b36017c22..4ac40d4c5 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -140,7 +140,6 @@ export const AstroConfigSchema = z.object({ .optional() .default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware), }) - .optional() .default({}), server: z.preprocess( // preprocess @@ -158,7 +157,6 @@ export const AstroConfigSchema = z.object({ port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port), headers: z.custom().optional(), }) - .optional() .default({}) ), redirects: z @@ -274,27 +272,11 @@ export const AstroConfigSchema = z.object({ .optional() .default(ASTRO_CONFIG_DEFAULTS.experimental.optimizeHoistedScript), }) - .passthrough() - .refine( - (d) => { - const validKeys = Object.keys(ASTRO_CONFIG_DEFAULTS.experimental); - const invalidKeys = Object.keys(d).filter((key) => !validKeys.includes(key)); - if (invalidKeys.length > 0) return false; - return true; - }, - (d) => { - const validKeys = Object.keys(ASTRO_CONFIG_DEFAULTS.experimental); - const invalidKeys = Object.keys(d).filter((key) => !validKeys.includes(key)); - return { - message: `Invalid experimental key: \`${invalidKeys.join( - ', ' - )}\`. \nMake sure the spelling is correct, and that your Astro version supports this experiment.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for more information.`, - }; - } + .strict( + `Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.` ) - .optional() .default({}), - legacy: z.object({}).optional().default({}), + legacy: z.object({}).default({}), }); export type AstroConfigType = z.infer; diff --git a/packages/astro/src/core/cookies/index.ts b/packages/astro/src/core/cookies/index.ts index f3c7b6d61..c8869f9ae 100644 --- a/packages/astro/src/core/cookies/index.ts +++ b/packages/astro/src/core/cookies/index.ts @@ -1,2 +1,6 @@ export { AstroCookies } from './cookies.js'; -export { attachCookiesToResponse, getSetCookiesFromResponse } from './response.js'; +export { + attachCookiesToResponse, + getSetCookiesFromResponse, + responseHasCookies, +} from './response.js'; diff --git a/packages/astro/src/core/cookies/response.ts b/packages/astro/src/core/cookies/response.ts index 8dc35e8c7..013f836bf 100644 --- a/packages/astro/src/core/cookies/response.ts +++ b/packages/astro/src/core/cookies/response.ts @@ -6,6 +6,10 @@ export function attachCookiesToResponse(response: Response, cookies: AstroCookie Reflect.set(response, astroCookiesSymbol, cookies); } +export function responseHasCookies(response: Response): boolean { + return Reflect.has(response, astroCookiesSymbol); +} + function getFromResponse(response: Response): AstroCookies | undefined { let cookies = Reflect.get(response, astroCookiesSymbol); if (cookies != null) { diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 1f336e5f8..e4fe35540 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -620,8 +620,42 @@ export const ExpectedImageOptions = { message: (options: string) => `Expected getImage() parameter to be an object. Received \`${options}\`.`, } satisfies ErrorData; + /** * @docs + * @see + * - [Images](https://docs.astro.build/en/guides/images/) + * @description + * Astro could not find an image you imported. Often, this is simply caused by a typo in the path. + * + * Images in Markdown are relative to the current file. To refer to an image that is located in the same folder as the `.md` file, the path should start with `./` + */ +export const ImageNotFound = { + name: 'ImageNotFound', + title: 'Image not found.', + message: (imagePath: string) => `Could not find requested image \`${imagePath}\`. Does it exist?`, + hint: 'This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly.', +} satisfies ErrorData; + +/** + * @docs + * @message Could not process image metadata for `IMAGE_PATH`. + * @see + * - [Images](https://docs.astro.build/en/guides/images/) + * @description + * Astro could not process the metadata of an image you imported. This is often caused by a corrupted or malformed image and re-exporting the image from your image editor may fix this issue. + */ +export const NoImageMetadata = { + name: 'NoImageMetadata', + title: 'Could not process image metadata.', + message: (imagePath: string | undefined) => + `Could not process image metadata${imagePath ? ' for `${imagePath}`' : ''}.`, + hint: 'This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue.', +} satisfies ErrorData; + +/** + * @docs + * @deprecated This error is no longer Markdown specific and as such, as been replaced by `ImageNotFound` * @message * Could not find requested image `IMAGE_PATH` at `FULL_IMAGE_PATH`. * @see @@ -640,6 +674,7 @@ export const MarkdownImageNotFound = { }`, hint: 'This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly.', } satisfies ErrorData; + /** * @docs * @description diff --git a/packages/astro/src/core/middleware/callMiddleware.ts b/packages/astro/src/core/middleware/callMiddleware.ts index 1725fd38d..40513c152 100644 --- a/packages/astro/src/core/middleware/callMiddleware.ts +++ b/packages/astro/src/core/middleware/callMiddleware.ts @@ -5,6 +5,7 @@ import type { MiddlewareHandler, MiddlewareNext, } from '../../@types/astro.js'; +import { attachCookiesToResponse, responseHasCookies } from '../cookies/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; import type { Environment } from '../render/index.js'; @@ -82,7 +83,7 @@ export async function callMiddleware( if (value instanceof Response === false) { throw new AstroError(AstroErrorData.MiddlewareNotAResponse); } - return value as R; + return ensureCookiesAttached(apiContext, value as Response); } else { /** * Here we handle the case where `next` was called and returned nothing. @@ -105,11 +106,18 @@ export async function callMiddleware( throw new AstroError(AstroErrorData.MiddlewareNotAResponse); } else { // Middleware did not call resolve and returned a value - return value as R; + return ensureCookiesAttached(apiContext, value as Response); } }); } +function ensureCookiesAttached(apiContext: APIContext, response: Response): Response { + if (apiContext.cookies !== undefined && !responseHasCookies(response)) { + attachCookiesToResponse(response, apiContext.cookies); + } + return response; +} + function isEndpointOutput(endpointResult: any): endpointResult is EndpointOutput { return ( !(endpointResult instanceof Response) && diff --git a/packages/astro/src/core/middleware/loadMiddleware.ts b/packages/astro/src/core/middleware/loadMiddleware.ts index 9a7f3e4bc..b8528eb4b 100644 --- a/packages/astro/src/core/middleware/loadMiddleware.ts +++ b/packages/astro/src/core/middleware/loadMiddleware.ts @@ -12,7 +12,7 @@ export async function loadMiddleware( srcDir: AstroSettings['config']['srcDir'] ) { // can't use node Node.js builtins - let middlewarePath = srcDir.pathname + '/' + MIDDLEWARE_PATH_SEGMENT_NAME; + let middlewarePath = `${decodeURI(srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}`; try { const module = await moduleLoader.import(middlewarePath); return module; diff --git a/packages/astro/src/events/session.ts b/packages/astro/src/events/session.ts index b37530342..d9c492cb0 100644 --- a/packages/astro/src/events/session.ts +++ b/packages/astro/src/events/session.ts @@ -1,25 +1,8 @@ -import type { AstroUserConfig } from '../@types/astro.js'; +import type { AstroIntegration, AstroUserConfig } from '../@types/astro.js'; +import { AstroConfigSchema } from '../core/config/schema.js'; const EVENT_SESSION = 'ASTRO_CLI_SESSION_STARTED'; -interface ConfigInfo { - markdownPlugins: string[]; - adapter: string | null; - integrations: string[]; - trailingSlash: undefined | 'always' | 'never' | 'ignore'; - build: - | undefined - | { - format: undefined | 'file' | 'directory'; - }; - markdown: - | undefined - | { - drafts: undefined | boolean; - syntaxHighlight: undefined | 'shiki' | 'prism' | false; - }; -} - interface EventPayload { cliCommand: string; config?: ConfigInfo; @@ -28,87 +11,126 @@ interface EventPayload { optionalIntegrations?: number; } -const multiLevelKeys = new Set([ - 'build', - 'markdown', - 'markdown.shikiConfig', - 'server', - 'vite', - 'vite.resolve', - 'vite.css', - 'vite.json', - 'vite.server', - 'vite.server.fs', - 'vite.build', - 'vite.preview', - 'vite.optimizeDeps', - 'vite.ssr', - 'vite.worker', -]); -function configKeys(obj: Record | undefined, parentKey: string): string[] { - if (!obj) { - return []; +type ConfigInfoValue = string | boolean | string[] | undefined; +type ConfigInfoRecord = Record; +type ConfigInfoBase = { + [alias in keyof AstroUserConfig]: ConfigInfoValue | ConfigInfoRecord; +}; +export interface ConfigInfo extends ConfigInfoBase { + build: ConfigInfoRecord; + image: ConfigInfoRecord; + markdown: ConfigInfoRecord; + experimental: ConfigInfoRecord; + legacy: ConfigInfoRecord; + vite: ConfigInfoRecord | undefined; +} + +function measureIsDefined(val: unknown) { + // if val is undefined, measure undefined as a value + if (val === undefined) { + return undefined; } + // otherwise, convert the value to a boolean + return Boolean(val); +} - return Object.entries(obj) - .map(([key, value]) => { - if (typeof value === 'object' && !Array.isArray(value)) { - const localKey = parentKey ? parentKey + '.' + key : key; - if (multiLevelKeys.has(localKey)) { - let keys = configKeys(value, localKey).map((subkey) => key + '.' + subkey); - keys.unshift(key); - return keys; - } - } +type StringLiteral = T extends string ? (string extends T ? never : T) : never; - return key; - }) - .flat(1); +/** + * Measure supports string literal values. Passing a generic `string` type + * results in an error, to make sure generic user input is never measured directly. + */ +function measureStringLiteral( + val: StringLiteral | boolean | undefined +): string | boolean | undefined { + return val; +} + +function measureIntegration(val: AstroIntegration | false | null | undefined): string | undefined { + if (!val || !val.name) { + return undefined; + } + return val.name; +} + +function sanitizeConfigInfo(obj: object | undefined, validKeys: string[]): ConfigInfoRecord { + if (!obj || validKeys.length === 0) { + return {}; + } + return validKeys.reduce( + (result, key) => { + result[key] = measureIsDefined((obj as Record)[key]); + return result; + }, + {} as Record + ); +} + +/** + * This function creates an anonymous ConfigInfo object from the user's config. + * All values are sanitized to preserve anonymity. Simple "exist" boolean checks + * are used by default, with a few additional sanitized values added manually. + * Helper functions should always be used to ensure correct sanitization. + */ +function createAnonymousConfigInfo(userConfig: AstroUserConfig) { + // Sanitize and measure the generic config object + // NOTE(fks): Using _def is the correct, documented way to get the `shape` + // from a Zod object that includes a wrapping default(), optional(), etc. + // Even though `_def` appears private, it is type-checked for us so that + // any changes between versions will be detected. + const configInfo: ConfigInfo = { + ...sanitizeConfigInfo(userConfig, Object.keys(AstroConfigSchema.shape)), + build: sanitizeConfigInfo( + userConfig.build, + Object.keys(AstroConfigSchema.shape.build._def.innerType.shape) + ), + image: sanitizeConfigInfo( + userConfig.image, + Object.keys(AstroConfigSchema.shape.image._def.innerType.shape) + ), + markdown: sanitizeConfigInfo( + userConfig.markdown, + Object.keys(AstroConfigSchema.shape.markdown._def.innerType.shape) + ), + experimental: sanitizeConfigInfo( + userConfig.experimental, + Object.keys(AstroConfigSchema.shape.experimental._def.innerType.shape) + ), + legacy: sanitizeConfigInfo( + userConfig.legacy, + Object.keys(AstroConfigSchema.shape.legacy._def.innerType.shape) + ), + vite: userConfig.vite + ? sanitizeConfigInfo(userConfig.vite, Object.keys(userConfig.vite)) + : undefined, + }; + // Measure string literal/enum configuration values + configInfo.build.format = measureStringLiteral(userConfig.build?.format); + configInfo.markdown.syntaxHighlight = measureStringLiteral(userConfig.markdown?.syntaxHighlight); + configInfo.output = measureStringLiteral(userConfig.output); + configInfo.scopedStyleStrategy = measureStringLiteral(userConfig.scopedStyleStrategy); + configInfo.trailingSlash = measureStringLiteral(userConfig.trailingSlash); + // Measure integration & adapter usage + configInfo.adapter = measureIntegration(userConfig.adapter); + configInfo.integrations = userConfig.integrations + ?.flat(100) + .map(measureIntegration) + .filter(Boolean) as string[]; + // Return the sanitized ConfigInfo object + return configInfo; } export function eventCliSession( cliCommand: string, - userConfig?: AstroUserConfig, + userConfig: AstroUserConfig, flags?: Record ): { eventName: string; payload: EventPayload }[] { - // Filter out falsy integrations - const configValues = userConfig - ? { - markdownPlugins: [ - ...(userConfig?.markdown?.remarkPlugins?.map((p) => - typeof p === 'string' ? p : typeof p - ) ?? []), - ...(userConfig?.markdown?.rehypePlugins?.map((p) => - typeof p === 'string' ? p : typeof p - ) ?? []), - ] as string[], - adapter: userConfig?.adapter?.name ?? null, - integrations: (userConfig?.integrations ?? []) - .filter(Boolean) - .flat() - .map((i: any) => i?.name), - trailingSlash: userConfig?.trailingSlash, - build: userConfig?.build - ? { - format: userConfig?.build?.format, - } - : undefined, - markdown: userConfig?.markdown - ? { - drafts: userConfig.markdown?.drafts, - syntaxHighlight: userConfig.markdown?.syntaxHighlight, - } - : undefined, - } - : undefined; - // Filter out yargs default `_` flag which is the cli command const cliFlags = flags ? Object.keys(flags).filter((name) => name != '_') : undefined; const payload: EventPayload = { cliCommand, - configKeys: userConfig ? configKeys(userConfig, '') : undefined, - config: configValues, + config: createAnonymousConfigInfo(userConfig), flags: cliFlags, }; return [{ eventName: EVENT_SESSION, payload }]; diff --git a/packages/astro/src/runtime/server/scripts.ts b/packages/astro/src/runtime/server/scripts.ts index 791be3201..47cd122f1 100644 --- a/packages/astro/src/runtime/server/scripts.ts +++ b/packages/astro/src/runtime/server/scripts.ts @@ -1,7 +1,7 @@ import type { SSRResult } from '../../@types/astro.js'; import islandScript from './astro-island.prebuilt.js'; -const ISLAND_STYLES = ``; +const ISLAND_STYLES = ``; export function determineIfNeedsHydrationScript(result: SSRResult): boolean { if (result._metadata.hasHydrationScript) { @@ -36,12 +36,12 @@ export function getPrescripts(result: SSRResult, type: PrescriptType, directive: // deps to be loaded immediately. switch (type) { case 'both': - return `${ISLAND_STYLES}`; case 'directive': - return ``; + return ``; } return ''; } diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index 65186af5e..6600b2f42 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -90,7 +90,7 @@ export async function handleHotUpdate( // Bugfix: sometimes style URLs get normalized and end with `lang.css=` // These will cause full reloads, so filter them out here - const mods = ctx.modules.filter((m) => !m.url.endsWith('=')); + const mods = [...filtered].filter((m) => !m.url.endsWith('=')); const file = ctx.file.replace(config.root.pathname, '/'); // If only styles are changed, remove the component file from the update list @@ -109,17 +109,6 @@ export async function handleHotUpdate( } } - // If this is a module that is imported from a