0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

Merge branch 'withastro:main' into main

This commit is contained in:
Saiya 2024-06-28 22:40:21 +08:00 committed by GitHub
commit 3feb8cd977
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
188 changed files with 1519 additions and 2037 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fixes an issue where Astro didn't throw an error when `Astro.rewrite` was used without providing the experimental flag

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Drop duplicated brackets in data collections schema generation.

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fixes a case where rewriting `/` would cause an issue, when `trailingSlash` was set to `"never"`.

View file

@ -1,27 +0,0 @@
---
'astro': minor
---
Adds [`ShikiTransformer`](https://shiki.style/packages/transformers#shikijs-transformers) support to the [`<Code />`](https://docs.astro.build/en/reference/api-reference/#code-) component with a new `transformers` prop.
Note that `transformers` only applies classes and you must provide your own CSS rules to target the elements of your code block.
```astro
---
import { transformerNotationFocus } from '@shikijs/transformers'
import { Code } from 'astro:components'
const code = `const foo = 'hello'
const bar = ' world'
console.log(foo + bar) // [!code focus]
`
---
<Code {code} lang="js" transformers={[transformerNotationFocus()]} />
<style is:global>
pre.has-focused .line:not(.focused) {
filter: blur(1px);
}
</style>
```

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Reverts a logic where it wasn't possible to rewrite `/404` in static mode. It's **now possible** again

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fixes type generation for empty content collections

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Improves type-checking and error handling to catch case where an image import is passed directly to `getImage()`

View file

@ -1,22 +0,0 @@
---
"astro": minor
---
Improves the developer experience of the `500.astro` file by passing it a new `error` prop.
When an error is thrown, the special `src/pages/500.astro` page now automatically receives the error as a prop. This allows you to display more specific information about the error on a custom 500 page.
```astro
---
// src/pages/500.astro
interface Props {
error: unknown
}
const { error } = Astro.props
---
<div>{error instanceof Error ? error.message : 'Unknown error'}</div>
```
If an error occurs rendering this page, your host's default 500 error page will be shown to your visitor in production, and Astro's default error overlay will be shown in development.

View file

@ -1,7 +0,0 @@
---
'astro': patch
---
Corrects an inconsistency in dev where middleware would run for prerendered 404 routes.
Middleware is not run for prerendered 404 routes in production, so this was incorrect.

View file

@ -1,7 +0,0 @@
---
'astro': patch
---
Fixes an issue that would break `Astro.request.url` and `Astro.request.headers` in `astro dev` if HTTP/2 was enabled.
HTTP/2 is now enabled by default in `astro dev` if `https` is configured in the Vite config.

View file

@ -1,5 +1,8 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: 💁 Support
url: https://astro.build/chat
about: 'This issue tracker is not for support questions. Join us on Discord for assistance!'
- name: 📘 Documentation - name: 📘 Documentation
url: https://github.com/withastro/docs url: https://github.com/withastro/docs
about: File an issue or make an improvement to the docs website. about: File an issue or make an improvement to the docs website.
@ -9,6 +12,3 @@ contact_links:
- name: 👾 Chat - name: 👾 Chat
url: https://astro.build/chat url: https://astro.build/chat
about: Our Discord server is active, come join us! about: Our Discord server is active, come join us!
- name: 💁 Support
url: https://astro.build/chat
about: 'This issue tracker is not for support questions. Join us on Discord for assistance!'

View file

@ -15,6 +15,7 @@
// manually bumping deps // manually bumping deps
"@biomejs/biome", "@biomejs/biome",
"@types/node", "@types/node",
"@preact/preset-vite", // v2.8.3 starts to use Vite's esbuild for perf, but this conflicts with the react plugin
"preact-render-to-string", // https://github.com/withastro/astro/pull/10200 "preact-render-to-string", // https://github.com/withastro/astro/pull/10200
"sharp", "sharp",

6
.npmrc
View file

@ -4,11 +4,11 @@ link-workspace-packages=true
save-workspace-protocol=false # This prevents the examples to have the `workspace:` prefix save-workspace-protocol=false # This prevents the examples to have the `workspace:` prefix
auto-install-peers=false auto-install-peers=false
# `github-slugger` is used by `vite-plugin-markdown-legacy`.
# Temporarily hoist this until we remove the feature.
public-hoist-pattern[]=github-slugger
# Vite's esbuild optimizer has trouble optimizing `@astrojs/lit/client-shim.js` # Vite's esbuild optimizer has trouble optimizing `@astrojs/lit/client-shim.js`
# which imports this dependency. # which imports this dependency.
public-hoist-pattern[]=@webcomponents/template-shadowroot public-hoist-pattern[]=@webcomponents/template-shadowroot
# There's a lit dependency duplication somewhere causing multiple Lit versions error. # There's a lit dependency duplication somewhere causing multiple Lit versions error.
public-hoist-pattern[]=*lit* public-hoist-pattern[]=*lit*
# `astro sync` could try to import `@astrojs/db` but could fail due to linked dependencies in the monorepo.
# We hoist it here so that it can easily resolve `@astrojs/db` without hardcoded handling.
public-hoist-pattern[]=@astrojs/db

View file

@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.7.1/schema.json", "$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
"files": { "files": {
"ignore": [ "ignore": [
"vendor", "vendor",
@ -20,7 +20,6 @@
"benchmark/results/", "benchmark/results/",
".changeset", ".changeset",
"pnpm-lock.yaml", "pnpm-lock.yaml",
"package.json",
"*.astro" "*.astro"
] ]
}, },
@ -30,7 +29,7 @@
"linter": { "enabled": false }, "linter": { "enabled": false },
"javascript": { "javascript": {
"formatter": { "formatter": {
"trailingComma": "es5", "trailingCommas": "es5",
"quoteStyle": "single", "quoteStyle": "single",
"semicolons": "always" "semicolons": "always"
} }
@ -44,5 +43,15 @@
"indentStyle": "space", "indentStyle": "space",
"trailingCommas": "none" "trailingCommas": "none"
} }
} },
"overrides": [
{
"include": ["package.json"],
"json": {
"formatter": {
"lineWidth": 1
}
}
}
]
} }

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,9 +11,9 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^3.1.1", "@astrojs/mdx": "^3.1.2",
"@astrojs/rss": "^4.0.6", "@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.1.6",
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -15,7 +15,7 @@
], ],
"scripts": {}, "scripts": {},
"devDependencies": { "devDependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "^4.0.0" "astro": "^4.0.0"

View file

@ -12,7 +12,7 @@
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3", "astro": "^4.11.3",
"@astrojs/react": "^3.6.0", "@astrojs/react": "^3.6.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",

View file

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@astrojs/alpinejs": "^0.4.0", "@astrojs/alpinejs": "^0.4.0",
"@types/alpinejs": "^3.13.10", "@types/alpinejs": "^3.13.10",
"alpinejs": "^3.14.0", "alpinejs": "^3.14.1",
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@astrojs/lit": "^4.3.0", "@astrojs/lit": "^4.3.0",
"@webcomponents/template-shadowroot": "^0.2.1", "@webcomponents/template-shadowroot": "^0.2.1",
"astro": "^4.10.3", "astro": "^4.11.3",
"lit": "^3.1.4" "lit": "^3.1.4"
} }
} }

View file

@ -18,12 +18,12 @@
"@astrojs/vue": "^4.5.0", "@astrojs/vue": "^4.5.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"preact": "^10.22.0", "preact": "^10.22.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@astrojs/preact": "^3.5.0", "@astrojs/preact": "^3.5.0",
"@preact/signals": "^1.2.3", "@preact/signals": "^1.2.3",
"astro": "^4.10.3", "astro": "^4.11.3",
"preact": "^10.22.0" "preact": "^10.22.0"
} }
} }

View file

@ -14,7 +14,7 @@
"@astrojs/react": "^3.6.0", "@astrojs/react": "^3.6.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
} }

View file

@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/solid-js": "^4.4.0", "@astrojs/solid-js": "^4.4.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"solid-js": "^1.8.17" "solid-js": "^1.8.17"
} }
} }

View file

@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/svelte": "^5.6.0", "@astrojs/svelte": "^5.6.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"svelte": "^4.2.18" "svelte": "^4.2.18"
} }
} }

View file

@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/vue": "^4.5.0", "@astrojs/vue": "^4.5.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^8.3.1", "@astrojs/node": "^8.3.2",
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -15,7 +15,7 @@
], ],
"scripts": {}, "scripts": {},
"devDependencies": { "devDependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "^4.0.0" "astro": "^4.0.0"

View file

@ -12,8 +12,8 @@
"server": "node dist/server/entry.mjs" "server": "node dist/server/entry.mjs"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^8.3.1", "@astrojs/node": "^8.3.2",
"astro": "^4.10.3", "astro": "^4.11.3",
"html-minifier": "^4.0.0" "html-minifier": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -12,9 +12,9 @@
"server": "node dist/server/entry.mjs" "server": "node dist/server/entry.mjs"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^8.3.1", "@astrojs/node": "^8.3.2",
"@astrojs/svelte": "^5.6.0", "@astrojs/svelte": "^5.6.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"svelte": "^4.2.18" "svelte": "^4.2.18"
} }
} }

View file

@ -10,8 +10,8 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3", "astro": "^4.11.3",
"sass": "^1.77.5", "sass": "^1.77.6",
"sharp": "^0.33.3" "sharp": "^0.33.3"
} }
} }

View file

@ -15,6 +15,6 @@
"./app": "./dist/app.js" "./app": "./dist/app.js"
}, },
"devDependencies": { "devDependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,7 +11,7 @@
}, },
"devDependencies": { "devDependencies": {
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@astrojs/node": "^8.3.1", "@astrojs/node": "^8.3.2",
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/markdoc": "^0.11.0", "@astrojs/markdoc": "^0.11.1",
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,8 +11,8 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/markdown-remark": "^5.1.0", "@astrojs/markdown-remark": "^5.1.1",
"astro": "^4.10.3", "astro": "^4.11.3",
"hast-util-select": "^6.0.2", "hast-util-select": "^6.0.2",
"rehype-autolink-headings": "^7.1.0", "rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3" "astro": "^4.11.3"
} }
} }

View file

@ -11,9 +11,9 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^3.1.1", "@astrojs/mdx": "^3.1.2",
"@astrojs/preact": "^3.5.0", "@astrojs/preact": "^3.5.0",
"astro": "^4.10.3", "astro": "^4.11.3",
"preact": "^10.22.0" "preact": "^10.22.0"
} }
} }

View file

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@astrojs/preact": "^3.5.0", "@astrojs/preact": "^3.5.0",
"@nanostores/preact": "^0.5.1", "@nanostores/preact": "^0.5.1",
"astro": "^4.10.3", "astro": "^4.11.3",
"nanostores": "^0.10.3", "nanostores": "^0.10.3",
"preact": "^10.22.0" "preact": "^10.22.0"
} }

View file

@ -11,10 +11,10 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^3.1.1", "@astrojs/mdx": "^3.1.2",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@types/canvas-confetti": "^1.6.4", "@types/canvas-confetti": "^1.6.4",
"astro": "^4.10.3", "astro": "^4.11.3",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"canvas-confetti": "^1.9.3", "canvas-confetti": "^1.9.3",
"postcss": "^8.4.38", "postcss": "^8.4.38",

View file

@ -12,7 +12,7 @@
"test": "vitest" "test": "vitest"
}, },
"dependencies": { "dependencies": {
"astro": "^4.10.3", "astro": "^4.11.3",
"vitest": "^1.6.0" "vitest": "^1.6.0"
} }
} }

View file

