mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
Merge branch 'main' into feat/fonts
This commit is contained in:
commit
c515886407
160 changed files with 1875 additions and 1502 deletions
23
.changeset/bright-eels-cross.md
Normal file
23
.changeset/bright-eels-cross.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Exposes extra APIs for scripting and testing.
|
||||
|
||||
### Config helpers
|
||||
|
||||
Two new helper functions exported from `astro/config`:
|
||||
|
||||
- `mergeConfig()` allows users to merge partially defined Astro configurations on top of a base config while following the merge rules of `updateConfig()` available for integrations.
|
||||
- `validateConfig()` allows users to validate that a given value is a valid Astro configuration and fills in default values as necessary.
|
||||
|
||||
These helpers are particularly useful for integration authors and for developers writing scripts that need to manipulate Astro configurations programmatically.
|
||||
|
||||
### Programmatic build
|
||||
|
||||
The `build` API now receives a second optional `BuildOptions` argument where users can specify:
|
||||
|
||||
- `devOutput` (default `false`): output a development-based build similar to code transformed in `astro dev`.
|
||||
- `teardownCompiler` (default `true`): teardown the compiler WASM instance after build.
|
||||
|
||||
These options provide more control when running Astro builds programmatically, especially for testing scenarios or custom build pipelines.
|
23
.changeset/clever-rice-compete.md
Normal file
23
.changeset/clever-rice-compete.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Adds experimental responsive image support in Markdown
|
||||
|
||||
Previously, the `experimental.responsiveImages` feature could only provide responsive images when using the `<Image />` and `<Picture />` components.
|
||||
|
||||
Now, images written with the `![]()` Markdown syntax in Markdown and MDX files will generate responsive images by default when using this experimental feature.
|
||||
|
||||
To try this experimental feature, set `experimental.responsiveImages` to true in your `astro.config.mjs` file:
|
||||
|
||||
```js
|
||||
{
|
||||
experimental: {
|
||||
responsiveImages: true,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Learn more about using this feature in the [experimental responsive images feature reference](https://docs.astro.build/en/reference/experimental-flags/responsive-images/).
|
||||
|
||||
For a complete overview, and to give feedback on this experimental API, see the [Responsive Images RFC](https://github.com/withastro/roadmap/blob/responsive-images/proposals/0053-responsive-images.md).
|
30
.changeset/grumpy-sloths-fail.md
Normal file
30
.changeset/grumpy-sloths-fail.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Adds a new configuration option `server.allowedHosts` and CLI option `--allowed-hosts`.
|
||||
|
||||
Now you can specify the hostnames that the dev and preview servers are allowed to respond to. This is useful for allowing additional subdomains, or running the dev server in a web container.
|
||||
|
||||
`allowedHosts` checks the Host header on HTTP requests from browsers and if it doesn't match, it will reject the request to prevent CSRF and XSS attacks.
|
||||
|
||||
```shell
|
||||
astro dev --allowed-hosts=foo.bar.example.com,bar.example.com
|
||||
```
|
||||
|
||||
```shell
|
||||
astro preview --allowed-hosts=foo.bar.example.com,bar.example.com
|
||||
```
|
||||
|
||||
```js
|
||||
// astro.config.mjs
|
||||
import {defineConfig} from "astro/config";
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
allowedHosts: ['foo.bar.example.com', 'bar.example.com']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
This feature is the same as [Vite's `server.allowHosts` configuration](https://vite.dev/config/server-options.html#server-allowedhosts).
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
'@astrojs/vue': patch
|
||||
---
|
||||
|
||||
Fixes a case where the compiler could not be resolved automatically
|
5
.changeset/late-mails-beam.md
Normal file
5
.changeset/late-mails-beam.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/db': patch
|
||||
---
|
||||
|
||||
Expose `ilike` function from `drizzle-orm`
|
5
.changeset/purple-jokes-pay.md
Normal file
5
.changeset/purple-jokes-pay.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes an issue where a form field named "attributes" shadows the form.attributes property.
|
7
.changeset/quiet-birds-joke.md
Normal file
7
.changeset/quiet-birds-joke.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
'@astrojs/internal-helpers': minor
|
||||
---
|
||||
|
||||
Adds remote URL filtering utilities
|
||||
|
||||
This adds logic to filter remote URLs so that it can be used by both `astro` and `@astrojs/markdown-remark`.
|
11
.changeset/shy-bats-exist.md
Normal file
11
.changeset/shy-bats-exist.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Adds the ability to process and optimize remote images in Markdown files
|
||||
|
||||
Previously, Astro only allowed local images to be optimized when included using `![]()` syntax in plain Markdown files. Astro's image service could only display remote images without any processing.
|
||||
|
||||
Now, Astro's image service can also optimize remote images written in standard Markdown syntax. This allows you to enjoy the benefits of Astro's image processing when your images are stored externally, for example in a CMS or digital asset manager.
|
||||
|
||||
No additional configuration is required to use this feature! Any existing remote images written in Markdown will now automatically be optimized. To opt-out of this processing, write your images in Markdown using the HTML `<img>` tag instead. Note that images located in your `public/` folder are still never processed.
|
7
.changeset/slimy-cougars-worry.md
Normal file
7
.changeset/slimy-cougars-worry.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes incorrect config update when calling `updateConfig` from `astro:build:setup` hook.
|
||||
|
||||
The function previously called a custom update config function made for merging an Astro config. Now it calls the appropriate `mergeConfig()` utility exported by Vite that updates functional options correctly.
|
5
.changeset/stale-oranges-call.md
Normal file
5
.changeset/stale-oranges-call.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes an issue where the dev server was applying second decoding of the URL of the incoming request, causing issues for certain URLs.
|
11
.changeset/tiny-cows-march.md
Normal file
11
.changeset/tiny-cows-march.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
'@astrojs/mdx': minor
|
||||
---
|
||||
|
||||
Adds the ability to process and optimize remote images in Markdown syntax in MDX files.
|
||||
|
||||
Previously, Astro only allowed local images to be optimized when included using `![]()` syntax. Astro's image service could only display remote images without any processing.
|
||||
|
||||
Now, Astro's image service can also optimize remote images written in standard Markdown syntax. This allows you to enjoy the benefits of Astro's image processing when your images are stored externally, for example in a CMS or digital asset manager.
|
||||
|
||||
No additional configuration is required to use this feature! Any existing remote images written in Markdown will now automatically be optimized. To opt-out of this processing, write your images in Markdown using the JSX `<img/>` tag instead. Note that images located in your `public/` folder are still never processed.
|
27
.changeset/tiny-gifts-drum.md
Normal file
27
.changeset/tiny-gifts-drum.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
'@astrojs/vercel': minor
|
||||
---
|
||||
|
||||
Adds support for regular expressions in ISR exclude list
|
||||
|
||||
Previously, excluding a page from ISR required explicitly listing it in `isr.exclude`. As websites grew larger, maintaining this list became increasingly difficult, especially for multiple API routes and pages that needed server-side rendering.
|
||||
|
||||
To address this, ISR exclusions now support regular expressions, allowing for more flexible and scalable configurations.
|
||||
|
||||
```js
|
||||
// astro.config.mjs
|
||||
import vercel from '@astrojs/vercel/serverless';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: vercel({
|
||||
isr: {
|
||||
exclude: [
|
||||
'/preview', // Excludes a specific route (e.g., pages/preview.astro)
|
||||
'/auth/[page]', // Excludes a dynamic route (e.g., pages/auth/[page].astro)
|
||||
/^\/api\/.+/, // Excludes all routes starting with /api/
|
||||
]
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
11
.changeset/warm-planes-swim.md
Normal file
11
.changeset/warm-planes-swim.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
'@astrojs/markdown-remark': minor
|
||||
---
|
||||
|
||||
Adds remote image optimization in Markdown
|
||||
|
||||
Previously, an internal remark plugin only looked for images in `![]()` syntax that referred to a relative file path. This meant that only local images stored in `src/` were passed through to an internal rehype plugin that would transform them for later processing by Astro's image service.
|
||||
|
||||
Now, the plugins recognize and transform both local and remote images using this syntax. Only [authorized remote images specified in your config](https://docs.astro.build/en/guides/images/#authorizing-remote-images) are transformed; remote images from other sources will not be processed.
|
||||
|
||||
While not configurable at this time, this process outputs two separate metadata fields (`localImagePaths` and `remoteImagePaths`) which allow for the possibility of controlling the behavior of each type of image separately in the future.
|
4
.github/scripts/announce.mjs
vendored
4
.github/scripts/announce.mjs
vendored
|
@ -1,6 +1,6 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { globby as glob } from 'globby';
|
||||
import { glob } from 'tinyglobby';
|
||||
import { setOutput } from './utils.mjs';
|
||||
|
||||
const { GITHUB_REF = 'main' } = process.env;
|
||||
|
@ -85,6 +85,8 @@ async function generatePackageMap() {
|
|||
const packageRoot = new URL('../../packages/', import.meta.url);
|
||||
const packages = await glob(['*/package.json', '*/*/package.json'], {
|
||||
cwd: fileURLToPath(packageRoot),
|
||||
expandDirectories: false,
|
||||
ignore: ['**/node_modules/**'],
|
||||
});
|
||||
await Promise.all(
|
||||
packages.map(async (pkg) => {
|
||||
|
|
26
.github/workflows/preview-comment.yml
vendored
26
.github/workflows/preview-comment.yml
vendored
|
@ -1,26 +0,0 @@
|
|||
name: Add continuous release label
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label:
|
||||
if: ${{ github.repository_owner == 'withastro' && startsWith(github.event.comment.body, '!preview') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check if user has write access
|
||||
uses: lannonbr/repo-permission-check-action@2.0.2
|
||||
with:
|
||||
permission: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- run: |
|
||||
gh issue edit ${{ github.event.issue.number }} --add-label "pr: preview" --repo ${{ github.repository }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
31
.github/workflows/preview-release.yml
vendored
31
.github/workflows/preview-release.yml
vendored
|
@ -24,8 +24,7 @@ env:
|
|||
|
||||
jobs:
|
||||
preview:
|
||||
if: |
|
||||
${{ github.repository_owner == 'withastro' && contains(github.event.pull_request.labels.*.name, 'pr: preview') }}
|
||||
if: ${{ github.repository_owner == 'withastro' && github.event.label.name == 'pr preview' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -54,28 +53,12 @@ jobs:
|
|||
|
||||
- name: Build Packages
|
||||
run: pnpm run build
|
||||
|
||||
- name: Remove Preview Label
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: "pr: preview"
|
||||
|
||||
# - name: Changesets status
|
||||
# run: pnpm changeset status --output=changesets.json
|
||||
#
|
||||
# - name: Retrieve packages to publish
|
||||
# uses: actions/github-script@v7
|
||||
# id: packages
|
||||
# with:
|
||||
# script: |
|
||||
# const fs = require('fs');
|
||||
# let packages = JSON.parse(fs.readFileSync('changesets.json', 'utf8'));
|
||||
# const releases = packages.releases
|
||||
# .filter(p => {
|
||||
# return p.changesets.length > 0;
|
||||
# })
|
||||
# .map(p => p.name);
|
||||
# if (releases.length > 0) {
|
||||
# return releases.join(' ');
|
||||
# }
|
||||
# return ""
|
||||
# result-encoding: string
|
||||
|
||||
- name: Publish packages
|
||||
run: |
|
||||
pnpx pkg-pr-new publish --pnpm --compact --no-template 'packages/astro' 'packages/integrations/node' 'packages/integrations/cloudflare' 'packages/integrations/netlify' 'packages/integrations/vercel'
|
||||
pnpm dlx pkg-pr-new publish --pnpm --compact --no-template 'packages/astro' 'packages/integrations/node' 'packages/integrations/cloudflare' 'packages/integrations/netlify' 'packages/integrations/vercel'
|
||||
|
|
206
.github/workflows/snapshot-release.yml
vendored
206
.github/workflows/snapshot-release.yml
vendored
|
@ -1,206 +0,0 @@
|
|||
name: Create a Snapshot Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
snapshot-release:
|
||||
name: Create a snapshot release of a pull request
|
||||
if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && (contains(github.event.comment.body, '!preview') || contains(github.event.comment.body, '/preview') || contains(github.event.comment.body, '!snapshot') || contains(github.event.comment.body, '/snapshot')) }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check if user has write access
|
||||
uses: lannonbr/repo-permission-check-action@2.0.2
|
||||
with:
|
||||
permission: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract the snapshot name from comment body
|
||||
id: getSnapshotName
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { body } = context.payload.comment;
|
||||
const PREVIEW_RE = /^[!\/](?:preview|snapshot)\s+(\S*)\s*$/gim;
|
||||
const [_, name] = PREVIEW_RE.exec(body) ?? [];
|
||||
if (name) return name;
|
||||
|
||||
const error = 'Invalid command. Expected: "/preview <snapshot-name>"'
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: error,
|
||||
})
|
||||
core.setFailed(error)
|
||||
result-encoding: string
|
||||
|
||||
- name: resolve pr refs
|
||||
id: refs
|
||||
uses: eficode/resolve-pr-refs@main
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.refs.outputs.head_ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract base branch from .changeset/config.json
|
||||
id: getBaseBranch
|
||||
run: |
|
||||
baseBranch=$(jq -r '.baseBranch' .changeset/config.json)
|
||||
echo "baseBranch=${baseBranch}" >> $GITHUB_OUTPUT
|
||||
|
||||
- run: git fetch origin ${{ steps.getBaseBranch.outputs.baseBranch }}:${{ steps.getBaseBranch.outputs.baseBranch }}
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build Packages
|
||||
run: pnpm run build
|
||||
|
||||
- name: Bump Package Versions
|
||||
id: changesets
|
||||
run: |
|
||||
pnpm exec changeset status --output status.output.json 2>&1
|
||||
# Snapshots don't work in pre mode. See https://github.com/changesets/changesets/issues/1195
|
||||
pnpm exec changeset pre exit || true
|
||||
pnpm exec changeset version --snapshot ${{ steps.getSnapshotName.outputs.result }}
|
||||
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
echo "status<<$EOF" >> $GITHUB_OUTPUT
|
||||
echo "$(cat status.output.json)" >> $GITHUB_OUTPUT
|
||||
echo "$EOF" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
# Needs access to run the script
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Disable color
|
||||
FORCE_COLOR: 0
|
||||
NO_COLOR: 1
|
||||
|
||||
- name: Publish Release
|
||||
id: publish
|
||||
run: |
|
||||
set -o pipefail
|
||||
GITHUB_ACTIONS=0 pnpm run build 2>&1 | tee build.output.txt
|
||||
BUILD_EXIT_CODE=$?
|
||||
|
||||
if [ $BUILD_EXIT_CODE -ne 0 ]; then
|
||||
echo "::error::Build failed. See output above."
|
||||
# Store the build output for the notification step before exiting
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
echo "build<<$EOF" >> $GITHUB_OUTPUT
|
||||
echo "$(cat build.output.txt)" >> $GITHUB_OUTPUT
|
||||
echo "$EOF" >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pnpm exec changeset publish --tag experimental--${{ steps.getSnapshotName.outputs.result }} 2>&1 | tee publish.output.txt
|
||||
PUBLISH_EXIT_CODE=$?
|
||||
|
||||
if [ $PUBLISH_EXIT_CODE -ne 0 ]; then
|
||||
echo "::error::Publish failed. See output above."
|
||||
fi
|
||||
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
|
||||
echo "build<<$EOF" >> $GITHUB_OUTPUT
|
||||
echo "$(cat build.output.txt)" >> $GITHUB_OUTPUT
|
||||
echo "$EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "publish<<$EOF" >> $GITHUB_OUTPUT
|
||||
echo "$(cat publish.output.txt)" >> $GITHUB_OUTPUT
|
||||
echo "$EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
# Exit with error if publish failed
|
||||
exit $PUBLISH_EXIT_CODE
|
||||
env:
|
||||
# Needs access to publish to npm
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
# Disable color
|
||||
FORCE_COLOR: 0
|
||||
NO_COLOR: 1
|
||||
|
||||
- name: Pull Request Notification
|
||||
uses: actions/github-script@v7
|
||||
if: always()
|
||||
env:
|
||||
TAG: ${{ steps.getSnapshotName.outputs.result }}
|
||||
STATUS_DATA: ${{ steps.changesets.outputs.status }}
|
||||
BUILD_LOG: ${{ steps.publish.outputs.build }}
|
||||
PUBLISH_LOG: ${{ steps.publish.outputs.publish }}
|
||||
JOB_STATUS: ${{ job.status }}
|
||||
with:
|
||||
script: |
|
||||
let changeset = { releases: [] };
|
||||
try {
|
||||
changeset = JSON.parse(process.env.STATUS_DATA);
|
||||
} catch (e) {}
|
||||
|
||||
let message = '';
|
||||
|
||||
if (process.env.JOB_STATUS === 'success') {
|
||||
message = 'Snapshots have been released for the following packages:';
|
||||
for (const release of changeset.releases) {
|
||||
if (release.type === 'none') continue;
|
||||
message += `\n- \`${release.name}@experimental--${process.env.TAG}\``;
|
||||
}
|
||||
} else {
|
||||
message = '❌ Snapshot release failed.';
|
||||
}
|
||||
|
||||
function details(title, body, failed = false) {
|
||||
if (!body) return;
|
||||
message += '\n';
|
||||
const icon = failed ? '❌ ' : '';
|
||||
message += `<details><summary><strong>${icon}${title}</strong></summary>`;
|
||||
message += '\n\n```\n';
|
||||
message += body;
|
||||
message += '\n```\n\n</details>';
|
||||
}
|
||||
|
||||
// Show build log first if it exists
|
||||
if (process.env.BUILD_LOG) {
|
||||
details('Build Log', process.env.BUILD_LOG, process.env.JOB_STATUS !== 'success');
|
||||
}
|
||||
|
||||
// Only show publish log if it exists (might not if build failed)
|
||||
if (process.env.PUBLISH_LOG) {
|
||||
details('Publish Log', process.env.PUBLISH_LOG, process.env.JOB_STATUS !== 'success');
|
||||
}
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: message,
|
||||
})
|
|
@ -283,6 +283,12 @@ Assigning labels isn't always easy and many times the distinction between the di
|
|||
- In case the number of reactions of an issue grows, the number of users affected grows, or a discussion uncovers some insights that weren't clear before, it's OK to change the priority of the issue. The maintainer **should** provide an explanation when assigning a different label.
|
||||
As with any other contribution, triaging is voluntary and best-efforts. We welcome and appreciate all the help you're happy to give (including reading this!) and nothing more. If you are not confident about an issue, you are welcome to leave an issue untriaged for someone who would have more context, or to bring it to their attention.
|
||||
|
||||
### Preview releases
|
||||
|
||||
You can trigger a preview release **from a PR** anytime by using the label `pr preview`. Add this label, and a workflow will trigger, which at the end will add a comment with the instructions of how to install the preview release.
|
||||
|
||||
If you're in need to trigger multiple preview releases from the same PR, remove the label and add it again.
|
||||
|
||||
## Code Structure
|
||||
|
||||
Server-side rendering (SSR) can be complicated. The Astro package (`packages/astro`) is structured in a way to help think about the different systems.
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"@benchmark/timer": "workspace:*",
|
||||
"@benchmark/adapter": "workspace:*",
|
||||
"astro": "workspace:*",
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
"@astrojs/mdx": "^4.0.8",
|
||||
"@astrojs/rss": "^4.0.11",
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
],
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"astro": "^4.0.0 || ^5.0.0"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "^4.2.0",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"vitest": "^3.0.5"
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
"@astrojs/alpinejs": "^0.4.3",
|
||||
"@types/alpinejs": "^3.13.11",
|
||||
"alpinejs": "^3.14.8",
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
"@astrojs/react": "^4.2.0",
|
||||
"@astrojs/solid-js": "^5.0.4",
|
||||
"@astrojs/svelte": "^7.0.4",
|
||||
"@astrojs/vue": "^5.0.6",
|
||||
"@astrojs/vue": "^5.0.7",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"preact": "^10.25.4",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@astrojs/preact": "^4.0.4",
|
||||
"@preact/signals": "^2.0.1",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"preact": "^10.25.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"@astrojs/react": "^4.2.0",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/solid-js": "^5.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"solid-js": "^1.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/svelte": "^7.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"svelte": "^5.19.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/vue": "^5.0.6",
|
||||
"astro": "^5.3.0",
|
||||
"@astrojs/vue": "^5.0.7",
|
||||
"astro": "^5.3.1",
|
||||
"vue": "^3.5.13"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.1.0",
|
||||
"astro": "^5.3.0"
|
||||
"@astrojs/node": "^9.1.1",
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
],
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"astro": "^4.0.0"
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
"server": "node dist/server/entry.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.1.0",
|
||||
"@astrojs/node": "^9.1.1",
|
||||
"@astrojs/svelte": "^7.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"svelte": "^5.19.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"sass": "^1.83.4",
|
||||
"sharp": "^0.33.3"
|
||||
}
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.17.8",
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/markdoc": "^0.12.9",
|
||||
"astro": "^5.3.0"
|
||||
"astro": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.0.8",
|
||||
"@astrojs/preact": "^4.0.4",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"preact": "^10.25.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@astrojs/preact": "^4.0.4",
|
||||
"@nanostores/preact": "^0.5.2",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"nanostores": "^0.11.3",
|
||||
"preact": "^10.25.4"
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"@astrojs/mdx": "^4.0.8",
|
||||
"@tailwindcss/vite": "^4.0.3",
|
||||
"@types/canvas-confetti": "^1.9.0",
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"tailwindcss": "^4.0.3"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.3.0",
|
||||
"astro": "^5.3.1",
|
||||
"vitest": "^3.0.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,11 +62,11 @@
|
|||
"esbuild": "^0.24.2",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-regexp": "^2.7.0",
|
||||
"globby": "^14.0.2",
|
||||
"only-allow": "^1.2.1",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"publint": "^0.3.2",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"turbo": "^2.4.0",
|
||||
"typescript": "~5.7.3",
|
||||
"typescript-eslint": "^8.23.0"
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
# astro
|
||||
|
||||
## 5.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#13233](https://github.com/withastro/astro/pull/13233) [`32fafeb`](https://github.com/withastro/astro/commit/32fafeb874cc4b6312eb50d54d9f0ca6b83aedbc) Thanks [@joshmkennedy](https://github.com/joshmkennedy)! - Ensures consistent behaviour of `Astro.rewrite`/`ctx.rewrite` when using `base` and `trailingSlash` options.
|
||||
|
||||
- [#13003](https://github.com/withastro/astro/pull/13003) [`ea79054`](https://github.com/withastro/astro/commit/ea790542e186b0d2d2e828cb3ebd23bde4d04879) Thanks [@chaegumi](https://github.com/chaegumi)! - Fixes a bug that caused the `vite.base` value to be ignored when running `astro dev`
|
||||
|
||||
- [#13299](https://github.com/withastro/astro/pull/13299) [`2e1321e`](https://github.com/withastro/astro/commit/2e1321e9d5b27da3e86bc4021e4136661a8055aa) Thanks [@bluwy](https://github.com/bluwy)! - Uses `tinyglobby` for globbing files
|
||||
|
||||
- [#13233](https://github.com/withastro/astro/pull/13233) [`32fafeb`](https://github.com/withastro/astro/commit/32fafeb874cc4b6312eb50d54d9f0ca6b83aedbc) Thanks [@joshmkennedy](https://github.com/joshmkennedy)! - Ensures that `Astro.url`/`ctx.url` is correctly updated with the `base` path after rewrites.
|
||||
|
||||
This change fixes an issue where `Astro.url`/`ctx.url` did not include the configured base path after Astro.rewrite was called. Now, the base path is correctly reflected in Astro.url.
|
||||
|
||||
Previously, any rewrites performed through `Astro.rewrite`/`ctx.rewrite` failed to append the base path to `Astro.url`/`ctx.rewrite`, which could lead to incorrect URL handling in downstream logic. By fixing this, we ensure that all routes remain consistent and predictable after a rewrite.
|
||||
|
||||
If you were relying on the work around of including the base path in astro.rewrite you can now remove it from the path.
|
||||
|
||||
## 5.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
import { type LocalImageProps, type RemoteImageProps, getImage, imageConfig } from 'astro:assets';
|
||||
import type { UnresolvedImageTransform } from '../dist/assets/types';
|
||||
import { applyResponsiveAttributes } from '../dist/assets/utils/imageAttributes.js';
|
||||
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
|
||||
import type { HTMLAttributes } from '../types';
|
||||
|
||||
|
@ -46,14 +45,7 @@ if (import.meta.env.DEV) {
|
|||
additionalAttributes['data-image-component'] = 'true';
|
||||
}
|
||||
|
||||
const { class: className, ...attributes } = useResponsive
|
||||
? applyResponsiveAttributes({
|
||||
layout,
|
||||
image,
|
||||
props,
|
||||
additionalAttributes,
|
||||
})
|
||||
: { ...additionalAttributes, ...image.attributes };
|
||||
const { class: className, ...attributes } = { ...additionalAttributes, ...image.attributes };
|
||||
---
|
||||
|
||||
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
---
|
||||
import { type LocalImageProps, type RemoteImageProps, getImage, imageConfig } from 'astro:assets';
|
||||
import * as mime from 'mrmime';
|
||||
import { applyResponsiveAttributes } from '../dist/assets/utils/imageAttributes';
|
||||
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
|
||||
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind.js';
|
||||
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
|
||||
import type {
|
||||
GetImageResult,
|
||||
|
@ -101,18 +100,14 @@ if (fallbackImage.srcSet.values.length > 0) {
|
|||
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
|
||||
}
|
||||
|
||||
const { class: className, ...attributes } = useResponsive
|
||||
? applyResponsiveAttributes({
|
||||
layout,
|
||||
image: fallbackImage,
|
||||
props,
|
||||
additionalAttributes: imgAdditionalAttributes,
|
||||
})
|
||||
: { ...imgAdditionalAttributes, ...fallbackImage.attributes };
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
imgAdditionalAttributes['data-image-component'] = 'true';
|
||||
}
|
||||
|
||||
const { class: className, ...attributes } = {
|
||||
...imgAdditionalAttributes,
|
||||
...fallbackImage.attributes,
|
||||
};
|
||||
---
|
||||
|
||||
<picture {...pictureAttributes}>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/db": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"@astrojs/react": "workspace:*",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/db": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"@astrojs/react": "workspace:*",
|
||||
"@types/react": "npm:types-react",
|
||||
"@types/react-dom": "npm:types-react-dom",
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2"
|
||||
"@astrojs/node": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2"
|
||||
"@astrojs/node": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"@astrojs/react": "workspace:*",
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"@astrojs/react": "workspace:*",
|
||||
"@astrojs/solid-js": "workspace:*",
|
||||
"@astrojs/svelte": "workspace:*",
|
||||
|
|
|
@ -12,5 +12,6 @@ export const prerender = false;
|
|||
<input type="hidden" name="name" value="Testing" />
|
||||
{postShowThrow ? <input type="hidden" name="throw" value="true" /> : ''}
|
||||
<input type="submit" value="Submit" id="submit" />
|
||||
<input type="text" name="attributes" />
|
||||
</form>
|
||||
</Layout>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "astro",
|
||||
"version": "5.3.0",
|
||||
"version": "5.3.1",
|
||||
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
|
||||
"type": "module",
|
||||
"author": "withastro",
|
||||
|
@ -144,7 +144,6 @@
|
|||
"es-module-lexer": "^1.6.0",
|
||||
"esbuild": "^0.24.2",
|
||||
"estree-walker": "^3.0.3",
|
||||
"fast-glob": "^3.3.3",
|
||||
"flattie": "^1.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"html-escaper": "3.0.3",
|
||||
|
@ -153,17 +152,18 @@
|
|||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.17",
|
||||
"magicast": "^0.3.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"mrmime": "^2.0.0",
|
||||
"neotraverse": "^0.6.18",
|
||||
"p-limit": "^6.2.0",
|
||||
"p-queue": "^8.1.0",
|
||||
"picomatch": "^4.0.2",
|
||||
"preferred-pm": "^4.1.1",
|
||||
"prompts": "^2.4.2",
|
||||
"rehype": "^13.0.2",
|
||||
"semver": "^7.7.1",
|
||||
"shiki": "^1.29.2",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"tsconfck": "^3.1.4",
|
||||
"ultrahtml": "^1.5.3",
|
||||
"unifont": "^0.1.7",
|
||||
|
@ -196,7 +196,7 @@
|
|||
"@types/html-escaper": "3.0.4",
|
||||
"@types/http-cache-semantics": "^4.0.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/picomatch": "^3.0.2",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/yargs-parser": "^21.0.3",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @ts-expect-error
|
||||
import { imageConfig } from 'astro:assets';
|
||||
import { isRemotePath } from '@astrojs/internal-helpers/path';
|
||||
import { isRemoteAllowed } from '@astrojs/internal-helpers/remote';
|
||||
import * as mime from 'mrmime';
|
||||
import type { APIRoute } from '../../types/public/common.js';
|
||||
import { getConfiguredImageService } from '../internal.js';
|
||||
import { etag } from '../utils/etag.js';
|
||||
import { isRemoteAllowed } from '../utils/remotePattern.js';
|
||||
|
||||
async function loadRemoteImage(src: URL, headers: Headers) {
|
||||
try {
|
||||
|
|
|
@ -6,11 +6,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|||
// @ts-expect-error
|
||||
import { assetsDir, imageConfig, outDir } from 'astro:assets';
|
||||
import { isRemotePath, removeQueryString } from '@astrojs/internal-helpers/path';
|
||||
import { isRemoteAllowed } from '@astrojs/internal-helpers/remote';
|
||||
import * as mime from 'mrmime';
|
||||
import type { APIRoute } from '../../types/public/common.js';
|
||||
import { getConfiguredImageService } from '../internal.js';
|
||||
import { etag } from '../utils/etag.js';
|
||||
import { isRemoteAllowed } from '../utils/remotePattern.js';
|
||||
|
||||
function replaceFileSystemReferences(src: string) {
|
||||
return os.platform().includes('win32') ? src.replace(/^\/@fs\//, '') : src.replace(/^\/@fs/, '');
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
type UnresolvedImageTransform,
|
||||
isImageMetadata,
|
||||
} from './types.js';
|
||||
import { addCSSVarsToStyle, cssFitValues } from './utils/imageAttributes.js';
|
||||
import { isESMImportedImage, isRemoteImage, resolveSrc } from './utils/imageKind.js';
|
||||
import { inferRemoteSize } from './utils/remoteProbe.js';
|
||||
|
||||
|
@ -151,6 +152,19 @@ export async function getImage(
|
|||
}
|
||||
delete resolvedOptions.priority;
|
||||
delete resolvedOptions.densities;
|
||||
|
||||
if (layout !== 'none') {
|
||||
resolvedOptions.style = addCSSVarsToStyle(
|
||||
{
|
||||
w: String(resolvedOptions.width),
|
||||
h: String(resolvedOptions.height),
|
||||
fit: cssFitValues.includes(resolvedOptions.fit ?? '') && resolvedOptions.fit,
|
||||
pos: resolvedOptions.position,
|
||||
},
|
||||
resolvedOptions.style,
|
||||
);
|
||||
resolvedOptions['data-astro-image'] = layout;
|
||||
}
|
||||
}
|
||||
|
||||
const validatedOptions = service.validateOptions
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { isRemoteAllowed } from '@astrojs/internal-helpers/remote';
|
||||
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
|
||||
import { isRemotePath, joinPaths } from '../../core/path.js';
|
||||
import type { AstroConfig } from '../../types/public/config.js';
|
||||
|
@ -9,7 +10,6 @@ import type {
|
|||
UnresolvedSrcSetValue,
|
||||
} from '../types.js';
|
||||
import { isESMImportedImage, isRemoteImage } from '../utils/imageKind.js';
|
||||
import { isRemoteAllowed } from '../utils/remotePattern.js';
|
||||
|
||||
export type ImageService = LocalImageService | ExternalImageService;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { toStyleString } from '../../runtime/server/render/util.js';
|
||||
import type { GetImageResult, ImageLayout, LocalImageProps, RemoteImageProps } from '../types.js';
|
||||
|
||||
export const cssFitValues = ['fill', 'contain', 'cover', 'scale-down'];
|
||||
|
||||
export function addCSSVarsToStyle(
|
||||
vars: Record<string, string | false | undefined>,
|
||||
|
@ -17,32 +18,3 @@ export function addCSSVarsToStyle(
|
|||
|
||||
return `${cssVars} ${style}`;
|
||||
}
|
||||
|
||||
const cssFitValues = ['fill', 'contain', 'cover', 'scale-down'];
|
||||
|
||||
export function applyResponsiveAttributes<
|
||||
T extends LocalImageProps<unknown> | RemoteImageProps<unknown>,
|
||||
>({
|
||||
layout,
|
||||
image,
|
||||
props,
|
||||
additionalAttributes,
|
||||
}: {
|
||||
layout: Exclude<ImageLayout, 'none'>;
|
||||
image: GetImageResult;
|
||||
additionalAttributes: Record<string, any>;
|
||||
props: T;
|
||||
}) {
|
||||
const attributes = { ...additionalAttributes, ...image.attributes };
|
||||
attributes.style = addCSSVarsToStyle(
|
||||
{
|
||||
w: image.attributes.width ?? props.width ?? image.options.width,
|
||||
h: image.attributes.height ?? props.height ?? image.options.height,
|
||||
fit: cssFitValues.includes(props.fit ?? '') && props.fit,
|
||||
pos: props.position,
|
||||
},
|
||||
attributes.style,
|
||||
);
|
||||
attributes['data-astro-image'] = layout;
|
||||
return attributes;
|
||||
}
|
||||
|
|
|
@ -2,15 +2,6 @@ export { emitESMImage } from './node/emitAsset.js';
|
|||
export { isESMImportedImage, isRemoteImage } from './imageKind.js';
|
||||
export { imageMetadata } from './metadata.js';
|
||||
export { getOrigQueryParams } from './queryParams.js';
|
||||
export {
|
||||
isRemoteAllowed,
|
||||
matchHostname,
|
||||
matchPathname,
|
||||
matchPattern,
|
||||
matchPort,
|
||||
matchProtocol,
|
||||
type RemotePattern,
|
||||
} from './remotePattern.js';
|
||||
export { hashTransform, propsToFilename } from './transformToPath.js';
|
||||
export { inferRemoteSize } from './remoteProbe.js';
|
||||
export { makeSvgComponent } from './svg.js';
|
||||
|
|
|
@ -20,6 +20,10 @@ export async function dev({ flags }: DevOptions) {
|
|||
['--host <custom-address>', `Expose on a network IP address at <custom-address>`],
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
['--force', 'Clear the content layer cache, forcing a full rebuild.'],
|
||||
[
|
||||
'--allowed-hosts',
|
||||
'Specify a comma-separated list of allowed hosts or allow any hostname.',
|
||||
],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
|
|
|
@ -25,6 +25,12 @@ export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig {
|
|||
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
|
||||
open:
|
||||
typeof flags.open === 'string' || typeof flags.open === 'boolean' ? flags.open : undefined,
|
||||
allowedHosts:
|
||||
typeof flags.allowedHosts === 'string'
|
||||
? flags.allowedHosts.split(',')
|
||||
: typeof flags.allowedHosts === 'boolean' && flags.allowedHosts === true
|
||||
? flags.allowedHosts
|
||||
: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ export async function preview({ flags }: PreviewOptions) {
|
|||
['--host', `Listen on all addresses, including LAN and public addresses.`],
|
||||
['--host <custom-address>', `Expose on a network IP address at <custom-address>`],
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
[
|
||||
'--allowed-hosts',
|
||||
'Specify a comma-separated list of allowed hosts or allow any hostname.',
|
||||
],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
|
|
|
@ -5,6 +5,8 @@ import type { ImageServiceConfig } from '../types/public/index.js';
|
|||
|
||||
export { defineConfig, getViteConfig } from './index.js';
|
||||
export { envField } from '../env/config.js';
|
||||
export { mergeConfig } from '../core/config/merge.js';
|
||||
export { validateConfig } from '../core/config/validate.js';
|
||||
export { fontProviders, defineFontProvider } from '../assets/fonts/providers/index.js';
|
||||
|
||||
/**
|
||||
|
|
|
@ -157,6 +157,7 @@ function createManifest(
|
|||
clientDirectives: manifest?.clientDirectives ?? getDefaultClientDirectives(),
|
||||
renderers: renderers ?? manifest?.renderers ?? [],
|
||||
base: manifest?.base ?? ASTRO_CONFIG_DEFAULTS.base,
|
||||
userAssetsBase: manifest?.userAssetsBase ?? '',
|
||||
componentMetadata: manifest?.componentMetadata ?? new Map(),
|
||||
inlinedScripts: manifest?.inlinedScripts ?? new Map(),
|
||||
i18n: manifest?.i18n,
|
||||
|
@ -234,6 +235,7 @@ type AstroContainerManifest = Pick<
|
|||
| 'renderers'
|
||||
| 'assetsPrefix'
|
||||
| 'base'
|
||||
| 'userAssetsBase'
|
||||
| 'routes'
|
||||
| 'assets'
|
||||
| 'entryModules'
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { promises as fs, existsSync } from 'node:fs';
|
||||
import { relative } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import fastGlob from 'fast-glob';
|
||||
import { bold, green } from 'kleur/colors';
|
||||
import micromatch from 'micromatch';
|
||||
import pLimit from 'p-limit';
|
||||
import picomatch from 'picomatch';
|
||||
import { glob as tinyglobby } from 'tinyglobby';
|
||||
import type { ContentEntryRenderFunction, ContentEntryType } from '../../types/public/content.js';
|
||||
import type { RenderedContent } from '../data-store.js';
|
||||
import { getContentEntryIdAndSlug, posixRelative } from '../utils.js';
|
||||
|
@ -236,8 +236,9 @@ export function glob(globOptions: GlobOptions): Loader {
|
|||
logger.warn(`The base directory "${fileURLToPath(baseDir)}" does not exist.`);
|
||||
}
|
||||
|
||||
const files = await fastGlob(globOptions.pattern, {
|
||||
const files = await tinyglobby(globOptions.pattern, {
|
||||
cwd: fileURLToPath(baseDir),
|
||||
expandDirectories: false,
|
||||
});
|
||||
|
||||
if (exists && files.length === 0) {
|
||||
|
@ -321,7 +322,7 @@ export function glob(globOptions: GlobOptions): Loader {
|
|||
watcher.add(filePath);
|
||||
|
||||
const matchesGlob = (entry: string) =>
|
||||
!entry.startsWith('../') && micromatch.isMatch(entry, globOptions.pattern);
|
||||
!entry.startsWith('../') && picomatch.isMatch(entry, globOptions.pattern);
|
||||
|
||||
const basePath = fileURLToPath(baseDir);
|
||||
|
||||
|
|
|
@ -414,13 +414,23 @@ async function updateImageReferencesInBody(html: string, fileName: string) {
|
|||
for (const [_full, imagePath] of html.matchAll(CONTENT_LAYER_IMAGE_REGEX)) {
|
||||
try {
|
||||
const decodedImagePath = JSON.parse(imagePath.replaceAll('"', '"'));
|
||||
const id = imageSrcToImportId(decodedImagePath.src, fileName);
|
||||
|
||||
const imported = imageAssetMap.get(id);
|
||||
if (!id || imageObjects.has(id) || !imported) {
|
||||
continue;
|
||||
let image: GetImageResult;
|
||||
if (URL.canParse(decodedImagePath.src)) {
|
||||
// Remote image, pass through without resolving import
|
||||
// We know we should resolve this remote image because either:
|
||||
// 1. It was collected with the remark-collect-images plugin, which respects the astro image configuration,
|
||||
// 2. OR it was manually injected by another plugin, and we should respect that.
|
||||
image = await getImage(decodedImagePath);
|
||||
} else {
|
||||
const id = imageSrcToImportId(decodedImagePath.src, fileName);
|
||||
|
||||
const imported = imageAssetMap.get(id);
|
||||
if (!id || imageObjects.has(id) || !imported) {
|
||||
continue;
|
||||
}
|
||||
image = await getImage({ ...decodedImagePath, src: imported });
|
||||
}
|
||||
const image: GetImageResult = await getImage({ ...decodedImagePath, src: imported });
|
||||
imageObjects.set(imagePath, image);
|
||||
} catch {
|
||||
throw new Error(`Failed to parse image reference: ${imagePath}`);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import type fsMod from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import glob from 'fast-glob';
|
||||
import { bold, cyan } from 'kleur/colors';
|
||||
import { glob } from 'tinyglobby';
|
||||
import { type ViteDevServer, normalizePath } from 'vite';
|
||||
import { type ZodSchema, z } from 'zod';
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
||||
|
@ -94,21 +94,16 @@ export async function createContentTypesGenerator({
|
|||
}
|
||||
const globResult = await glob('**', {
|
||||
cwd: fileURLToPath(contentPaths.contentDir),
|
||||
fs: {
|
||||
readdir: fs.readdir.bind(fs),
|
||||
readdirSync: fs.readdirSync.bind(fs),
|
||||
},
|
||||
onlyFiles: false,
|
||||
objectMode: true,
|
||||
absolute: true,
|
||||
});
|
||||
|
||||
for (const entry of globResult) {
|
||||
const fullPath = path.join(fileURLToPath(contentPaths.contentDir), entry.path);
|
||||
for (const fullPath of globResult) {
|
||||
const entryURL = pathToFileURL(fullPath);
|
||||
if (entryURL.href.startsWith(contentPaths.config.url.href)) continue;
|
||||
if (entry.dirent.isFile()) {
|
||||
const stat = fs.statSync(fullPath);
|
||||
if (stat.isFile()) {
|
||||
events.push({ name: 'add', entry: entryURL });
|
||||
} else if (entry.dirent.isDirectory()) {
|
||||
} else if (stat.isDirectory()) {
|
||||
events.push({ name: 'addDir', entry: entryURL });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ import nodeFs from 'node:fs';
|
|||
import { extname } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { dataToEsm } from '@rollup/pluginutils';
|
||||
import glob from 'fast-glob';
|
||||
import pLimit from 'p-limit';
|
||||
import { glob } from 'tinyglobby';
|
||||
import type { Plugin, ViteDevServer } from 'vite';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { rootRelativePath } from '../core/viteUtils.js';
|
||||
|
@ -280,7 +280,7 @@ export async function generateLookupMap({
|
|||
{
|
||||
absolute: true,
|
||||
cwd: fileURLToPath(root),
|
||||
fs,
|
||||
expandDirectories: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ export class App {
|
|||
if (!pathname) {
|
||||
pathname = prependForwardSlash(this.removeBase(url.pathname));
|
||||
}
|
||||
let routeData = matchRoute(pathname, this.#manifestData);
|
||||
let routeData = matchRoute(decodeURI(pathname), this.#manifestData);
|
||||
|
||||
// missing routes fall-through, pre rendered are handled by static layer
|
||||
if (!routeData || routeData.prerender) return undefined;
|
||||
|
|
|
@ -48,6 +48,13 @@ export type SSRManifest = {
|
|||
routes: RouteInfo[];
|
||||
site?: string;
|
||||
base: string;
|
||||
/**
|
||||
* The base of the assets generated **by the user**. For example, scripts created by the user falls under this category.
|
||||
*
|
||||
* The value of this field comes from `vite.base`. We aren't usually this tight to vite in our code base, so probably
|
||||
* this should be refactored somehow.
|
||||
*/
|
||||
userAssetsBase: string | undefined;
|
||||
trailingSlash: AstroConfig['trailingSlash'];
|
||||
buildFormat: NonNullable<AstroConfig['build']>['format'];
|
||||
compressHTML: boolean;
|
||||
|
|
|
@ -360,7 +360,7 @@ async function getPathsForRoute(
|
|||
// NOTE: The same URL may match multiple routes in the manifest.
|
||||
// Routing priority needs to be verified here for any duplicate
|
||||
// paths to ensure routing priority rules are enforced in the final build.
|
||||
const matchedRoute = matchRoute(staticPath, options.routesList);
|
||||
const matchedRoute = matchRoute(decodeURI(staticPath), options.routesList);
|
||||
return matchedRoute === route;
|
||||
});
|
||||
|
||||
|
@ -630,6 +630,7 @@ function createBuildManifest(
|
|||
compressHTML: settings.config.compressHTML,
|
||||
renderers,
|
||||
base: settings.config.base,
|
||||
userAssetsBase: settings.config?.vite?.base,
|
||||
assetsPrefix: settings.config.build.assetsPrefix,
|
||||
site: settings.config.site,
|
||||
componentMetadata: internals.componentMetadata,
|
||||
|
|
|
@ -45,7 +45,9 @@ export interface BuildOptions {
|
|||
* Teardown the compiler WASM instance after build. This can improve performance when
|
||||
* building once, but may cause a performance hit if building multiple times in a row.
|
||||
*
|
||||
* @internal only used for testing
|
||||
* When building multiple projects in the same execution (e.g. during tests), disabling
|
||||
* this option can greatly improve performance at the cost of some extra memory usage.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
teardownCompiler?: boolean;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import glob from 'fast-glob';
|
||||
import type { OutputChunk } from 'rollup';
|
||||
import { glob } from 'tinyglobby';
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import { getAssetsPrefix } from '../../../assets/utils/getAssetsPrefix.js';
|
||||
import { normalizeTheLocale } from '../../../i18n/index.js';
|
||||
|
@ -287,6 +287,7 @@ function buildManifest(
|
|||
routes,
|
||||
site: settings.config.site,
|
||||
base: settings.config.base,
|
||||
userAssetsBase: settings.config?.vite?.base,
|
||||
trailingSlash: settings.config.trailingSlash,
|
||||
compressHTML: settings.config.compressHTML,
|
||||
assetsPrefix: settings.config.build.assetsPrefix,
|
||||
|
|
|
@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { teardown } from '@astrojs/compiler';
|
||||
import glob from 'fast-glob';
|
||||
import { bgGreen, black, green } from 'kleur/colors';
|
||||
import { glob } from 'tinyglobby';
|
||||
import * as vite from 'vite';
|
||||
import { type BuildInternals, createBuildInternals } from '../../core/build/internal.js';
|
||||
import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
|
||||
|
@ -247,8 +247,8 @@ async function clientBuild(
|
|||
// Nothing to do if there is no client-side JS.
|
||||
if (!input.size) {
|
||||
// If SSR, copy public over
|
||||
if (ssr) {
|
||||
await copyFiles(settings.config.publicDir, out, true);
|
||||
if (ssr && fs.existsSync(settings.config.publicDir)) {
|
||||
await fs.promises.cp(settings.config.publicDir, out, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -384,31 +384,12 @@ async function cleanServerOutput(
|
|||
.map((fileName) => fs.promises.rm(new URL(fileName, out))),
|
||||
);
|
||||
// Copy assets before cleaning directory if outside root
|
||||
await copyFiles(out, opts.settings.config.outDir, true);
|
||||
await fs.promises.cp(out, opts.settings.config.outDir, { recursive: true, force: true });
|
||||
await fs.promises.rm(out, { recursive: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles = false) {
|
||||
const files = await glob('**/*', {
|
||||
cwd: fileURLToPath(fromFolder),
|
||||
dot: includeDotfiles,
|
||||
});
|
||||
if (files.length === 0) return;
|
||||
return await Promise.all(
|
||||
files.map(async function copyFile(filename) {
|
||||
const from = new URL(filename, fromFolder);
|
||||
const to = new URL(filename, toFolder);
|
||||
const lastFolder = new URL('./', to);
|
||||
return fs.promises.mkdir(lastFolder, { recursive: true }).then(async function fsCopyFile() {
|
||||
const p = await fs.promises.copyFile(from, to, fs.constants.COPYFILE_FICLONE);
|
||||
return p;
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function ssrMoveAssets(opts: StaticBuildOptions) {
|
||||
opts.logger.info('build', 'Rearranging server assets...');
|
||||
const serverRoot =
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { mergeConfig as mergeViteConfig } from 'vite';
|
||||
import type { DeepPartial } from '../../type-utils.js';
|
||||
import type { AstroConfig, AstroInlineConfig } from '../../types/public/index.js';
|
||||
import { arraify, isObject, isURL } from '../util.js';
|
||||
|
||||
function mergeConfigRecursively(
|
||||
|
@ -64,10 +66,9 @@ function mergeConfigRecursively(
|
|||
return merged;
|
||||
}
|
||||
|
||||
export function mergeConfig(
|
||||
defaults: Record<string, any>,
|
||||
overrides: Record<string, any>,
|
||||
isRoot = true,
|
||||
): Record<string, any> {
|
||||
return mergeConfigRecursively(defaults, overrides, isRoot ? '' : '.');
|
||||
export function mergeConfig<C extends AstroConfig | AstroInlineConfig>(
|
||||
defaults: C,
|
||||
overrides: DeepPartial<C>,
|
||||
): C {
|
||||
return mergeConfigRecursively(defaults, overrides, '') as C;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ export const ASTRO_CONFIG_DEFAULTS = {
|
|||
host: false,
|
||||
port: 4321,
|
||||
open: false,
|
||||
allowedHosts: [],
|
||||
},
|
||||
integrations: [],
|
||||
markdown: markdownConfigDefaults,
|
||||
|
@ -218,6 +219,10 @@ export const AstroConfigSchema = z.object({
|
|||
.default(ASTRO_CONFIG_DEFAULTS.server.host),
|
||||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
headers: z.custom<OutgoingHttpHeaders>().optional(),
|
||||
allowedHosts: z
|
||||
.union([z.array(z.string()), z.literal(true)])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts),
|
||||
})
|
||||
.default({}),
|
||||
),
|
||||
|
@ -801,6 +806,10 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
|
|||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
headers: z.custom<OutgoingHttpHeaders>().optional(),
|
||||
streaming: z.boolean().optional().default(true),
|
||||
allowedHosts: z
|
||||
.union([z.array(z.string()), z.literal(true)])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import nodeFs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import glob from 'fast-glob';
|
||||
import { convertPathToPattern } from 'tinyglobby';
|
||||
import * as vite from 'vite';
|
||||
import { crawlFrameworkPkgs } from 'vitefu';
|
||||
import { vitePluginActions, vitePluginUserActions } from '../actions/plugins.js';
|
||||
|
@ -124,7 +124,7 @@ export async function createVite(
|
|||
},
|
||||
});
|
||||
|
||||
const srcDirPattern = glob.convertPathToPattern(fileURLToPath(settings.config.srcDir));
|
||||
const srcDirPattern = convertPathToPattern(fileURLToPath(settings.config.srcDir));
|
||||
const envLoader = createEnvLoader(mode, settings.config);
|
||||
|
||||
// Start with the Vite configuration that Astro core needs
|
||||
|
|
|
@ -56,7 +56,7 @@ export async function createContainer({
|
|||
|
||||
const {
|
||||
base,
|
||||
server: { host, headers, open: serverOpen },
|
||||
server: { host, headers, open: serverOpen, allowedHosts },
|
||||
} = settings.config;
|
||||
|
||||
// serverOpen = true, isRestart = false
|
||||
|
@ -92,7 +92,7 @@ export async function createContainer({
|
|||
const mode = inlineConfig?.mode ?? 'development';
|
||||
const viteConfig = await createVite(
|
||||
{
|
||||
server: { host, headers, open },
|
||||
server: { host, headers, open, allowedHosts },
|
||||
optimizeDeps: {
|
||||
include: rendererClientEntries,
|
||||
},
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
// This is the main entrypoint when importing the `astro` package.
|
||||
|
||||
import type { AstroInlineConfig } from '../types/public/config.js';
|
||||
import { default as _build } from './build/index.js';
|
||||
import { default as _sync } from './sync/index.js';
|
||||
|
||||
export { default as build } from './build/index.js';
|
||||
export { default as dev } from './dev/index.js';
|
||||
export { default as preview } from './preview/index.js';
|
||||
|
||||
/**
|
||||
* Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory.
|
||||
* If SSR is enabled, this will generate the necessary server files to serve your site.
|
||||
*
|
||||
* @experimental The JavaScript API is experimental
|
||||
*/
|
||||
// Wrap `_build` to prevent exposing the second internal options parameter
|
||||
export const build = (inlineConfig: AstroInlineConfig) => _build(inlineConfig);
|
||||
|
||||
/**
|
||||
* Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing,
|
||||
* and defines the `astro:content` module for the Content Collections API.
|
||||
|
|
|
@ -36,6 +36,7 @@ export default async function createStaticPreviewServer(
|
|||
port: settings.config.server.port,
|
||||
headers: settings.config.server.headers,
|
||||
open: settings.config.server.open,
|
||||
allowedHosts: settings.config.server.allowedHosts,
|
||||
},
|
||||
plugins: [vitePluginAstroPreview(settings)],
|
||||
});
|
||||
|
|
|
@ -394,6 +394,7 @@ export class RenderContext {
|
|||
// calling the render() function will populate the object with scripts, styles, etc.
|
||||
const result: SSRResult = {
|
||||
base: manifest.base,
|
||||
userAssetsBase: manifest.userAssetsBase,
|
||||
cancelled: false,
|
||||
clientDirectives,
|
||||
inlinedScripts,
|
||||
|
|
|
@ -5,18 +5,17 @@ import { SERVER_ISLAND_BASE_PREFIX, SERVER_ISLAND_COMPONENT } from '../server-is
|
|||
|
||||
/** Find matching route from pathname */
|
||||
export function matchRoute(pathname: string, manifest: RoutesList): RouteData | undefined {
|
||||
const decodedPathname = decodeURI(pathname);
|
||||
return manifest.routes.find((route) => {
|
||||
return (
|
||||
route.pattern.test(decodedPathname) ||
|
||||
route.fallbackRoutes.some((fallbackRoute) => fallbackRoute.pattern.test(decodedPathname))
|
||||
route.pattern.test(pathname) ||
|
||||
route.fallbackRoutes.some((fallbackRoute) => fallbackRoute.pattern.test(pathname))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/** Finds all matching routes from pathname */
|
||||
export function matchAllRoutes(pathname: string, manifest: RoutesList): RouteData[] {
|
||||
return manifest.routes.filter((route) => route.pattern.test(decodeURI(pathname)));
|
||||
return manifest.routes.filter((route) => route.pattern.test(pathname));
|
||||
}
|
||||
|
||||
const ROUTE404_RE = /^\/404\/?$/;
|
||||
|
|
|
@ -5,7 +5,12 @@ import { shouldAppendForwardSlash } from '../build/util.js';
|
|||
import { originPathnameSymbol } from '../constants.js';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import type { Logger } from '../logger/core.js';
|
||||
import { appendForwardSlash, removeTrailingForwardSlash } from '../path.js';
|
||||
import {
|
||||
appendForwardSlash,
|
||||
joinPaths,
|
||||
prependForwardSlash,
|
||||
removeTrailingForwardSlash,
|
||||
} from '../path.js';
|
||||
import { createRequest } from '../request.js';
|
||||
import { DEFAULT_404_ROUTE } from './astro-designed-error-pages.js';
|
||||
|
||||
|
@ -45,14 +50,31 @@ export function findRouteToRewrite({
|
|||
} else {
|
||||
newUrl = new URL(payload, new URL(request.url).origin);
|
||||
}
|
||||
|
||||
let pathname = newUrl.pathname;
|
||||
|
||||
const shouldAppendSlash = shouldAppendForwardSlash(trailingSlash, buildFormat);
|
||||
|
||||
if (base !== '/' && newUrl.pathname.startsWith(base)) {
|
||||
pathname = shouldAppendForwardSlash(trailingSlash, buildFormat)
|
||||
pathname = shouldAppendSlash
|
||||
? appendForwardSlash(newUrl.pathname)
|
||||
: removeTrailingForwardSlash(newUrl.pathname);
|
||||
pathname = pathname.slice(base.length);
|
||||
}
|
||||
|
||||
if (!pathname.startsWith('/') && shouldAppendSlash && newUrl.pathname.endsWith('/')) {
|
||||
// when base is in the rewrite call and trailingSlash is 'always' this is needed or it will 404.
|
||||
pathname = prependForwardSlash(pathname);
|
||||
}
|
||||
|
||||
if (pathname === '/' && base !== '/' && !shouldAppendSlash) {
|
||||
// when rewriting to index and trailingSlash is 'never' this is needed or it will 404
|
||||
// in this case the pattern will look for '/^$/' so '/' will never match
|
||||
pathname = '';
|
||||
}
|
||||
|
||||
newUrl.pathname = joinPaths(...[base, pathname].filter(Boolean));
|
||||
|
||||
const decodedPathname = decodeURI(pathname);
|
||||
let foundRoute;
|
||||
for (const route of routes) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { AddressInfo } from 'node:net';
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { bold } from 'kleur/colors';
|
||||
import type { InlineConfig, ViteDevServer } from 'vite';
|
||||
import { mergeConfig as mergeViteConfig } from 'vite';
|
||||
import astroIntegrationActionsRouteHandler from '../actions/integration.js';
|
||||
import { isActionsFilePresent } from '../actions/utils.js';
|
||||
import { CONTENT_LAYER_TYPE } from '../content/consts.js';
|
||||
|
@ -197,7 +198,7 @@ export async function runHookConfigSetup({
|
|||
updatedSettings.scripts.push({ stage, content });
|
||||
},
|
||||
updateConfig: (newConfig) => {
|
||||
updatedConfig = mergeConfig(updatedConfig, newConfig) as AstroConfig;
|
||||
updatedConfig = mergeConfig(updatedConfig, newConfig);
|
||||
return { ...updatedConfig };
|
||||
},
|
||||
injectRoute: (injectRoute) => {
|
||||
|
@ -511,7 +512,7 @@ export async function runHookBuildSetup({
|
|||
pages,
|
||||
target,
|
||||
updateConfig: (newConfig) => {
|
||||
updatedConfig = mergeConfig(updatedConfig, newConfig);
|
||||
updatedConfig = mergeViteConfig(updatedConfig, newConfig);
|
||||
return { ...updatedConfig };
|
||||
},
|
||||
logger: getLogger(integration, logger),
|
||||
|
|
|
@ -27,6 +27,10 @@ export function renderAllHeadContent(result: SSRResult) {
|
|||
const scripts = Array.from(result.scripts)
|
||||
.filter(uniqueElements)
|
||||
.map((script) => {
|
||||
if (result.userAssetsBase) {
|
||||
script.props.src =
|
||||
(result.base === '/' ? '' : result.base) + result.userAssetsBase + script.props.src;
|
||||
}
|
||||
return renderElement('script', script, false);
|
||||
});
|
||||
const links = Array.from(result.links)
|
||||
|
|
|
@ -20,5 +20,7 @@ export async function renderScript(result: SSRResult, id: string) {
|
|||
}
|
||||
|
||||
const resolved = await result.resolve(id);
|
||||
return markHTMLString(`<script type="module" src="${resolved}"></script>`);
|
||||
return markHTMLString(
|
||||
`<script type="module" src="${result.userAssetsBase ? (result.base === '/' ? '' : result.base) + result.userAssetsBase : ''}${resolved}"></script>`,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -398,7 +398,9 @@ async function transition(
|
|||
// Note: getNamedItem can return null in real life, even if TypeScript doesn't think so, hence
|
||||
// the ?.
|
||||
init.body =
|
||||
form?.attributes.getNamedItem('enctype')?.value === 'application/x-www-form-urlencoded'
|
||||
from !== undefined &&
|
||||
Reflect.get(HTMLFormElement.prototype, 'attributes', form).getNamedItem('enctype')
|
||||
?.value === 'application/x-www-form-urlencoded'
|
||||
? new URLSearchParams(preparationEvent.formData as any)
|
||||
: preparationEvent.formData;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { OutgoingHttpHeaders } from 'node:http';
|
||||
import type { RemotePattern } from '@astrojs/internal-helpers/remote';
|
||||
import type {
|
||||
RehypePlugins,
|
||||
RemarkPlugins,
|
||||
|
@ -8,7 +9,6 @@ import type {
|
|||
import type { BuiltinDriverName, BuiltinDriverOptions, Driver, Storage } from 'unstorage';
|
||||
import type { UserConfig as OriginalViteUserConfig, SSROptions as ViteSSROptions } from 'vite';
|
||||
import type { ImageFit, ImageLayout } from '../../assets/types.js';
|
||||
import type { RemotePattern } from '../../assets/utils/remotePattern.js';
|
||||
import type { SvgRenderMode } from '../../assets/utils/svg.js';
|
||||
import type { AssetsPrefix } from '../../core/app/types.js';
|
||||
import type { AstroConfigType } from '../../core/config/schema.js';
|
||||
|
@ -72,6 +72,26 @@ export type ServerConfig = {
|
|||
*/
|
||||
port?: number;
|
||||
|
||||
/**
|
||||
* @name server.allowedHosts
|
||||
* @type {string[] | true}
|
||||
* @default `[]`
|
||||
* @version 5.4.0
|
||||
* @description
|
||||
*
|
||||
* A list of hostnames that Astro is allowed to respond to. When the value is set to `true`, any
|
||||
* hostname is allowed.
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* server: {
|
||||
* allowedHosts: ['staging.example.com', 'qa.example.com']
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
allowedHosts?: string[] | true;
|
||||
|
||||
/**
|
||||
* @name server.headers
|
||||
* @typeraw {OutgoingHttpHeaders}
|
||||
|
|
|
@ -14,6 +14,7 @@ export type * from './manifest.js';
|
|||
export type { AstroIntegrationLogger } from '../../core/logger/core.js';
|
||||
export type { ToolbarServerHelpers } from '../../runtime/client/dev-toolbar/helpers.js';
|
||||
|
||||
export type { RemotePattern } from '@astrojs/internal-helpers/remote';
|
||||
export type {
|
||||
MarkdownHeading,
|
||||
RehypePlugins,
|
||||
|
@ -35,7 +36,6 @@ export type {
|
|||
ImageTransform,
|
||||
UnresolvedImageTransform,
|
||||
} from '../../assets/types.js';
|
||||
export type { RemotePattern } from '../../assets/utils/remotePattern.js';
|
||||
export type { AssetsPrefix, SSRManifest } from '../../core/app/types.js';
|
||||
export type {
|
||||
AstroCookieGetOptions,
|
||||
|
|
|
@ -214,6 +214,7 @@ export interface SSRResult {
|
|||
*/
|
||||
cancelled: boolean;
|
||||
base: string;
|
||||
userAssetsBase: string | undefined;
|
||||
styles: Set<SSRElement>;
|
||||
scripts: Set<SSRElement>;
|
||||
links: Set<SSRElement>;
|
||||
|
|
|
@ -190,6 +190,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
|
|||
clientDirectives: settings.clientDirectives,
|
||||
renderers: [],
|
||||
base: settings.config.base,
|
||||
userAssetsBase: settings.config?.vite?.base,
|
||||
assetsPrefix: settings.config.build.assetsPrefix,
|
||||
site: settings.config.site,
|
||||
componentMetadata: new Map(),
|
||||
|
|
|
@ -18,7 +18,10 @@ export const markdownContentEntryType: ContentEntryType = {
|
|||
handlePropagation: true,
|
||||
|
||||
async getRenderFunction(config) {
|
||||
const processor = await createMarkdownProcessor(config.markdown);
|
||||
const processor = await createMarkdownProcessor({
|
||||
image: config.image,
|
||||
...config.markdown,
|
||||
});
|
||||
return async function renderToString(entry) {
|
||||
// Process markdown even if it's empty as remark/rehype plugins may add content or frontmatter dynamically
|
||||
const result = await processor.render(entry.body ?? '', {
|
||||
|
@ -28,7 +31,10 @@ export const markdownContentEntryType: ContentEntryType = {
|
|||
});
|
||||
return {
|
||||
html: result.code,
|
||||
metadata: result.metadata,
|
||||
metadata: {
|
||||
...result.metadata,
|
||||
imagePaths: result.metadata.localImagePaths.concat(result.metadata.remoteImagePaths),
|
||||
},
|
||||
};
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
export type MarkdownImagePath = { raw: string; safeName: string };
|
||||
|
||||
export function getMarkdownCodeForImages(imagePaths: MarkdownImagePath[], html: string) {
|
||||
export function getMarkdownCodeForImages(
|
||||
localImagePaths: MarkdownImagePath[],
|
||||
remoteImagePaths: string[],
|
||||
html: string,
|
||||
) {
|
||||
return `
|
||||
import { getImage } from "astro:assets";
|
||||
${imagePaths
|
||||
${localImagePaths
|
||||
.map((entry) => `import Astro__${entry.safeName} from ${JSON.stringify(entry.raw)};`)
|
||||
.join('\n')}
|
||||
|
||||
const images = async function(html) {
|
||||
const imageSources = {};
|
||||
${imagePaths
|
||||
${localImagePaths
|
||||
.map((entry) => {
|
||||
const rawUrl = JSON.stringify(entry.raw);
|
||||
return `{
|
||||
|
@ -29,6 +33,25 @@ export function getMarkdownCodeForImages(imagePaths: MarkdownImagePath[], html:
|
|||
}`;
|
||||
})
|
||||
.join('\n')}
|
||||
${remoteImagePaths
|
||||
.map((raw) => {
|
||||
const rawUrl = JSON.stringify(raw);
|
||||
return `{
|
||||
const regex = new RegExp('__ASTRO_IMAGE_="([^"]*' + ${rawUrl.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
'\\\\$&',
|
||||
)} + '[^"]*)"', 'g');
|
||||
let match;
|
||||
let occurrenceCounter = 0;
|
||||
while ((match = regex.exec(html)) !== null) {
|
||||
const matchKey = ${rawUrl} + '_' + occurrenceCounter;
|
||||
const props = JSON.parse(match[1].replace(/"/g, '"'));
|
||||
imageSources[matchKey] = await getImage(props);
|
||||
occurrenceCounter++;
|
||||
}
|
||||
}`;
|
||||
})
|
||||
.join('\n')}
|
||||
return imageSources;
|
||||
};
|
||||
|
||||
|
|
|
@ -60,7 +60,10 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug
|
|||
|
||||
// Lazily initialize the Markdown processor
|
||||
if (!processor) {
|
||||
processor = createMarkdownProcessor(settings.config.markdown);
|
||||
processor = createMarkdownProcessor({
|
||||
image: settings.config.image,
|
||||
...settings.config.markdown,
|
||||
});
|
||||
}
|
||||
|
||||
const renderResult = await (await processor).render(raw.content, {
|
||||
|
@ -75,16 +78,21 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug
|
|||
}
|
||||
|
||||
let html = renderResult.code;
|
||||
const { headings, imagePaths: rawImagePaths, frontmatter } = renderResult.metadata;
|
||||
const {
|
||||
headings,
|
||||
localImagePaths: rawLocalImagePaths,
|
||||
remoteImagePaths,
|
||||
frontmatter,
|
||||
} = renderResult.metadata;
|
||||
|
||||
// Add default charset for markdown pages
|
||||
const isMarkdownPage = isPage(fileURL, settings);
|
||||
const charset = isMarkdownPage ? '<meta charset="utf-8">' : '';
|
||||
|
||||
// Resolve all the extracted images from the content
|
||||
const imagePaths: MarkdownImagePath[] = [];
|
||||
for (const imagePath of rawImagePaths) {
|
||||
imagePaths.push({
|
||||
const localImagePaths: MarkdownImagePath[] = [];
|
||||
for (const imagePath of rawLocalImagePaths) {
|
||||
localImagePaths.push({
|
||||
raw: imagePath,
|
||||
safeName: shorthash(imagePath),
|
||||
});
|
||||
|
@ -108,8 +116,8 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug
|
|||
|
||||
${
|
||||
// Only include the code relevant to `astro:assets` if there's images in the file
|
||||
imagePaths.length > 0
|
||||
? getMarkdownCodeForImages(imagePaths, html)
|
||||
localImagePaths.length > 0 || remoteImagePaths.length > 0
|
||||
? getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html)
|
||||
: `const html = () => ${JSON.stringify(html)};`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { after, before, describe, it } from 'node:test';
|
||||
import { load as cheerioLoad } from 'cheerio';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Astro dev headers', () => {
|
||||
|
@ -38,3 +39,36 @@ describe('Astro dev headers', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Astro dev with vite.base path', () => {
|
||||
let fixture;
|
||||
let devServer;
|
||||
const headers = {
|
||||
'x-astro': 'test',
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/astro-dev-headers/',
|
||||
server: {
|
||||
headers,
|
||||
},
|
||||
vite: {
|
||||
base: '/hello',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await devServer.stop();
|
||||
});
|
||||
|
||||
it('Generated script src get included with vite.base path', async () => {
|
||||
const result = await fixture.fetch('/hello');
|
||||
const html = await result.text();
|
||||
const $ = cheerioLoad(html);
|
||||
assert.match($('script').attr('src'), /^\/hello\/@vite\/client$/);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"@astrojs/node": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/node": "^9.0.2"
|
||||
"@astrojs/node": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
4
packages/astro/test/fixtures/rewrite-trailing-slash-never/src/pages/bar.astro
vendored
Normal file
4
packages/astro/test/fixtures/rewrite-trailing-slash-never/src/pages/bar.astro
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
return Astro.rewrite("/")
|
||||
---
|
||||
<h1>hi</h1>
|
|
@ -4,5 +4,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<h1>Index</h1>
|
||||
<p>{Astro.url.pathname}</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue