The web framework for content-driven websites. ⭐️ Star to support our work!
OTHER License
Published by astrobot-houston 5 months ago
d0248bc
Thanks @dkobierski! - Fixes suppressed logs when error occursPublished by astrobot-houston 5 months ago
#10974 2668ef9
Thanks @florian-lefebvre! - Adds experimental support for the astro:env
API.
The astro:env
API lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate /client
or /server
module:
---
import { PUBLIC_APP_ID } from 'astro:env/client';
import { PUBLIC_API_URL, getSecret } from 'astro:env/server';
const API_TOKEN = getSecret('API_TOKEN');
const data = await fetch(`${PUBLIC_API_URL}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${API_TOKEN}`,
},
body: JSON.stringify({ appId: PUBLIC_APP_ID }),
});
---
To define the data type and properties of your environment variables, declare a schema in your Astro config in experimental.env.schema
. The envField
helper allows you define your variable as a string, number, or boolean and pass properties in an object:
// astro.config.mjs
import { defineConfig, envField } from 'astro/config';
export default defineConfig({
experimental: {
env: {
schema: {
PUBLIC_API_URL: envField.string({ context: 'client', access: 'public', optional: true }),
PUBLIC_PORT: envField.number({ context: 'server', access: 'public', default: 4321 }),
API_SECRET: envField.string({ context: 'server', access: 'secret' }),
},
},
},
});
There are three kinds of environment variables, determined by the combination of context
(client
or server
) and access
(private
or public
) settings defined in your env.schema
:
Public client variables: These variables end up in both your final client and server bundles, and can be accessed from both client and server through the astro:env/client
module:
import { PUBLIC_API_URL } from 'astro:env/client';
Public server variables: These variables end up in your final server bundle and can be accessed on the server through the astro:env/server
module:
import { PUBLIC_PORT } from 'astro:env/server';
Secret server variables: These variables are not part of your final bundle and can be accessed on the server through the getSecret()
helper function available from the astro:env/server
module:
import { getSecret } from 'astro:env/server';
const API_SECRET = getSecret('API_SECRET'); // typed
const SECRET_NOT_IN_SCHEMA = getSecret('SECRET_NOT_IN_SCHEMA'); // string | undefined
Note: Secret client variables are not supported because there is no safe way to send this data to the client. Therefore, it is not possible to configure both context: "client"
and access: "secret"
in your schema.
To learn more, check out the documentation.
#11192 58b10a0
Thanks @liruifengv! - Improves DX by throwing the original AstroUserError
when an error is thrown inside a .mdx
file.
#11136 35ef53c
Thanks @ematipico! - Errors that are emitted during a rewrite are now bubbled up and shown to the user. A 404 response is not returned anymore.
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
#11144 803dd80
Thanks @ematipico! - BREAKING CHANGE to the experimental Container API only
Changes the type of the renderers
option of the AstroContainer::create
function and adds a dedicated function loadRenderers()
to load the rendering scripts from renderer integration packages (@astrojs/react
, @astrojs/preact
, @astrojs/solid-js
, @astrojs/svelte
, @astrojs/vue
, @astrojs/lit
, and @astrojs/mdx
).
You no longer need to know the individual, direct file paths to the client and server rendering scripts for each renderer integration package. Now, there is a dedicated function to load the renderer from each package, which is available from getContainerRenderer()
:
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from "astro:container";
import { getContainerRenderer } from "@astrojs/react";
test('ReactWrapper with react renderer', async () => {
+ const renderers = await loadRenderers([getContainerRenderer()])
- const renderers = [
- {
- name: '@astrojs/react',
- clientEntrypoint: '@astrojs/react/client.js',
- serverEntrypoint: '@astrojs/react/server.js',
- },
- ];
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
The new loadRenderers()
helper function is available from astro:container
, a virtual module that can be used when running the Astro container inside vite
.
#11136 35ef53c
Thanks @ematipico! - It's not possible anymore to use Astro.rewrite("/404")
inside static pages. This isn't counterproductive because Astro will end-up emitting a page that contains the HTML of 404 error page.
It's still possible to use Astro.rewrite("/404")
inside on-demand pages, or pages that opt-out from prerendering.
#11191 6e29a17
Thanks @matthewp! - Fixes a case where Astro.url
would be incorrect when having build.format
set to 'preserve'
in the Astro config
#11182 40b0b4d
Thanks @ematipico! - Fixes an issue where Astro.rewrite
wasn't carrying over the body of a Request
in on-demand pages.
#11194 97fbe93
Thanks @ematipico! - Fixes an issue where the function getViteConfig
wasn't returning the correct merged Astro configuration
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11144 803dd80
Thanks @ematipico! - The integration now exposes a function called getContainerRenderer
, that can be used inside the Container APIs to load the relative renderer.
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ReactWrapper from '../src/components/ReactWrapper.astro';
import { loadRenderers } from 'astro:container';
import { getContainerRenderer } from '@astrojs/react';
test('ReactWrapper with react renderer', async () => {
const renderers = await loadRenderers([getContainerRenderer()]);
const container = await AstroContainer.create({
renderers,
});
const result = await container.renderToString(ReactWrapper);
expect(result).toContain('Counter');
expect(result).toContain('Count: <!-- -->5');
});
Published by astrobot-houston 5 months ago
#11171 ff8004f
Thanks @Princesseuh! - Guard globalThis.astroAsset usage in proxy code to avoid errors in wonky situations
#11178 1734c49
Thanks @theoephraim! - Improves isPromise
utility to check the presence of then
on an object before trying to access it - which can cause undesired side-effects on Proxy objects
#11183 3cfa2ac
Thanks @66Leo66! - Suggest pnpm dlx
instead of pnpx
in update check.
#11147 2d93902
Thanks @kitschpatrol! - Fixes invalid MIME types in Picture source elements for jpg and svg extensions, which was preventing otherwise valid source variations from being shown by the browser
#11141 19df89f
Thanks @ematipico! - Fixes an internal error that prevented the AstroContainer
to render the Content
component.
You can now write code similar to the following to render content collections:
const entry = await getEntry(collection, slug);
const { Content } = await entry.render();
const content = await container.renderToString(Content);
#11170 ba20c71
Thanks @matthewp! - Retain client scripts in content cache
Published by astrobot-houston 5 months ago
cf9b2ff
Thanks @scottnath! - Removes deprecated template
attribute and replaces deprecated domparser functionPublished by astrobot-houston 5 months ago
#11138 98e0372
Thanks @ematipico! - You can now pass props
when rendering a component using the Container APIs:
import { experimental_AstroContainer as AstroContainer } from 'astro/contaienr';
import Card from '../src/components/Card.astro';
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
props: {
someState: true,
},
});
Published by astrobot-houston 5 months ago
aaf0635
Thanks @Princesseuh! - Fixes @astrojs/upgrade not using the package manager that was used to install the project to install dependenciesPublished by astrobot-houston 5 months ago
Published by astrobot-houston 5 months ago
#11051 12a1bcc
Thanks @ematipico! - Introduces an experimental Container API to render .astro
components in isolation.
This API introduces three new functions to allow you to create a new container and render an Astro component returning either a string or a Response:
create()
: creates a new instance of the container.renderToString()
: renders a component and return a string.renderToResponse()
: renders a component and returns the Response
emitted by the rendering phase.The first supported use of this new API is to enable unit testing. For example, with vitest
, you can create a container to render your component with test data and check the result:
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { expect, test } from 'vitest';
import Card from '../src/components/Card.astro';
test('Card with slots', async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
slots: {
default: 'Card content',
},
});
expect(result).toContain('This is a card');
expect(result).toContain('Card content');
});
For a complete reference, see the Container API docs.
For a feature overview, and to give feedback on this experimental API, see the Container API roadmap discussion.
#11021 2d4c8fa
Thanks @ematipico! - The CSRF protection feature that was introduced behind a flag in v4.6.0 is no longer experimental and is available for general use.
To enable the stable version, add the new top-level security
option in astro.config.mjs
. If you were previously using the experimental version of this feature, also delete the experimental flag:
export default defineConfig({
- experimental: {
- security: {
- csrfProtection: {
- origin: true
- }
- }
- },
+ security: {
+ checkOrigin: true
+ }
})
Enabling this setting performs a check that the "origin"
header, automatically passed by all modern browsers, matches the URL sent by each Request.
This check is executed only for pages rendered on demand, and only for the requests POST
, PATCH
, DELETE
and PUT
with one of the following "content-type"
headers: 'application/x-www-form-urlencoded'
, 'multipart/form-data'
, 'text/plain'
.
If the "origin"
header doesn't match the pathname of the request, Astro will return a 403 status code and won't render the page.
For more information, see the security
configuration docs.
#11022 be68ab4
Thanks @ematipico! - The i18nDomains
routing feature introduced behind a flag in v3.4.0 is no longer experimental and is available for general use.
This routing option allows you to configure different domains for individual locales in entirely server-rendered projects using the @astrojs/node or @astrojs/vercel adapter with a site
configured.
If you were using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro'
export default defineConfig({
- experimental: {
- i18nDomains: true,
- }
})
If you have been waiting for stabilization before using this routing option, you can now do so.
Please see the internationalization docs for more about this feature.
#11071 8ca7c73
Thanks @bholmesdev! - Adds two new functions experimental_getActionState()
and experimental_withState()
to support the React 19 useActionState()
hook when using Astro Actions. This introduces progressive enhancement when calling an Action with the withState()
utility.
This example calls a like
action that accepts a postId
and returns the number of likes. Pass this action to the experimental_withState()
function to apply progressive enhancement info, and apply to useActionState()
to track the result:
import { actions } from 'astro:actions';
import { experimental_withState } from '@astrojs/react/actions';
export function Like({ postId }: { postId: string }) {
const [state, action, pending] = useActionState(
experimental_withState(actions.like),
0 // initial likes
);
return (
<form action={action}>
<input type="hidden" name="postId" value={postId} />
<button disabled={pending}>{state} ❤️</button>
</form>
);
}
You can also access the state stored by useActionState()
from your action handler
. Call experimental_getActionState()
with the API context, and optionally apply a type to the result:
import { defineAction, z } from 'astro:actions';
import { experimental_getActionState } from '@astrojs/react/actions';
export const server = {
like: defineAction({
input: z.object({
postId: z.string(),
}),
handler: async ({ postId }, ctx) => {
const currentLikes = experimental_getActionState<number>(ctx);
// write to database
return currentLikes + 1;
},
}),
};
#11101 a6916e4
Thanks @linguofeng! - Updates Astro's code for adapters to use the header x-forwarded-for
to initialize the clientAddress
.
To take advantage of the new change, integration authors must upgrade the version of Astro in their adapter peerDependencies
to 4.9.0
.
#11071 8ca7c73
Thanks @bholmesdev! - Adds compatibility for Astro Actions in the React 19 beta. Actions can be passed to a form action
prop directly, and Astro will automatically add metadata for progressive enhancement.
import { actions } from 'astro:actions';
function Like() {
return (
<form action={actions.like}>
{/* auto-inserts hidden input for progressive enhancement */}
<button type="submit">Like</button>
</form>
);
}
#11088 9566fa0
Thanks @bholmesdev! - Allow actions to be called on the server. This allows you to call actions as utility functions in your Astro frontmatter, endpoints, and server-side UI components.
Import and call directly from astro:actions
as you would for client actions:
---
// src/pages/blog/[postId].astro
import { actions } from 'astro:actions';
await actions.like({ postId: Astro.params.postId });
---
#11112 29a8650
Thanks @bholmesdev! - Deprecate the getApiContext()
function. API Context can now be accessed from the second parameter to your Action handler()
:
// src/actions/index.ts
import {
defineAction,
z,
- getApiContext,
} from 'astro:actions';
export const server = {
login: defineAction({
input: z.object({ id: z.string }),
+ handler(input, context) {
const user = context.locals.auth(input.id);
return user;
}
}),
}
Published by astrobot-houston 5 months ago
Published by astrobot-houston 5 months ago
#11055 b92de22
Thanks @niklas-wortmann! - Updates the devtools
type to allow passing VueDevToolsOptions
For more customization, you can pass options that the Vue DevTools Vite Plugin supports. (Note: appendTo
is not supported.) For example, you can set launchEditor
to your preferred editor if you are not using Visual Studio Code:
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [
vue({
devtools: { launchEditor: 'webstorm' },
}),
],
});
Published by astrobot-houston 5 months ago
#11071 8ca7c73
Thanks @bholmesdev! - Adds two new functions experimental_getActionState()
and experimental_withState()
to support the React 19 useActionState()
hook when using Astro Actions. This introduces progressive enhancement when calling an Action with the withState()
utility.
This example calls a like
action that accepts a postId
and returns the number of likes. Pass this action to the experimental_withState()
function to apply progressive enhancement info, and apply to useActionState()
to track the result:
import { actions } from 'astro:actions';
import { experimental_withState } from '@astrojs/react/actions';
export function Like({ postId }: { postId: string }) {
const [state, action, pending] = useActionState(
experimental_withState(actions.like),
0 // initial likes
);
return (
<form action={action}>
<input type="hidden" name="postId" value={postId} />
<button disabled={pending}>{state} ❤️</button>
</form>
);
}
You can also access the state stored by useActionState()
from your action handler
. Call experimental_getActionState()
with the API context, and optionally apply a type to the result:
import { defineAction, z } from 'astro:actions';
import { experimental_getActionState } from '@astrojs/react/actions';
export const server = {
like: defineAction({
input: z.object({
postId: z.string(),
}),
handler: async ({ postId }, ctx) => {
const currentLikes = experimental_getActionState<number>(ctx);
// write to database
return currentLikes + 1;
},
}),
};
Published by astrobot-houston 5 months ago
#11073 f5c8fee
Thanks @matthewp! - Prevent cache content from being left in dist folder
When contentCollectionsCache
is enabled temporary cached content is copied into the outDir
for processing. This fixes it so that this content is cleaned out, along with the rest of the temporary build JS.
#11054 f6b171e
Thanks @bholmesdev! - Respect error status when handling Actions with a progressive fallback.
#11092 bfe9c73
Thanks @duckycoding-dev! - Change slot
attribute of IntrinsicAttributes
to match the definition of HTMLAttributes
's own slot
attribute of type string | undefined | null
#10875 b5f95b2
Thanks @W1M0R! - Fixes a typo in a JSDoc annotation
#11111 a5d79dd
Thanks @bholmesdev! - Fix unexpected headers
warning on prerendered routes when using Astro Actions.
#11081 af42e05
Thanks @V3RON! - Correctly position inspection tooltip in RTL mode
When RTL mode is turned on, the inspection tooltip tend to overflow the window on the left side.
Additional check has been added to prevent that.
Published by astrobot-houston 5 months ago