@ -16,10 +16,10 @@
"dev": "turbo run dev --concurrency=40 --parallel --filter=astro --filter=create-astro --filter=\"@astrojs/*\" --filter=\"@benchmark/*\"", "dev": "turbo run dev --concurrency=40 --parallel --filter=astro --filter=create-astro --filter=\"@astrojs/*\" --filter=\"@benchmark/*\"",
"format": "pnpm run format:code && pnpm run format:imports", "format": "pnpm run format:code && pnpm run format:imports",
"format:ci": "pnpm run format:code:ci && pnpm run format:imports:ci", "format:ci": "pnpm run format:code:ci && pnpm run format:imports:ci",
"format:code": "biome format ./ --write && prettier -w \"**/*\" --ignore-unknown --cache", "format:code": "biome format --write && prettier -w \"**/*\" --ignore-unknown --cache",
"format:code:ci": "biome format ./ && prettier -w \"**/*\" --ignore-unknown --cache --check", "format:code:ci": "biome format && prettier -w \"**/*\" --ignore-unknown --cache --check",
"format:imports": "biome check --apply .", "format:imports": "biome check --formatter-enabled=false --write",
"format:imports:ci": "biome ci .", "format:imports:ci": "biome ci --formatter-enabled=false",
"test": "turbo run test --concurrency=1 --filter=astro --filter=create-astro --filter=\"@astrojs/*\"", "test": "turbo run test --concurrency=1 --filter=astro --filter=create-astro --filter=\"@astrojs/*\"",
"test:citgm": "pnpm -r --filter=astro test", "test:citgm": "pnpm -r --filter=astro test",
"test:match": "cd packages/astro && pnpm run test:match", "test:match": "cd packages/astro && pnpm run test:match",
@ -52,9 +52,9 @@
}, },
"devDependencies": { "devDependencies": {
"@astrojs/check": "^0.7.0", "@astrojs/check": "^0.7.0",
"@biomejs/biome": "1.7.1", "@biomejs/biome": "1.8.1",
"@changesets/changelog-github": "^0.5.0", "@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.5", "@changesets/cli": "^2.27.6",
"@eslint/eslintrc": "^3.1.0", "@eslint/eslintrc": "^3.1.0",
"@types/node": "^18.17.8", "@types/node": "^18.17.8",
"esbuild": "^0.21.5", "esbuild": "^0.21.5",
@ -68,8 +68,8 @@
"prettier-plugin-astro": "^0.14.0", "prettier-plugin-astro": "^0.14.0",
"tiny-glob": "^0.2.9", "tiny-glob": "^0.2.9",
"turbo": "^1.13.4", "turbo": "^1.13.4",
"typescript": "~5.4.5", "typescript": "~5.5.2",
"typescript-eslint": "^7.13.0" "typescript-eslint": "^7.13.1"
}, },
"pnpm": { "pnpm": {
"packageExtensions": { "packageExtensions": {

View file

@ -1,5 +1,11 @@
# @astrojs/rss # @astrojs/rss
## 4.0.7
### Patch Changes
- [#11299](https://github.com/withastro/astro/pull/11299) [`8ce66f2`](https://github.com/withastro/astro/commit/8ce66f2ef7328546d823f1076f9bab4217a6be7d) Thanks [@ematipico](https://github.com/ematipico)! - Fixes an issue where the `pagesGlobToRssItems` returned an incorrect type for `items`
## 4.0.6 ## 4.0.6
### Patch Changes ### Patch Changes

View file

@ -1,7 +1,7 @@
{ {
"name": "@astrojs/rss", "name": "@astrojs/rss",
"description": "Add RSS feeds to your Astro projects", "description": "Add RSS feeds to your Astro projects",
"version": "4.0.6", "version": "4.0.7",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"author": "withastro", "author": "withastro",

View file

@ -32,13 +32,13 @@ export type RSSOptions = {
export type RSSFeedItem = { export type RSSFeedItem = {
/** Link to item */ /** Link to item */
link: z.infer<typeof rssSchema>['link']; link?: z.infer<typeof rssSchema>['link'];
/** Full content of the item. Should be valid HTML */ /** Full content of the item. Should be valid HTML */
content?: z.infer<typeof rssSchema>['content']; content?: z.infer<typeof rssSchema>['content'];
/** Title of item */ /** Title of item */
title: z.infer<typeof rssSchema>['title']; title?: z.infer<typeof rssSchema>['title'];
/** Publication date of item */ /** Publication date of item */
pubDate: z.infer<typeof rssSchema>['pubDate']; pubDate?: z.infer<typeof rssSchema>['pubDate'];
/** Item description */ /** Item description */
description?: z.infer<typeof rssSchema>['description']; description?: z.infer<typeof rssSchema>['description'];
/** Append some other XML-valid data to this item */ /** Append some other XML-valid data to this item */

View file

@ -1,5 +1,111 @@
# astro # astro
## 4.11.3
### Patch Changes
- [#11347](https://github.com/withastro/astro/pull/11347) [`33bdc54`](https://github.com/withastro/astro/commit/33bdc5472929f72fa8e39624598bf929c48e60c0) Thanks [@bluwy](https://github.com/bluwy)! - Fixes installed packages detection when running `astro check`
- [#11327](https://github.com/withastro/astro/pull/11327) [`0df8142`](https://github.com/withastro/astro/commit/0df81422a81c8f8900684d100e9b8f26365fa0b1) Thanks [@ematipico](https://github.com/ematipico)! - Fixes an issue with the container APIs where a runtime error was thrown during the build, when using `pnpm` as package manager.
## 4.11.2
### Patch Changes
- [#11335](https://github.com/withastro/astro/pull/11335) [`4c4741b`](https://github.com/withastro/astro/commit/4c4741b42dc531403f7b9647bd51951d0cdb8f5b) Thanks [@ematipico](https://github.com/ematipico)! - Reverts [#11292](https://github.com/withastro/astro/pull/11292), which caused a regression to the input type
- [#11326](https://github.com/withastro/astro/pull/11326) [`41121fb`](https://github.com/withastro/astro/commit/41121fbe00e144d4d93835811e1c4349664d9003) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Fixes a case where running `astro sync` when using the experimental `astro:env` feature would fail if environment variables were missing
- [#11338](https://github.com/withastro/astro/pull/11338) [`9752a0b`](https://github.com/withastro/astro/commit/9752a0b27526270fd0066f3db7049e9ae6af1ef8) Thanks [@zaaakher](https://github.com/zaaakher)! - Fixes svg icon margin in devtool tooltip title to look coherent in `rtl` and `ltr` layouts
- [#11331](https://github.com/withastro/astro/pull/11331) [`f1b78a4`](https://github.com/withastro/astro/commit/f1b78a496034d53b0e9dfc276a4a1b1d691772c4) Thanks [@bluwy](https://github.com/bluwy)! - Removes `resolve` package and simplify internal resolve check
- [#11339](https://github.com/withastro/astro/pull/11339) [`8fdbf0e`](https://github.com/withastro/astro/commit/8fdbf0e45beffdae3da1e7f36797575c92f8a0ba) Thanks [@matthewp](https://github.com/matthewp)! - Remove non-fatal errors from telemetry
Previously we tracked non-fatal errors in telemetry to get a good idea of the types of errors that occur in `astro dev`. However this has become noisy over time and results in a lot of data that isn't particularly useful. This removes those non-fatal errors from being tracked.
## 4.11.1
### Patch Changes
- [#11308](https://github.com/withastro/astro/pull/11308) [`44c61dd`](https://github.com/withastro/astro/commit/44c61ddfd85f1c23f8cec8caeaa5e25897121996) Thanks [@ematipico](https://github.com/ematipico)! - Fixes an issue where custom `404.astro` and `500.astro` were not returning the correct status code when rendered inside a rewriting cycle.
- [#11302](https://github.com/withastro/astro/pull/11302) [`0622567`](https://github.com/withastro/astro/commit/06225673269201044358788f2a81dbe13912adce) Thanks [@martrapp](https://github.com/martrapp)! - Fixes an issue with the view transition router when redirecting to an URL with different origin.
- Updated dependencies [[`b6afe6a`](https://github.com/withastro/astro/commit/b6afe6a782f68f4a279463a144baaf99cb96b6dc), [`41064ce`](https://github.com/withastro/astro/commit/41064cee78c1cccd428f710a24c483aeb275fd95)]:
- @astrojs/markdown-remark@5.1.1
- @astrojs/internal-helpers@0.4.1
## 4.11.0
### Minor Changes
- [#11197](https://github.com/withastro/astro/pull/11197) [`4b46bd9`](https://github.com/withastro/astro/commit/4b46bd9bdcbb302f294aa27b8aa07099e104fa17) Thanks [@braebo](https://github.com/braebo)! - Adds [`ShikiTransformer`](https://shiki.style/packages/transformers#shikijs-transformers) support to the [`<Code />`](https://docs.astro.build/en/reference/api-reference/#code-) component with a new `transformers` prop.
Note that `transformers` only applies classes and you must provide your own CSS rules to target the elements of your code block.
```astro
---
import { transformerNotationFocus } from '@shikijs/transformers';
import { Code } from 'astro:components';
const code = `const foo = 'hello'
const bar = ' world'
console.log(foo + bar) // [!code focus]
`;
---
<Code {code} lang="js" transformers={[transformerNotationFocus()]} />
<style is:global>
pre.has-focused .line:not(.focused) {
filter: blur(1px);
}
</style>
```
- [#11134](https://github.com/withastro/astro/pull/11134) [`9042be0`](https://github.com/withastro/astro/commit/9042be049157ce859355f911565bc0c3d68f0aa1) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Improves the developer experience of the `500.astro` file by passing it a new `error` prop.
When an error is thrown, the special `src/pages/500.astro` page now automatically receives the error as a prop. This allows you to display more specific information about the error on a custom 500 page.
```astro
---
// src/pages/500.astro
interface Props {
error: unknown;
}
const { error } = Astro.props;
---
<div>{error instanceof Error ? error.message : 'Unknown error'}</div>
```
If an error occurs rendering this page, your host's default 500 error page will be shown to your visitor in production, and Astro's default error overlay will be shown in development.
### Patch Changes
- [#11280](https://github.com/withastro/astro/pull/11280) [`fd3645f`](https://github.com/withastro/astro/commit/fd3645fe8364ec5e280b6802d1468867890d463c) Thanks [@ascorbic](https://github.com/ascorbic)! - Fixes a bug that prevented cookies from being set when using experimental rewrites
- [#11275](https://github.com/withastro/astro/pull/11275) [`bab700d`](https://github.com/withastro/astro/commit/bab700d69085b1de8f03fc1b0b31651f709cbfe3) Thanks [@syhily](https://github.com/syhily)! - Drop duplicated brackets in data collections schema generation.
- [#11272](https://github.com/withastro/astro/pull/11272) [`ea987d7`](https://github.com/withastro/astro/commit/ea987d7da589ead9aa4b550f167f5e2f6c939d2e) Thanks [@ematipico](https://github.com/ematipico)! - Fixes a case where rewriting `/` would cause an issue, when `trailingSlash` was set to `"never"`.
- [#11272](https://github.com/withastro/astro/pull/11272) [`ea987d7`](https://github.com/withastro/astro/commit/ea987d7da589ead9aa4b550f167f5e2f6c939d2e) Thanks [@ematipico](https://github.com/ematipico)! - Reverts a logic where it wasn't possible to rewrite `/404` in static mode. It's **now possible** again
- [#11264](https://github.com/withastro/astro/pull/11264) [`5a9c9a6`](https://github.com/withastro/astro/commit/5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9) Thanks [@Fryuni](https://github.com/Fryuni)! - Fixes type generation for empty content collections
- [#11279](https://github.com/withastro/astro/pull/11279) [`9a08d74`](https://github.com/withastro/astro/commit/9a08d74bc00ae2c3bc254f99580a22ce4df1d002) Thanks [@ascorbic](https://github.com/ascorbic)! - Improves type-checking and error handling to catch case where an image import is passed directly to `getImage()`
- [#11292](https://github.com/withastro/astro/pull/11292) [`7f8f347`](https://github.com/withastro/astro/commit/7f8f34799528ed0b2011e1ea273bd0636f6e767d) Thanks [@jdtjenkins](https://github.com/jdtjenkins)! - Fixes a case where `defineAction` autocomplete for the `accept` prop would not show `"form"` as a possible value
- [#11273](https://github.com/withastro/astro/pull/11273) [`cb4d078`](https://github.com/withastro/astro/commit/cb4d07819f0dbdfd94bc4f084edf7720ada01323) Thanks [@ascorbic](https://github.com/ascorbic)! - Corrects an inconsistency in dev where middleware would run for prerendered 404 routes.
Middleware is not run for prerendered 404 routes in production, so this was incorrect.
- [#11284](https://github.com/withastro/astro/pull/11284) [`f4b029b`](https://github.com/withastro/astro/commit/f4b029b08264268c68fc81ea25b264e81f47e683) Thanks [@ascorbic](https://github.com/ascorbic)! - Fixes an issue that would break `Astro.request.url` and `Astro.request.headers` in `astro dev` if HTTP/2 was enabled.
HTTP/2 is now enabled by default in `astro dev` if `https` is configured in the Vite config.
## 4.10.3 ## 4.10.3
### Patch Changes ### Patch Changes

View file

@ -19,6 +19,6 @@
"astro": "workspace:*", "astro": "workspace:*",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"typescript": "^5.4.5" "typescript": "^5.5.2"
} }
} }

View file

@ -19,7 +19,7 @@
"astro": "workspace:*", "astro": "workspace:*",
"react": "19.0.0-rc-fb9a90fa48-20240614", "react": "19.0.0-rc-fb9a90fa48-20240614",
"react-dom": "19.0.0-rc-fb9a90fa48-20240614", "react-dom": "19.0.0-rc-fb9a90fa48-20240614",
"typescript": "^5.4.5" "typescript": "^5.5.2"
}, },
"overrides": { "overrides": {
"@types/react": "npm:types-react", "@types/react": "npm:types-react",

View file

@ -5,6 +5,6 @@
"dependencies": { "dependencies": {
"@astrojs/vue": "workspace:*", "@astrojs/vue": "workspace:*",
"astro": "workspace:*", "astro": "workspace:*",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -4,6 +4,6 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"astro": "workspace:*", "astro": "workspace:*",
"sass": "^1.77.5" "sass": "^1.77.6"
} }
} }

View file

@ -12,9 +12,9 @@
"preact": "^10.22.0", "preact": "^10.22.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"sass": "^1.77.5", "sass": "^1.77.6",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -4,6 +4,6 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"astro": "workspace:*", "astro": "workspace:*",
"sass": "^1.77.5" "sass": "^1.77.6"
} }
} }

View file

@ -19,6 +19,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,6 +16,6 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -16,7 +16,7 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"solid-js": "^1.8.17", "solid-js": "^1.8.17",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
}, },
"scripts": { "scripts": {
"dev": "astro dev" "dev": "astro dev"

View file

@ -11,6 +11,6 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -11,6 +11,7 @@ import Layout from '../components/Layout.astro';
<a id="click-self" href="">go to top</a> <a id="click-self" href="">go to top</a>
<a id="click-redirect-two" href="/redirect-two">go to redirect 2</a> <a id="click-redirect-two" href="/redirect-two">go to redirect 2</a>
<a id="click-redirect-external" href="/redirect-external">go to a redirect external</a> <a id="click-redirect-external" href="/redirect-external">go to a redirect external</a>
<a id="click-redirect" href="/redirect">redirect cross-origin</a>
<a id="click-404" href="/undefined-page">go to undefined page</a> <a id="click-404" href="/undefined-page">go to undefined page</a>
<custom-a id="custom-click-two"> <custom-a id="custom-click-two">
<template shadowrootmode="open"> <template shadowrootmode="open">

View file

@ -0,0 +1,10 @@
---
const myURL = Astro.request.url;
const redirectTo = (myURL.startsWith("http://localhost")
? myURL.replace("http://localhost","http://127.0.0.1")
: myURL.replace("http://127.0.0.1", "http://localhost"))
.replace("redirect","two");
return Astro.redirect(redirectTo);
---
<h1>Should not see this</h1>

View file

@ -6,6 +6,6 @@
"@astrojs/mdx": "workspace:*", "@astrojs/mdx": "workspace:*",
"@astrojs/vue": "workspace:*", "@astrojs/vue": "workspace:*",
"astro": "workspace:*", "astro": "workspace:*",
"vue": "^3.4.29" "vue": "^3.4.30"
} }
} }

View file

@ -788,6 +788,25 @@ test.describe('View Transitions', () => {
expect(loads.length, 'There should be 2 page loads').toEqual(2); expect(loads.length, 'There should be 2 page loads').toEqual(2);
}); });
test('Cross origin redirects do not raise errors', async ({ page, astro }) => {
let consoleErrors = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
// Go to page 1
await page.goto(astro.resolveUrl('/one'));
let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
await page.click('#click-redirect');
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
expect(consoleErrors.length, 'There should be no errors').toEqual(0);
});
test('client:only styles are retained on transition (1/2)', async ({ page, astro }) => { test('client:only styles are retained on transition (1/2)', async ({ page, astro }) => {
const totalExpectedStyles = 9; const totalExpectedStyles = 9;

View file

@ -1,6 +1,6 @@
{ {
"name": "astro", "name": "astro",
"version": "4.10.3", "version": "4.11.3",
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
"type": "module", "type": "module",
"author": "withastro", "author": "withastro",
@ -123,7 +123,7 @@
"test:node": "astro-scripts test \"test/**/*.test.js\"" "test:node": "astro-scripts test \"test/**/*.test.js\""
}, },
"dependencies": { "dependencies": {
"@astrojs/compiler": "^2.8.0", "@astrojs/compiler": "^2.8.1",
"@astrojs/internal-helpers": "workspace:*", "@astrojs/internal-helpers": "workspace:*",
"@astrojs/markdown-remark": "workspace:*", "@astrojs/markdown-remark": "workspace:*",
"@astrojs/telemetry": "workspace:*", "@astrojs/telemetry": "workspace:*",
@ -151,7 +151,7 @@
"diff": "^5.2.0", "diff": "^5.2.0",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"dset": "^3.1.3", "dset": "^3.1.3",
"es-module-lexer": "^1.5.3", "es-module-lexer": "^1.5.4",
"esbuild": "^0.21.5", "esbuild": "^0.21.5",
"estree-walker": "^3.0.3", "estree-walker": "^3.0.3",
"execa": "^8.0.1", "execa": "^8.0.1",
@ -172,9 +172,8 @@
"preferred-pm": "^3.1.3", "preferred-pm": "^3.1.3",
"prompts": "^2.4.2", "prompts": "^2.4.2",
"rehype": "^13.0.1", "rehype": "^13.0.1",
"resolve": "^1.22.8",
"semver": "^7.6.2", "semver": "^7.6.2",
"shiki": "^1.6.5", "shiki": "^1.9.0",
"string-width": "^7.1.0", "string-width": "^7.1.0",
"strip-ansi": "^7.1.0", "strip-ansi": "^7.1.0",
"tsconfck": "^3.1.0", "tsconfck": "^3.1.0",
@ -185,7 +184,7 @@
"which-pm": "^2.2.0", "which-pm": "^2.2.0",
"yargs-parser": "^21.1.1", "yargs-parser": "^21.1.1",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-to-json-schema": "^3.23.0" "zod-to-json-schema": "^3.23.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"sharp": "^0.33.3" "sharp": "^0.33.3"
@ -209,7 +208,6 @@
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/probe-image-size": "^7.2.4", "@types/probe-image-size": "^7.2.4",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"@types/resolve": "^1.20.6",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/send": "^0.17.4", "@types/send": "^0.17.4",
"@types/unist": "^3.0.2", "@types/unist": "^3.0.2",
@ -220,17 +218,17 @@
"mdast-util-mdx": "^3.0.0", "mdast-util-mdx": "^3.0.0",
"mdast-util-mdx-jsx": "^3.1.2", "mdast-util-mdx-jsx": "^3.1.2",
"memfs": "^4.9.3", "memfs": "^4.9.3",
"node-mocks-http": "^1.14.1", "node-mocks-http": "^1.15.0",
"parse-srcset": "^1.0.2", "parse-srcset": "^1.0.2",
"rehype-autolink-headings": "^7.1.0", "rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"rehype-toc": "^3.0.2", "rehype-toc": "^3.0.2",
"remark-code-titles": "^0.1.2", "remark-code-titles": "^0.1.2",
"rollup": "^4.18.0", "rollup": "^4.18.0",
"sass": "^1.77.5", "sass": "^1.77.6",
"srcset-parse": "^1.1.0", "srcset-parse": "^1.1.0",
"undici": "^6.19.2", "undici": "^6.19.2",
"unified": "^11.0.4" "unified": "^11.0.5"
}, },
"engines": { "engines": {
"node": "^18.17.1 || ^20.3.0 || >=21.0.0", "node": "^18.17.1 || ^20.3.0 || >=21.0.0",

View file

@ -750,8 +750,13 @@ export interface AstroUserConfig {
* @type {boolean} * @type {boolean}
* @default `true` * @default `true`
* @description * @description
* This is an option to minify your HTML output and reduce the size of your HTML files. By default, Astro removes all whitespace from your HTML, including line breaks, from `.astro` components. This occurs both in development mode and in the final build. *
* To disable HTML compression, set the `compressHTML` flag to `false`. * This is an option to minify your HTML output and reduce the size of your HTML files.
*
* By default, Astro removes whitespace from your HTML, including line breaks, from `.astro` components in a lossless manner.
* Some whitespace may be kept as needed to preserve the visual rendering of your HTML. This occurs both in development mode and in the final build.
*
* To disable HTML compression, set `compressHTML` to false.
* *
* ```js * ```js
* { * {

View file

@ -1,5 +1,4 @@
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
import boxen from 'boxen'; import boxen from 'boxen';
import ci from 'ci-info'; import ci from 'ci-info';
import { execa } from 'execa'; import { execa } from 'execa';
@ -7,9 +6,9 @@ import { bold, cyan, dim, magenta } from 'kleur/colors';
import ora from 'ora'; import ora from 'ora';
import preferredPM from 'preferred-pm'; import preferredPM from 'preferred-pm';
import prompts from 'prompts'; import prompts from 'prompts';
import resolvePackage from 'resolve';
import whichPm from 'which-pm'; import whichPm from 'which-pm';
import { type Logger } from '../core/logger/core.js'; import type { Logger } from '../core/logger/core.js';
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
type GetPackageOptions = { type GetPackageOptions = {
@ -25,17 +24,9 @@ export async function getPackage<T>(
otherDeps: string[] = [] otherDeps: string[] = []
): Promise<T | undefined> { ): Promise<T | undefined> {
try { try {
// Custom resolution logic for @astrojs/db. Since it lives in our monorepo, // Try to resolve with `createRequire` first to prevent ESM caching of the package
// the generic tryResolve() method doesn't work. // if it errors and fails here
if (packageName === '@astrojs/db') { require.resolve(packageName, { paths: [options.cwd ?? process.cwd()] });
const packageJsonLoc = require.resolve(packageName + '/package.json', {
paths: [options.cwd ?? process.cwd()],
});
const packageLoc = pathToFileURL(packageJsonLoc.replace(`package.json`, 'dist/index.js'));
const packageImport = await import(packageLoc.toString());
return packageImport as T;
}
await tryResolve(packageName, options.cwd ?? process.cwd());
const packageImport = await import(packageName); const packageImport = await import(packageName);
return packageImport as T; return packageImport as T;
} catch (e) { } catch (e) {
@ -65,24 +56,6 @@ export async function getPackage<T>(
} }
} }
function tryResolve(packageName: string, cwd: string) {
return new Promise((resolve, reject) => {
resolvePackage(
packageName,
{
basedir: cwd,
},
(err) => {
if (err) {
reject(err);
} else {
resolve(0);
}
}
);
});
}
function getInstallCommand(packages: string[], packageManager: string) { function getInstallCommand(packages: string[], packageManager: string) {
switch (packageManager) { switch (packageManager) {
case 'npm': case 'npm':

View file

@ -48,7 +48,7 @@ export function getViteConfig(
astroContentListenPlugin({ settings, logger, fs }), astroContentListenPlugin({ settings, logger, fs }),
], ],
}, },
{ settings, logger, mode } { settings, logger, mode, sync: false }
); );
await runHookConfigDone({ settings, logger }); await runHookConfigDone({ settings, logger });
return mergeConfig(viteConfig, userViteConfig); return mergeConfig(viteConfig, userViteConfig);

View file

@ -14,8 +14,8 @@ import type {
SSRManifest, SSRManifest,
SSRResult, SSRResult,
} from '../@types/astro.js'; } from '../@types/astro.js';
import { validateConfig } from '../core/config/config.js';
import { ASTRO_CONFIG_DEFAULTS } from '../core/config/schema.js'; import { ASTRO_CONFIG_DEFAULTS } from '../core/config/schema.js';
import { validateConfig } from '../core/config/validate.js';
import { Logger } from '../core/logger/core.js'; import { Logger } from '../core/logger/core.js';
import { nodeLogDestination } from '../core/logger/node.js'; import { nodeLogDestination } from '../core/logger/node.js';
import { removeLeadingForwardSlash } from '../core/path.js'; import { removeLeadingForwardSlash } from '../core/path.js';
@ -208,7 +208,7 @@ type AstroContainerConstructor = {
renderers?: SSRLoadedRenderer[]; renderers?: SSRLoadedRenderer[];
manifest?: AstroContainerManifest; manifest?: AstroContainerManifest;
resolve?: SSRResult['resolve']; resolve?: SSRResult['resolve'];
astroConfig: AstroConfig; astroConfig?: AstroConfig;
}; };
export class experimental_AstroContainer { export class experimental_AstroContainer {
@ -253,10 +253,10 @@ export class experimental_AstroContainer {
}); });
} }
async #containerResolve(specifier: string, astroConfig: AstroConfig): Promise<string> { async #containerResolve(specifier: string, astroConfig?: AstroConfig): Promise<string> {
const found = this.#pipeline.manifest.entryModules[specifier]; const found = this.#pipeline.manifest.entryModules[specifier];
if (found) { if (found) {
return new URL(found, astroConfig.build.client).toString(); return new URL(found, astroConfig?.build.client).toString();
} }
return found; return found;
} }

View file

@ -7,13 +7,10 @@ import type {
} from '../@types/astro.js'; } from '../@types/astro.js';
import { type HeadElements, Pipeline } from '../core/base-pipeline.js'; import { type HeadElements, Pipeline } from '../core/base-pipeline.js';
import type { SinglePageBuiltModule } from '../core/build/types.js'; import type { SinglePageBuiltModule } from '../core/build/types.js';
import { RouteNotFound } from '../core/errors/errors-data.js';
import { AstroError } from '../core/errors/index.js';
import { import {
createModuleScriptElement, createModuleScriptElement,
createStylesheetElementSet, createStylesheetElementSet,
} from '../core/render/ssr-element.js'; } from '../core/render/ssr-element.js';
import { DEFAULT_404_ROUTE } from '../core/routing/astro-designed-error-pages.js';
import { findRouteToRewrite } from '../core/routing/rewrite.js'; import { findRouteToRewrite } from '../core/routing/rewrite.js';
export class ContainerPipeline extends Pipeline { export class ContainerPipeline extends Pipeline {

View file

@ -3,12 +3,13 @@ import { extname } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url'; import { fileURLToPath, pathToFileURL } from 'node:url';
import glob from 'fast-glob'; import glob from 'fast-glob';
import pLimit from 'p-limit'; import pLimit from 'p-limit';
import { type Plugin } from 'vite'; import type { Plugin } from 'vite';
import type { AstroSettings } from '../@types/astro.js'; import type { AstroSettings } from '../@types/astro.js';
import { encodeName } from '../core/build/util.js'; import { encodeName } from '../core/build/util.js';
import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { appendForwardSlash, removeFileExtension } from '../core/path.js'; import { appendForwardSlash, removeFileExtension } from '../core/path.js';
import { isServerLikeOutput, rootRelativePath } from '../core/util.js'; import { isServerLikeOutput } from '../core/util.js';
import { rootRelativePath } from '../core/viteUtils.js';
import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js'; import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js';
import { import {
CONTENT_FLAG, CONTENT_FLAG,

View file

@ -139,7 +139,7 @@ class AstroBuilder {
middlewareMode: true, middlewareMode: true,
}, },
}, },
{ settings: this.settings, logger: this.logger, mode: 'build', command: 'build' } { settings: this.settings, logger: this.logger, mode: 'build', command: 'build', sync: false }
); );
await runHookConfigDone({ settings: this.settings, logger: logger }); await runHookConfigDone({ settings: this.settings, logger: logger });

View file

@ -32,7 +32,7 @@ export function vitePluginAnalyzer(
): VitePlugin { ): VitePlugin {
function hoistedScriptScanner() { function hoistedScriptScanner() {
const uniqueHoistedIds = new Map<string, string>(); const uniqueHoistedIds = new Map<string, string>();
const pageScripts = new Map< const pageScriptsMap = new Map<
string, string,
{ {
hoistedSet: Set<string>; hoistedSet: Set<string>;
@ -54,20 +54,22 @@ export function vitePluginAnalyzer(
if (hoistedScripts.size) { if (hoistedScripts.size) {
for (const parentInfo of getParentModuleInfos(from, this, isPropagatedAsset)) { for (const parentInfo of getParentModuleInfos(from, this, isPropagatedAsset)) {
if (isPropagatedAsset(parentInfo.id)) { if (isPropagatedAsset(parentInfo.id)) {
if (!internals.propagatedScriptsMap.has(parentInfo.id)) {
internals.propagatedScriptsMap.set(parentInfo.id, new Set());
}
const propagatedScripts = internals.propagatedScriptsMap.get(parentInfo.id)!;
for (const hid of hoistedScripts) { for (const hid of hoistedScripts) {
if (!internals.propagatedScriptsMap.has(parentInfo.id)) { propagatedScripts.add(hid);
internals.propagatedScriptsMap.set(parentInfo.id, new Set());
}
internals.propagatedScriptsMap.get(parentInfo.id)?.add(hid);
} }
} else if (moduleIsTopLevelPage(parentInfo)) { } else if (moduleIsTopLevelPage(parentInfo)) {
if (!pageScriptsMap.has(parentInfo.id)) {
pageScriptsMap.set(parentInfo.id, {
hoistedSet: new Set(),
});
}
const pageScripts = pageScriptsMap.get(parentInfo.id)!;
for (const hid of hoistedScripts) { for (const hid of hoistedScripts) {
if (!pageScripts.has(parentInfo.id)) { pageScripts.hoistedSet.add(hid);
pageScripts.set(parentInfo.id, {
hoistedSet: new Set(),
});
}
pageScripts.get(parentInfo.id)?.hoistedSet.add(hid);
} }
} }
} }
@ -83,7 +85,7 @@ export function vitePluginAnalyzer(
} }
} }
for (const [pageId, { hoistedSet }] of pageScripts) { for (const [pageId, { hoistedSet }] of pageScriptsMap) {
const pageData = getPageDataByViteID(internals, pageId); const pageData = getPageDataByViteID(internals, pageId);
if (!pageData) continue; if (!pageData) continue;

View file

@ -508,11 +508,14 @@ export function pluginContent(
return; return;
} }
// Cache build output of chunks and assets // Cache build output of chunks and assets
const promises: Promise<void[] | undefined>[] = [];
for (const { cached, dist } of cachedBuildOutput) { for (const { cached, dist } of cachedBuildOutput) {
if (fsMod.existsSync(dist)) { if (fsMod.existsSync(dist)) {
await copyFiles(dist, cached, true); promises.push(copyFiles(dist, cached, true));
} }
} }
if (promises.length) await Promise.all(promises);
}, },
}, },
}; };

View file

@ -1,7 +1,7 @@
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import glob from 'fast-glob'; import glob from 'fast-glob';
import type { OutputChunk } from 'rollup'; import type { OutputChunk } from 'rollup';
import { type Plugin as VitePlugin } from 'vite'; import type { Plugin as VitePlugin } from 'vite';
import { getAssetsPrefix } from '../../../assets/utils/getAssetsPrefix.js'; import { getAssetsPrefix } from '../../../assets/utils/getAssetsPrefix.js';
import { normalizeTheLocale } from '../../../i18n/index.js'; import { normalizeTheLocale } from '../../../i18n/index.js';
import { toRoutingStrategy } from '../../../i18n/utils.js'; import { toRoutingStrategy } from '../../../i18n/utils.js';

View file

@ -9,7 +9,7 @@ import type { AstroPreferences } from '../../preferences/index.js';
import type { AstroError } from '../errors/errors.js'; import type { AstroError } from '../errors/errors.js';
import { AggregateError, CompilerError } from '../errors/errors.js'; import { AggregateError, CompilerError } from '../errors/errors.js';
import { AstroErrorData } from '../errors/index.js'; import { AstroErrorData } from '../errors/index.js';
import { resolvePath } from '../util.js'; import { resolvePath } from '../viteUtils.js';
import { type PartialCompileCssResult, createStylePreprocessor } from './style.js'; import { type PartialCompileCssResult, createStylePreprocessor } from './style.js';
import type { CompileCssResult } from './types.js'; import type { CompileCssResult } from './types.js';

View file

@ -1,4 +1,9 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as colors from 'kleur/colors';
import type { Arguments as Flags } from 'yargs-parser'; import type { Arguments as Flags } from 'yargs-parser';
import { ZodError } from 'zod';
import type { import type {
AstroConfig, AstroConfig,
AstroInlineConfig, AstroInlineConfig,
@ -6,49 +11,14 @@ import type {
AstroUserConfig, AstroUserConfig,
CLIFlags, CLIFlags,
} from '../../@types/astro.js'; } from '../../@types/astro.js';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as colors from 'kleur/colors';
import { ZodError } from 'zod';
import { eventConfigError, telemetry } from '../../events/index.js'; import { eventConfigError, telemetry } from '../../events/index.js';
import { trackAstroConfigZodError } from '../errors/errors.js'; import { trackAstroConfigZodError } from '../errors/errors.js';
import { AstroError, AstroErrorData } from '../errors/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js';
import { formatConfigErrorMessage } from '../messages.js'; import { formatConfigErrorMessage } from '../messages.js';
import { mergeConfig } from './merge.js'; import { mergeConfig } from './merge.js';
import { createRelativeSchema } from './schema.js'; import { validateConfig } from './validate.js';
import { loadConfigWithVite } from './vite-load.js'; import { loadConfigWithVite } from './vite-load.js';
/** Turn raw config values into normalized values */
export async function validateConfig(
userConfig: any,
root: string,
cmd: string
): Promise<AstroConfig> {
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
// First-Pass Validation
let result: AstroConfig;
try {
result = await AstroConfigRelativeSchema.parseAsync(userConfig);
} catch (e) {
// Improve config zod error messages
if (e instanceof ZodError) {
// Mark this error so the callee can decide to suppress Zod's error if needed.
// We still want to throw the error to signal an error in validation.
trackAstroConfigZodError(e);
// eslint-disable-next-line no-console
console.error(formatConfigErrorMessage(e) + '\n');
telemetry.record(eventConfigError({ cmd, err: e, isFatal: true }));
}
throw e;
}
// If successful, return the result as a verified AstroConfig object.
return result;
}
/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */ /** Convert the generic "yargs" flag object into our own, custom TypeScript object. */
// NOTE: This function will be removed in a later PR. Use `flagsToAstroInlineConfig` instead. // NOTE: This function will be removed in a later PR. Use `flagsToAstroInlineConfig` instead.
// All CLI related flow should be located in the `packages/astro/src/cli` directory. // All CLI related flow should be located in the `packages/astro/src/cli` directory.
@ -197,7 +167,22 @@ export async function resolveConfig(
const userConfig = await loadConfig(root, inlineOnlyConfig.configFile, fsMod); const userConfig = await loadConfig(root, inlineOnlyConfig.configFile, fsMod);
const mergedConfig = mergeConfig(userConfig, inlineUserConfig); const mergedConfig = mergeConfig(userConfig, inlineUserConfig);
const astroConfig = await validateConfig(mergedConfig, root, command); // First-Pass Validation
let astroConfig: AstroConfig;
try {
astroConfig = await validateConfig(mergedConfig, root, command);
} catch (e) {
// Improve config zod error messages
if (e instanceof ZodError) {
// Mark this error so the callee can decide to suppress Zod's error if needed.
// We still want to throw the error to signal an error in validation.
trackAstroConfigZodError(e);
// eslint-disable-next-line no-console
console.error(formatConfigErrorMessage(e) + '\n');
telemetry.record(eventConfigError({ cmd: command, err: e, isFatal: true }));
}
throw e;
}
return { userConfig: mergedConfig, astroConfig }; return { userConfig: mergedConfig, astroConfig };
} }

View file

@ -0,0 +1,14 @@
import type { AstroConfig } from '../../@types/astro.js';
import { createRelativeSchema } from './schema.js';
/** Turn raw config values into normalized values */
export async function validateConfig(
userConfig: any,
root: string,
cmd: string
): Promise<AstroConfig> {
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
// First-Pass Validation
return await AstroConfigRelativeSchema.parseAsync(userConfig);
}

View file

@ -191,6 +191,19 @@ class AstroCookies implements AstroCookiesInterface {
} }
} }
/**
* Merges a new AstroCookies instance into the current instance. Any new cookies
* will be added to the current instance, overwriting any existing cookies with the same name.
*/
merge(cookies: AstroCookies) {
const outgoing = cookies.#outgoing;
if (outgoing) {
for (const [key, value] of outgoing) {
this.#ensureOutgoingMap().set(key, value);
}
}
}
/** /**
* Astro.cookies.header() returns an iterator for the cookies that have previously * Astro.cookies.header() returns an iterator for the cookies that have previously
* been set by either Astro.cookies.set() or Astro.cookies.delete(). * been set by either Astro.cookies.set() or Astro.cookies.delete().

View file

@ -10,7 +10,7 @@ export function responseHasCookies(response: Response): boolean {
return Reflect.has(response, astroCookiesSymbol); return Reflect.has(response, astroCookiesSymbol);
} }
function getFromResponse(response: Response): AstroCookies | undefined { export function getCookiesFromResponse(response: Response): AstroCookies | undefined {
let cookies = Reflect.get(response, astroCookiesSymbol); let cookies = Reflect.get(response, astroCookiesSymbol);
if (cookies != null) { if (cookies != null) {
return cookies as AstroCookies; return cookies as AstroCookies;
@ -20,7 +20,7 @@ function getFromResponse(response: Response): AstroCookies | undefined {
} }
export function* getSetCookiesFromResponse(response: Response): Generator<string, string[]> { export function* getSetCookiesFromResponse(response: Response): Generator<string, string[]> {
const cookies = getFromResponse(response); const cookies = getCookiesFromResponse(response);
if (!cookies) { if (!cookies) {
return []; return [];
} }

View file

@ -47,6 +47,7 @@ interface CreateViteOptions {
// will be undefined when using `getViteConfig` // will be undefined when using `getViteConfig`
command?: 'dev' | 'build'; command?: 'dev' | 'build';
fs?: typeof nodeFs; fs?: typeof nodeFs;
sync: boolean;
} }
const ALWAYS_NOEXTERNAL = [ const ALWAYS_NOEXTERNAL = [
@ -74,7 +75,7 @@ const ONLY_DEV_EXTERNAL = [
/** Return a base vite config as a common starting point for all Vite commands. */ /** Return a base vite config as a common starting point for all Vite commands. */
export async function createVite( export async function createVite(
commandConfig: vite.InlineConfig, commandConfig: vite.InlineConfig,
{ settings, logger, mode, command, fs = nodeFs }: CreateViteOptions { settings, logger, mode, command, fs = nodeFs, sync }: CreateViteOptions
): Promise<vite.InlineConfig> { ): Promise<vite.InlineConfig> {
const astroPkgsConfig = await crawlFrameworkPkgs({ const astroPkgsConfig = await crawlFrameworkPkgs({
root: fileURLToPath(settings.config.root), root: fileURLToPath(settings.config.root),
@ -137,7 +138,7 @@ export async function createVite(
// the build to run very slow as the filewatcher is triggered often. // the build to run very slow as the filewatcher is triggered often.
mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }), mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }),
envVitePlugin({ settings }), envVitePlugin({ settings }),
astroEnv({ settings, mode, fs }), astroEnv({ settings, mode, fs, sync }),
markdownVitePlugin({ settings, logger }), markdownVitePlugin({ settings, logger }),
htmlVitePlugin(), htmlVitePlugin(),
mdxVitePlugin(), mdxVitePlugin(),

View file

@ -74,7 +74,7 @@ export async function createContainer({
include: rendererClientEntries, include: rendererClientEntries,
}, },
}, },
{ settings, logger, mode: 'dev', command: 'dev', fs } { settings, logger, mode: 'dev', command: 'dev', fs, sync: false }
); );
await runHookConfigDone({ settings, logger }); await runHookConfigDone({ settings, logger });
const viteServer = await vite.createServer(viteConfig); const viteServer = await vite.createServer(viteConfig);

View file

@ -27,6 +27,7 @@ import {
responseSentSymbol, responseSentSymbol,
} from './constants.js'; } from './constants.js';
import { AstroCookies, attachCookiesToResponse } from './cookies/index.js'; import { AstroCookies, attachCookiesToResponse } from './cookies/index.js';
import { getCookiesFromResponse } from './cookies/response.js';
import { AstroError, AstroErrorData } from './errors/index.js'; import { AstroError, AstroErrorData } from './errors/index.js';
import { callMiddleware } from './middleware/callMiddleware.js'; import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js'; import { sequence } from './middleware/index.js';
@ -135,6 +136,7 @@ export class RenderContext {
const lastNext = async (ctx: APIContext, payload?: RewritePayload) => { const lastNext = async (ctx: APIContext, payload?: RewritePayload) => {
if (payload) { if (payload) {
if (this.pipeline.manifest.rewritingEnabled) { if (this.pipeline.manifest.rewritingEnabled) {
pipeline.logger.debug('router', 'Called rewriting to:', payload);
// we intentionally let the error bubble up // we intentionally let the error bubble up
const [routeData, component] = await pipeline.tryRewrite( const [routeData, component] = await pipeline.tryRewrite(
payload, payload,
@ -145,20 +147,23 @@ export class RenderContext {
componentInstance = component; componentInstance = component;
this.isRewriting = true; this.isRewriting = true;
} else { } else {
this.pipeline.logger.warn( this.pipeline.logger.error(
'router', 'router',
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.' 'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
); );
} }
} }
let response: Response;
switch (this.routeData.type) { switch (this.routeData.type) {
case 'endpoint': case 'endpoint': {
return renderEndpoint(componentInstance as any, ctx, serverLike, logger); response = await renderEndpoint(componentInstance as any, ctx, serverLike, logger);
break;
}
case 'redirect': case 'redirect':
return renderRedirect(this); return renderRedirect(this);
case 'page': { case 'page': {
const result = await this.createResult(componentInstance!); const result = await this.createResult(componentInstance!);
let response: Response;
try { try {
response = await renderPage( response = await renderPage(
result, result,
@ -185,12 +190,19 @@ export class RenderContext {
) { ) {
response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no'); response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no');
} }
return response; break;
} }
case 'fallback': { case 'fallback': {
return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: 'fallback' } }); return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: 'fallback' } });
} }
} }
// We need to merge the cookies from the response back into this.cookies
// because they may need to be passed along from a rewrite.
const responseCookies = getCookiesFromResponse(response);
if (responseCookies) {
cookies.merge(responseCookies);
}
return response;
}; };
const response = await callMiddleware( const response = await callMiddleware(
@ -227,6 +239,20 @@ export class RenderContext {
const rewrite = async (reroutePayload: RewritePayload) => { const rewrite = async (reroutePayload: RewritePayload) => {
pipeline.logger.debug('router', 'Called rewriting to:', reroutePayload); pipeline.logger.debug('router', 'Called rewriting to:', reroutePayload);
if (!this.pipeline.manifest.rewritingEnabled) {
this.pipeline.logger.error(
'router',
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
);
return new Response(
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
{
status: 500,
statusText:
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
}
);
}
const [routeData, component, newURL] = await pipeline.tryRewrite( const [routeData, component, newURL] = await pipeline.tryRewrite(
reroutePayload, reroutePayload,
this.request, this.request,
@ -421,6 +447,20 @@ export class RenderContext {
}; };
const rewrite = async (reroutePayload: RewritePayload) => { const rewrite = async (reroutePayload: RewritePayload) => {
if (!this.pipeline.manifest.rewritingEnabled) {
this.pipeline.logger.error(
'router',
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
);
return new Response(
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
{
status: 500,
statusText:
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
}
);
}
pipeline.logger.debug('router', 'Calling rewrite: ', reroutePayload); pipeline.logger.debug('router', 'Calling rewrite: ', reroutePayload);
const [routeData, component, newURL] = await pipeline.tryRewrite( const [routeData, component, newURL] = await pipeline.tryRewrite(
reroutePayload, reroutePayload,

View file

@ -7,6 +7,7 @@ import type { AstroConfig, AstroInlineConfig, AstroSettings } from '../../@types
import { getPackage } from '../../cli/install-package.js'; import { getPackage } from '../../cli/install-package.js';
import { createContentTypesGenerator } from '../../content/index.js'; import { createContentTypesGenerator } from '../../content/index.js';
import { globalContentConfigObserver } from '../../content/utils.js'; import { globalContentConfigObserver } from '../../content/utils.js';
import { syncAstroEnv } from '../../env/sync.js';
import { telemetry } from '../../events/index.js'; import { telemetry } from '../../events/index.js';
import { eventCliSession } from '../../events/session.js'; import { eventCliSession } from '../../events/session.js';
import { runHookConfigSetup } from '../../integrations/hooks.js'; import { runHookConfigSetup } from '../../integrations/hooks.js';
@ -83,6 +84,7 @@ export default async function sync(
await dbPackage?.typegen?.(astroConfig); await dbPackage?.typegen?.(astroConfig);
const exitCode = await syncContentCollections(settings, { ...options, logger }); const exitCode = await syncContentCollections(settings, { ...options, logger });
if (exitCode !== 0) return exitCode; if (exitCode !== 0) return exitCode;
syncAstroEnv(settings, options?.fs);
logger.info(null, `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`); logger.info(null, `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`);
return 0; return 0;
@ -123,7 +125,7 @@ export async function syncContentCollections(
ssr: { external: [] }, ssr: { external: [] },
logLevel: 'silent', logLevel: 'silent',
}, },
{ settings, logger, mode: 'build', command: 'build', fs } { settings, logger, mode: 'build', command: 'build', fs, sync: true }
) )
); );

View file

@ -1,11 +1,9 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { normalizePath } from 'vite';
import type { AstroConfig, AstroSettings, RouteType } from '../@types/astro.js'; import type { AstroConfig, AstroSettings, RouteType } from '../@types/astro.js';
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './constants.js'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './constants.js';
import type { ModuleLoader } from './module-loader/index.js'; import { removeTrailingForwardSlash, slash } from './path.js';
import { prependForwardSlash, removeTrailingForwardSlash, slash } from './path.js';
/** Returns true if argument is an object of any prototype/class (but not null). */ /** Returns true if argument is an object of any prototype/class (but not null). */
export function isObject(value: unknown): value is Record<string, any> { export function isObject(value: unknown): value is Record<string, any> {
@ -184,54 +182,10 @@ export function relativeToSrcDir(config: AstroConfig, idOrUrl: URL | string) {
return id.slice(slash(fileURLToPath(config.srcDir)).length); return id.slice(slash(fileURLToPath(config.srcDir)).length);
} }
export function rootRelativePath(
root: URL,
idOrUrl: URL | string,
shouldPrependForwardSlash = true
) {
let id: string;
if (typeof idOrUrl !== 'string') {
id = unwrapId(viteID(idOrUrl));
} else {
id = idOrUrl;
}
const normalizedRoot = normalizePath(fileURLToPath(root));
if (id.startsWith(normalizedRoot)) {
id = id.slice(normalizedRoot.length);
}
return shouldPrependForwardSlash ? prependForwardSlash(id) : id;
}
export function emoji(char: string, fallback: string) { export function emoji(char: string, fallback: string) {
return process.platform !== 'win32' ? char : fallback; return process.platform !== 'win32' ? char : fallback;
} }
/**
* Simulate Vite's resolve and import analysis so we can import the id as an URL
* through a script tag or a dynamic import as-is.
*/
// NOTE: `/@id/` should only be used when the id is fully resolved
export async function resolveIdToUrl(loader: ModuleLoader, id: string, root?: URL) {
let resultId = await loader.resolveId(id, undefined);
// Try resolve jsx to tsx
if (!resultId && id.endsWith('.jsx')) {
resultId = await loader.resolveId(id.slice(0, -4), undefined);
}
if (!resultId) {
return VALID_ID_PREFIX + id;
}
if (path.isAbsolute(resultId)) {
const normalizedRoot = root && normalizePath(fileURLToPath(root));
// Convert to root-relative path if path is inside root
if (normalizedRoot && resultId.startsWith(normalizedRoot)) {
return resultId.slice(normalizedRoot.length - 1);
} else {
return '/@fs' + prependForwardSlash(resultId);
}
}
return VALID_ID_PREFIX + resultId;
}
export function resolveJsToTs(filePath: string) { export function resolveJsToTs(filePath: string) {
if (filePath.endsWith('.jsx') && !fs.existsSync(filePath)) { if (filePath.endsWith('.jsx') && !fs.existsSync(filePath)) {
const tryPath = filePath.slice(0, -4) + '.tsx'; const tryPath = filePath.slice(0, -4) + '.tsx';
@ -242,18 +196,6 @@ export function resolveJsToTs(filePath: string) {
return filePath; return filePath;
} }
/**
* Resolve the hydration paths so that it can be imported in the client
*/
export function resolvePath(specifier: string, importer: string) {
if (specifier.startsWith('.')) {
const absoluteSpecifier = path.resolve(path.dirname(importer), specifier);
return resolveJsToTs(normalizePath(absoluteSpecifier));
} else {
return specifier;
}
}
/** /**
* Set a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config * Set a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config
*/ */

View file

@ -0,0 +1,62 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { normalizePath } from 'vite';
import { prependForwardSlash } from '../core/path.js';
import type { ModuleLoader } from './module-loader/index.js';
import { VALID_ID_PREFIX, resolveJsToTs, unwrapId, viteID } from './util.js';
/**
* Resolve the hydration paths so that it can be imported in the client
*/
export function resolvePath(specifier: string, importer: string) {
if (specifier.startsWith('.')) {
const absoluteSpecifier = path.resolve(path.dirname(importer), specifier);
return resolveJsToTs(normalizePath(absoluteSpecifier));
} else {
return specifier;
}
}
export function rootRelativePath(
root: URL,
idOrUrl: URL | string,
shouldPrependForwardSlash = true
) {
let id: string;
if (typeof idOrUrl !== 'string') {
id = unwrapId(viteID(idOrUrl));
} else {
id = idOrUrl;
}
const normalizedRoot = normalizePath(fileURLToPath(root));
if (id.startsWith(normalizedRoot)) {
id = id.slice(normalizedRoot.length);
}
return shouldPrependForwardSlash ? prependForwardSlash(id) : id;
}
/**
* Simulate Vite's resolve and import analysis so we can import the id as an URL
* through a script tag or a dynamic import as-is.
*/
// NOTE: `/@id/` should only be used when the id is fully resolved
export async function resolveIdToUrl(loader: ModuleLoader, id: string, root?: URL) {
let resultId = await loader.resolveId(id, undefined);
// Try resolve jsx to tsx
if (!resultId && id.endsWith('.jsx')) {
resultId = await loader.resolveId(id.slice(0, -4), undefined);
}
if (!resultId) {
return VALID_ID_PREFIX + id;
}
if (path.isAbsolute(resultId)) {
const normalizedRoot = root && normalizePath(fileURLToPath(root));
// Convert to root-relative path if path is inside root
if (normalizedRoot && resultId.startsWith(normalizedRoot)) {
return resultId.slice(normalizedRoot.length - 1);
} else {
return '/@fs' + prependForwardSlash(resultId);
}
}
return VALID_ID_PREFIX + resultId;
}

30
packages/astro/src/env/sync.ts vendored Normal file
View file

@ -0,0 +1,30 @@
import fsMod from 'node:fs';
import type { AstroSettings } from '../@types/astro.js';
import { ENV_TYPES_FILE, TYPES_TEMPLATE_URL } from './constants.js';
import { getEnvFieldType } from './validators.js';
export function syncAstroEnv(settings: AstroSettings, fs = fsMod) {
if (!settings.config.experimental.env) {
return;
}
const schema = settings.config.experimental.env.schema ?? {};
let client = '';
let server = '';
for (const [key, options] of Object.entries(schema)) {
const str = `export const ${key}: ${getEnvFieldType(options)}; \n`;
if (options.context === 'client') {
client += str;
} else {
server += str;
}
}
const template = fs.readFileSync(TYPES_TEMPLATE_URL, 'utf-8');
const dts = template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server);
fs.mkdirSync(settings.dotAstroDir, { recursive: true });
fs.writeFileSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir), dts, 'utf-8');
}

View file

@ -4,14 +4,12 @@ import { type Plugin, loadEnv } from 'vite';
import type { AstroSettings } from '../@types/astro.js'; import type { AstroSettings } from '../@types/astro.js';
import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { import {
ENV_TYPES_FILE,
MODULE_TEMPLATE_URL, MODULE_TEMPLATE_URL,
TYPES_TEMPLATE_URL,
VIRTUAL_MODULES_IDS, VIRTUAL_MODULES_IDS,
VIRTUAL_MODULES_IDS_VALUES, VIRTUAL_MODULES_IDS_VALUES,
} from './constants.js'; } from './constants.js';
import type { EnvSchema } from './schema.js'; import type { EnvSchema } from './schema.js';
import { getEnvFieldType, validateEnvVariable } from './validators.js'; import { validateEnvVariable } from './validators.js';
// TODO: reminders for when astro:env comes out of experimental // TODO: reminders for when astro:env comes out of experimental
// Types should always be generated (like in types/content.d.ts). That means the client module will be empty // Types should always be generated (like in types/content.d.ts). That means the client module will be empty
@ -23,14 +21,16 @@ interface AstroEnvVirtualModPluginParams {
settings: AstroSettings; settings: AstroSettings;
mode: 'dev' | 'build' | string; mode: 'dev' | 'build' | string;
fs: typeof fsMod; fs: typeof fsMod;
sync: boolean;
} }
export function astroEnv({ export function astroEnv({
settings, settings,
mode, mode,
fs, fs,
sync,
}: AstroEnvVirtualModPluginParams): Plugin | undefined { }: AstroEnvVirtualModPluginParams): Plugin | undefined {
if (!settings.config.experimental.env) { if (!settings.config.experimental.env || sync) {
return; return;
} }
const schema = settings.config.experimental.env.schema ?? {}; const schema = settings.config.experimental.env.schema ?? {};
@ -54,23 +54,10 @@ export function astroEnv({
const validatedVariables = validatePublicVariables({ schema, loadedEnv }); const validatedVariables = validatePublicVariables({ schema, loadedEnv });
const clientTemplates = getClientTemplates({ validatedVariables });
const serverTemplates = getServerTemplates({ validatedVariables, schema, fs });
templates = { templates = {
client: clientTemplates.module, ...getTemplates(schema, fs, validatedVariables),
server: serverTemplates.module,
internal: `export const schema = ${JSON.stringify(schema)};`, internal: `export const schema = ${JSON.stringify(schema)};`,
}; };
generateDts({
settings,
fs,
content: getDts({
fs,
client: clientTemplates.types,
server: serverTemplates.types,
}),
});
}, },
buildEnd() { buildEnd() {
templates = null; templates = null;
@ -104,19 +91,6 @@ function resolveVirtualModuleId<T extends string>(id: T): `\0${T}` {
return `\0${id}`; return `\0${id}`;
} }
function generateDts({
content,
settings,
fs,
}: {
content: string;
settings: AstroSettings;
fs: typeof fsMod;
}) {
fs.mkdirSync(settings.dotAstroDir, { recursive: true });
fs.writeFileSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir), content, 'utf-8');
}
function validatePublicVariables({ function validatePublicVariables({
schema, schema,
loadedEnv, loadedEnv,
@ -152,55 +126,22 @@ function validatePublicVariables({
return valid; return valid;
} }
function getDts({ function getTemplates(
client, schema: EnvSchema,
server, fs: typeof fsMod,
fs, validatedVariables: ReturnType<typeof validatePublicVariables>
}: { ) {
client: string; let client = '';
server: string; let server = fs.readFileSync(MODULE_TEMPLATE_URL, 'utf-8');
fs: typeof fsMod;
}) {
const template = fs.readFileSync(TYPES_TEMPLATE_URL, 'utf-8');
return template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server);
}
function getClientTemplates({
validatedVariables,
}: {
validatedVariables: ReturnType<typeof validatePublicVariables>;
}) {
let module = '';
let types = '';
for (const { key, type, value } of validatedVariables.filter((e) => e.context === 'client')) {
module += `export const ${key} = ${JSON.stringify(value)};`;
types += `export const ${key}: ${type}; \n`;
}
return {
module,
types,
};
}
function getServerTemplates({
validatedVariables,
schema,
fs,
}: {
validatedVariables: ReturnType<typeof validatePublicVariables>;
schema: EnvSchema;
fs: typeof fsMod;
}) {
let module = fs.readFileSync(MODULE_TEMPLATE_URL, 'utf-8');
let types = '';
let onSetGetEnv = ''; let onSetGetEnv = '';
for (const { key, type, value } of validatedVariables.filter((e) => e.context === 'server')) { for (const { key, value, context } of validatedVariables) {
module += `export const ${key} = ${JSON.stringify(value)};`; const str = `export const ${key} = ${JSON.stringify(value)};`;
types += `export const ${key}: ${type}; \n`; if (context === 'client') {
client += str;
} else {
server += str;
}
} }
for (const [key, options] of Object.entries(schema)) { for (const [key, options] of Object.entries(schema)) {
@ -208,15 +149,14 @@ function getServerTemplates({
continue; continue;
} }
types += `export const ${key}: ${getEnvFieldType(options)}; \n`; server += `export let ${key} = _internalGetSecret(${JSON.stringify(key)});\n`;
module += `export let ${key} = _internalGetSecret(${JSON.stringify(key)});\n`;
onSetGetEnv += `${key} = reset ? undefined : _internalGetSecret(${JSON.stringify(key)});\n`; onSetGetEnv += `${key} = reset ? undefined : _internalGetSecret(${JSON.stringify(key)});\n`;
} }
module = module.replace('// @@ON_SET_GET_ENV@@', onSetGetEnv); server = server.replace('// @@ON_SET_GET_ENV@@', onSetGetEnv);
return { return {
module, client,
types, server,
}; };
} }

View file

@ -2,7 +2,7 @@ import type { PluginObj } from '@babel/core';
import * as t from '@babel/types'; import * as t from '@babel/types';
import { AstroError } from '../core/errors/errors.js'; import { AstroError } from '../core/errors/errors.js';
import { AstroErrorData } from '../core/errors/index.js'; import { AstroErrorData } from '../core/errors/index.js';
import { resolvePath } from '../core/util.js'; import { resolvePath } from '../core/viteUtils.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js'; import type { PluginMetadata } from '../vite-plugin-astro/types.js';
const ClientOnlyPlaceholder = 'astro-client-only'; const ClientOnlyPlaceholder = 'astro-client-only';

View file

@ -9,7 +9,7 @@ import { visit } from 'unist-util-visit';
import type { VFile } from 'vfile'; import type { VFile } from 'vfile';
import { AstroError } from '../core/errors/errors.js'; import { AstroError } from '../core/errors/errors.js';
import { AstroErrorData } from '../core/errors/index.js'; import { AstroErrorData } from '../core/errors/index.js';
import { resolvePath } from '../core/util.js'; import { resolvePath } from '../core/viteUtils.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js'; import type { PluginMetadata } from '../vite-plugin-astro/types.js';
// This import includes ambient types for hast to include mdx nodes // This import includes ambient types for hast to include mdx nodes

View file

@ -45,7 +45,7 @@ export class DevToolbarTooltip extends HTMLElement {
svg { svg {
vertical-align: bottom; vertical-align: bottom;
margin-right: 4px; margin-inline-end: 4px;
} }
hr { hr {

View file

@ -1,4 +1,4 @@
import type { RouteData, SSRResult } from '../../../@types/astro.js'; import type { AstroConfig, RouteData, SSRResult } from '../../../@types/astro.js';
import { type NonAstroPageComponent, renderComponentToString } from './component.js'; import { type NonAstroPageComponent, renderComponentToString } from './component.js';
import type { AstroComponentFactory } from './index.js'; import type { AstroComponentFactory } from './index.js';
@ -85,6 +85,16 @@ export async function renderPage(
if (route?.component.endsWith('.md')) { if (route?.component.endsWith('.md')) {
headers.set('Content-Type', 'text/html; charset=utf-8'); headers.set('Content-Type', 'text/html; charset=utf-8');
} }
const response = new Response(body, { ...init, headers }); let status = init.status;
return response; // Custom 404.astro and 500.astro are particular routes that must return a fixed status code
if (route?.route === '/404') {
status = 404;
} else if (route?.route === '/500') {
status = 500;
}
if (status) {
return new Response(body, { ...init, headers, status });
} else {
return new Response(body, { ...init, headers });
}
} }

View file

@ -418,7 +418,13 @@ async function transition(
} }
// if there was a redirection, show the final URL in the browser's address bar // if there was a redirection, show the final URL in the browser's address bar
if (response.redirected) { if (response.redirected) {
preparationEvent.to = new URL(response.redirected); const redirectedTo = new URL(response.redirected);
// but do not redirect cross origin
if (redirectedTo.origin !== preparationEvent.to.origin) {
preparationEvent.preventDefault();
return;
}
preparationEvent.to = redirectedTo;
} }
parser ??= new DOMParser(); parser ??= new DOMParser();

View file

@ -24,11 +24,6 @@ export function recordServerError(
// Our error should already be complete, but let's try to add a bit more through some guesswork // Our error should already be complete, but let's try to add a bit more through some guesswork
const errorWithMetadata = collectErrorMetadata(err, config.root); const errorWithMetadata = collectErrorMetadata(err, config.root);
// Ignore unhandled rejection errors as they appear A LOT and we cannot record the amount to telemetry
if (errorWithMetadata.name !== AstroErrorData.UnhandledRejection.name) {
telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
}
logger.error(null, formatErrorMessage(errorWithMetadata, logger.level() === 'debug')); logger.error(null, formatErrorMessage(errorWithMetadata, logger.level() === 'debug'));
return { return {

View file

@ -12,18 +12,16 @@ import type {
} from '../@types/astro.js'; } from '../@types/astro.js';
import { getInfoOutput } from '../cli/info/index.js'; import { getInfoOutput } from '../cli/info/index.js';
import { type HeadElements } from '../core/base-pipeline.js'; import { type HeadElements } from '../core/base-pipeline.js';
import { shouldAppendForwardSlash } from '../core/build/util.js';
import { ASTRO_VERSION, DEFAULT_404_COMPONENT } from '../core/constants.js'; import { ASTRO_VERSION, DEFAULT_404_COMPONENT } from '../core/constants.js';
import { enhanceViteSSRError } from '../core/errors/dev/index.js'; import { enhanceViteSSRError } from '../core/errors/dev/index.js';
import { RewriteEncounteredAnError } from '../core/errors/errors-data.js'; import { AggregateError, CSSError, MarkdownError } from '../core/errors/index.js';
import { AggregateError, AstroError, CSSError, MarkdownError } from '../core/errors/index.js';
import type { Logger } from '../core/logger/core.js'; import type { Logger } from '../core/logger/core.js';
import type { ModuleLoader } from '../core/module-loader/index.js'; import type { ModuleLoader } from '../core/module-loader/index.js';
import { prependForwardSlash, removeTrailingForwardSlash } from '../core/path.js';
import { Pipeline, loadRenderer } from '../core/render/index.js'; import { Pipeline, loadRenderer } from '../core/render/index.js';
import { DEFAULT_404_ROUTE, default404Page } from '../core/routing/astro-designed-error-pages.js'; import { default404Page } from '../core/routing/astro-designed-error-pages.js';
import { findRouteToRewrite } from '../core/routing/rewrite.js'; import { findRouteToRewrite } from '../core/routing/rewrite.js';
import { isPage, isServerLikeOutput, resolveIdToUrl, viteID } from '../core/util.js'; import { isPage, isServerLikeOutput, viteID } from '../core/util.js';
import { resolveIdToUrl } from '../core/viteUtils.js';
import { PAGE_SCRIPT_ID } from '../vite-plugin-scripts/index.js'; import { PAGE_SCRIPT_ID } from '../vite-plugin-scripts/index.js';
import { getStylesForURL } from './css.js'; import { getStylesForURL } from './css.js';
import { getComponentMetadata } from './metadata.js'; import { getComponentMetadata } from './metadata.js';

View file

@ -1,5 +1,5 @@
import type { ModuleLoader } from '../core/module-loader/index.js'; import type { ModuleLoader } from '../core/module-loader/index.js';
import { resolveIdToUrl } from '../core/util.js'; import { resolveIdToUrl } from '../core/viteUtils.js';
export function createResolve(loader: ModuleLoader, root: URL) { export function createResolve(loader: ModuleLoader, root: URL) {
// Resolves specifiers in the inline hydrated scripts, such as: // Resolves specifiers in the inline hydrated scripts, such as:

View file

@ -1,7 +1,8 @@
import type { SSRElement } from '../@types/astro.js'; import type { SSRElement } from '../@types/astro.js';
import type { ModuleInfo, ModuleLoader } from '../core/module-loader/index.js'; import type { ModuleInfo, ModuleLoader } from '../core/module-loader/index.js';
import { createModuleScriptElementWithSrc } from '../core/render/ssr-element.js'; import { createModuleScriptElementWithSrc } from '../core/render/ssr-element.js';
import { rootRelativePath, viteID } from '../core/util.js'; import { viteID } from '../core/util.js';
import { rootRelativePath } from '../core/viteUtils.js';
import type { PluginMetadata as AstroPluginMetadata } from '../vite-plugin-astro/types.js'; import type { PluginMetadata as AstroPluginMetadata } from '../vite-plugin-astro/types.js';
import { crawlGraph } from './vite.js'; import { crawlGraph } from './vite.js';

Some files were not shown because too many files have changed in this diff Show more