From f5f71094ec74961b4cca2ee451798abd830c617a Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 8 Nov 2024 16:07:40 +0100 Subject: [PATCH 01/20] fix: error overlay message escape (#12305) Co-authored-by: Princesseuh <3019731+Princesseuh@users.noreply.github.com> --- .changeset/breezy-plums-clap.md | 5 +++++ packages/astro/src/core/errors/dev/vite.ts | 2 ++ packages/astro/src/core/module-loader/vite.ts | 21 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 .changeset/breezy-plums-clap.md diff --git a/.changeset/breezy-plums-clap.md b/.changeset/breezy-plums-clap.md new file mode 100644 index 000000000000..d6d3f4b26aa8 --- /dev/null +++ b/.changeset/breezy-plums-clap.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes a case where the error overlay would not escape the message diff --git a/packages/astro/src/core/errors/dev/vite.ts b/packages/astro/src/core/errors/dev/vite.ts index 56688877a861..9063e45b7079 100644 --- a/packages/astro/src/core/errors/dev/vite.ts +++ b/packages/astro/src/core/errors/dev/vite.ts @@ -105,6 +105,7 @@ export function enhanceViteSSRError({ } export interface AstroErrorPayload { + __isEnhancedAstroErrorPayload: true; type: ErrorPayload['type']; err: Omit & { name?: string; @@ -164,6 +165,7 @@ export async function getViteErrorPayload(err: ErrorWithMetadata): Promise { + events.emit('hmr-error', { + type: 'error', + err: { + message: payload.err.message, + stack: payload.err.stack, + }, + }); + + args[0] = payload; + _wsSend.apply(this, args); + }); + return; + } events.emit('hmr-error', msg); } _wsSend.apply(this, args); From 823e73b164eab4115af31b1de8e978f2b4e0a95d Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 8 Nov 2024 15:55:53 +0000 Subject: [PATCH 02/20] fix(actions): better runtime check for invalid usages (#12402) --- .changeset/dull-lemons-check.md | 14 ++++++++++++++ packages/astro/src/actions/runtime/middleware.ts | 3 ++- packages/astro/src/actions/runtime/route.ts | 3 ++- packages/astro/src/actions/runtime/utils.ts | 7 +++++++ .../astro/src/actions/runtime/virtual/server.ts | 10 ++++++++-- packages/astro/src/actions/utils.ts | 3 ++- packages/astro/test/actions.test.js | 6 ++++++ .../test/fixtures/actions/src/pages/invalid.astro | 6 ++++++ 8 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 .changeset/dull-lemons-check.md create mode 100644 packages/astro/test/fixtures/actions/src/pages/invalid.astro diff --git a/.changeset/dull-lemons-check.md b/.changeset/dull-lemons-check.md new file mode 100644 index 000000000000..c2c51c412088 --- /dev/null +++ b/.changeset/dull-lemons-check.md @@ -0,0 +1,14 @@ +--- +'astro': patch +--- + +Fixes a case where Astro allowed to call an action without using `Astro.callAction`. This is now invalid, and Astro will show a proper error. + +```diff +--- +import { actions } from "astro:actions"; + +-const result = actions.getUser({ userId: 123 }); ++const result = Astro.callAction(actions.getUser, { userId: 123 }); +--- +``` diff --git a/packages/astro/src/actions/runtime/middleware.ts b/packages/astro/src/actions/runtime/middleware.ts index 26966553149a..fdaac0b47785 100644 --- a/packages/astro/src/actions/runtime/middleware.ts +++ b/packages/astro/src/actions/runtime/middleware.ts @@ -3,7 +3,7 @@ import type { APIContext, MiddlewareNext } from '../../@types/astro.js'; import { defineMiddleware } from '../../core/middleware/index.js'; import { getOriginPathname } from '../../core/routing/rewrite.js'; import { ACTION_QUERY_PARAMS } from '../consts.js'; -import { formContentTypes, hasContentType } from './utils.js'; +import { ACTION_API_CONTEXT_SYMBOL, formContentTypes, hasContentType } from './utils.js'; import { getAction } from './virtual/get-action.js'; import { type SafeResult, @@ -100,6 +100,7 @@ async function handlePost({ formData = await request.clone().formData(); } const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context; + Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true); const action = baseAction.bind(actionAPIContext); const actionResult = await action(formData); diff --git a/packages/astro/src/actions/runtime/route.ts b/packages/astro/src/actions/runtime/route.ts index 103936d72005..685025b19042 100644 --- a/packages/astro/src/actions/runtime/route.ts +++ b/packages/astro/src/actions/runtime/route.ts @@ -1,5 +1,5 @@ import type { APIRoute } from '../../@types/astro.js'; -import { formContentTypes, hasContentType } from './utils.js'; +import { ACTION_API_CONTEXT_SYMBOL, formContentTypes, hasContentType } from './utils.js'; import { getAction } from './virtual/get-action.js'; import { serializeActionResult } from './virtual/shared.js'; @@ -28,6 +28,7 @@ export const POST: APIRoute = async (context) => { return new Response(null, { status: 415 }); } const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context; + Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true); const action = baseAction.bind(actionAPIContext); const result = await action(args); const serialized = serializeActionResult(result); diff --git a/packages/astro/src/actions/runtime/utils.ts b/packages/astro/src/actions/runtime/utils.ts index 84208c879d60..ed64ee812f2d 100644 --- a/packages/astro/src/actions/runtime/utils.ts +++ b/packages/astro/src/actions/runtime/utils.ts @@ -1,5 +1,7 @@ import type { APIContext } from '../../@types/astro.js'; +export const ACTION_API_CONTEXT_SYMBOL = Symbol.for('astro.actionAPIContext'); + export const formContentTypes = ['application/x-www-form-urlencoded', 'multipart/form-data']; export function hasContentType(contentType: string, expected: string[]) { @@ -26,3 +28,8 @@ export type MaybePromise = T | Promise; * `result.error.fields` will be typed with the `name` field. */ export type ErrorInferenceObject = Record; + +export function isActionAPIContext(ctx: ActionAPIContext): boolean { + const symbol = Reflect.get(ctx, ACTION_API_CONTEXT_SYMBOL); + return symbol === true; +} diff --git a/packages/astro/src/actions/runtime/virtual/server.ts b/packages/astro/src/actions/runtime/virtual/server.ts index 8e5e6bb4f1a5..01e8fbd86bfe 100644 --- a/packages/astro/src/actions/runtime/virtual/server.ts +++ b/packages/astro/src/actions/runtime/virtual/server.ts @@ -1,7 +1,12 @@ import { z } from 'zod'; import { ActionCalledFromServerError } from '../../../core/errors/errors-data.js'; import { AstroError } from '../../../core/errors/errors.js'; -import type { ActionAPIContext, ErrorInferenceObject, MaybePromise } from '../utils.js'; +import { + type ActionAPIContext, + type ErrorInferenceObject, + type MaybePromise, + isActionAPIContext, +} from '../utils.js'; import { ActionError, ActionInputError, type SafeResult, callSafely } from './shared.js'; export * from './shared.js'; @@ -60,7 +65,8 @@ export function defineAction< : getJsonServerHandler(handler, inputSchema); async function safeServerHandler(this: ActionAPIContext, unparsedInput: unknown) { - if (typeof this === 'function') { + // The ActionAPIContext should always contain the `params` property + if (typeof this === 'function' || !isActionAPIContext(this)) { throw new AstroError(ActionCalledFromServerError); } return callSafely(() => serverHandler(unparsedInput, this)); diff --git a/packages/astro/src/actions/utils.ts b/packages/astro/src/actions/utils.ts index 0e7c6fb62190..d8596a322930 100644 --- a/packages/astro/src/actions/utils.ts +++ b/packages/astro/src/actions/utils.ts @@ -2,7 +2,7 @@ import type fsMod from 'node:fs'; import * as eslexer from 'es-module-lexer'; import type { APIContext } from '../@types/astro.js'; import type { Locals } from './runtime/middleware.js'; -import type { ActionAPIContext } from './runtime/utils.js'; +import { ACTION_API_CONTEXT_SYMBOL, type ActionAPIContext } from './runtime/utils.js'; import { deserializeActionResult, getActionQueryString } from './runtime/virtual/shared.js'; export function hasActionPayload(locals: APIContext['locals']): locals is Locals { @@ -23,6 +23,7 @@ export function createGetActionResult(locals: APIContext['locals']): APIContext[ export function createCallAction(context: ActionAPIContext): APIContext['callAction'] { return (baseAction, input) => { + Reflect.set(context, ACTION_API_CONTEXT_SYMBOL, true); const action = baseAction.bind(context); return action(input) as any; }; diff --git a/packages/astro/test/actions.test.js b/packages/astro/test/actions.test.js index 0ed98db9357a..eed4d8734415 100644 --- a/packages/astro/test/actions.test.js +++ b/packages/astro/test/actions.test.js @@ -132,6 +132,12 @@ describe('Astro Actions', () => { assert.equal(data, 'Hello, ben!'); } }); + + it('Should fail when calling an action without using Astro.callAction', async () => { + const res = await fixture.fetch('/invalid/'); + const text = await res.text(); + assert.match(text, /ActionCalledFromServerError/); + }); }); describe('build', () => { diff --git a/packages/astro/test/fixtures/actions/src/pages/invalid.astro b/packages/astro/test/fixtures/actions/src/pages/invalid.astro new file mode 100644 index 000000000000..908eee853bd4 --- /dev/null +++ b/packages/astro/test/fixtures/actions/src/pages/invalid.astro @@ -0,0 +1,6 @@ +--- +import { actions } from "astro:actions"; + +// this is invalid, it should fail +const result = await actions.imageUploadInChunks(); +--- From 9cca10843912698e13d35f1bc3c493e2c96a06ee Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Fri, 8 Nov 2024 14:06:19 -0500 Subject: [PATCH 03/20] Fix incorrect status code in dev server for action errors (#12401) * remove default status that swallows response.status * refactor status compute to be more readable * changeset --- .changeset/nine-mayflies-film.md | 5 ++++ .../src/vite-plugin-astro-server/route.ts | 29 ++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 .changeset/nine-mayflies-film.md diff --git a/.changeset/nine-mayflies-film.md b/.changeset/nine-mayflies-film.md new file mode 100644 index 000000000000..c782c73bc443 --- /dev/null +++ b/.changeset/nine-mayflies-film.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes unexpected 200 status in dev server logs for action errors and redirects. diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 30a6eda0190e..9f6d434b55e8 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -130,7 +130,6 @@ type HandleRoute = { manifestData: ManifestData; incomingRequest: http.IncomingMessage; incomingResponse: http.ServerResponse; - status?: 404 | 500 | 200; pipeline: DevPipeline; }; @@ -138,7 +137,6 @@ export async function handleRoute({ matchedRoute, url, pathname, - status = getStatus(matchedRoute), body, origin, pipeline, @@ -208,12 +206,17 @@ export async function handleRoute({ }); let response; + let statusCode = 200; let isReroute = false; let isRewrite = false; try { response = await renderContext.render(mod); isReroute = response.headers.has(REROUTE_DIRECTIVE_HEADER); isRewrite = response.headers.has(REWRITE_DIRECTIVE_HEADER_KEY); + statusCode = isRewrite + ? // Ignore `matchedRoute` status for rewrites + response.status + : (getStatusByMatchedRoute(matchedRoute) ?? response.status); } catch (err: any) { const custom500 = getCustom500Route(manifestData); if (!custom500) { @@ -225,7 +228,7 @@ export async function handleRoute({ const preloaded500Component = await pipeline.preload(custom500, filePath500); renderContext.props.error = err; response = await renderContext.render(preloaded500Component); - status = 500; + statusCode = 500; } if (isLoggedRequest(pathname)) { @@ -235,20 +238,20 @@ export async function handleRoute({ req({ url: pathname, method: incomingRequest.method, - statusCode: isRewrite ? response.status : (status ?? response.status), + statusCode, isRewrite, reqTime: timeEnd - timeStart, }), ); } - if (response.status === 404 && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no') { + + if (statusCode === 404 && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no') { const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline); if (options && options.route !== fourOhFourRoute?.route) return handleRoute({ ...options, matchedRoute: fourOhFourRoute, url: new URL(pathname, url), - status: 404, body, origin, pipeline, @@ -290,18 +293,18 @@ export async function handleRoute({ // Apply the `status` override to the response object before responding. // Response.status is read-only, so a clone is required to override. - if (status && response.status !== status && (status === 404 || status === 500)) { + if (response.status !== statusCode) { response = new Response(response.body, { - status: status, + status: statusCode, headers: response.headers, }); } await writeSSRResult(request, response, incomingResponse); } -function getStatus(matchedRoute?: MatchedRoute): 404 | 500 | 200 { - if (!matchedRoute) return 404; - if (matchedRoute.route.route === '/404') return 404; - if (matchedRoute.route.route === '/500') return 500; - return 200; +/** Check for /404 and /500 custom routes to compute status code */ +function getStatusByMatchedRoute(matchedRoute?: MatchedRoute) { + if (matchedRoute?.route.route === '/404') return 404; + if (matchedRoute?.route.route === '/500') return 500; + return undefined; } From d10f91815e63f169cff3d1daef5505aef077c76c Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Fri, 8 Nov 2024 17:03:57 -0500 Subject: [PATCH 04/20] Actions middleware (#12373) * add manual middleware config option with getMiddlewareContext() * refactor requestInfo to action object * set action error response status from render context * update automatic middleware to plain POST handler * fix missing Locals type * test: add separate POST and cookie forwarding tests * remove actions.middleware flag * add docs on actionResultAlreadySet * test: use Astro.rewrite instead of middleware next(). TODO: fix next() * fix type errors from rebase * test: remove middleware handler * test: use cookie forwarding for 'lots of fields' * refactor: _isPrerendered -> ctx.isPrerendered * expose getOriginPathname as middleware utility * add support for handling RPC action results from middleware * test: RPC security middleware * refactor POST route handler to use getMiddlewareContext() * remove unused actionRedirect flag * changeset * test: add expectedd rewrite failure for Ema to debug * fix e2e test * nit: form -> from Co-authored-by: Emanuele Stoppa * rename getMiddlewareContext -> getActionContext * rename form-action -> form * move /_actions/ route pattern to const * move type defs to user-accessible ActionMiddlewareContext type * export action middleware context type * strip omitted fields for Action API Context * add satisfies to type for good measure * move getOriginPathname to shared ctx.originPathname * remove `next()` rewrite because it isn't supported * fix empty forms raising a 415 * fix missing async on cookie example * nit: ctx -> context * fix json parse error when content length is 0 * refactor body parsing to function * edit: migration -> updating your HTML form actions Co-authored-by: Sarah Rainsberger * update changeset to match docs v5 guide * add absolute urls to changeset links --------- Co-authored-by: Emanuele Stoppa Co-authored-by: Sarah Rainsberger --- .changeset/tall-waves-impress.md | 62 +++++++ packages/astro/e2e/actions-blog.test.js | 4 +- .../fixtures/actions-blog/astro.config.mjs | 2 +- .../actions-blog/src/actions/index.ts | 12 +- .../fixtures/actions-blog/src/middleware.ts | 43 ++++- .../src/pages/blog/[...slug].astro | 12 +- .../src/pages/lots-of-fields.astro | 72 ++++---- .../actions-blog/src/pages/rewritten.astro | 8 +- .../fixtures/actions-blog/src/pages/sum.astro | 3 + packages/astro/src/actions/consts.ts | 3 +- packages/astro/src/actions/integration.ts | 4 +- .../astro/src/actions/runtime/middleware.ts | 167 +----------------- packages/astro/src/actions/runtime/route.ts | 35 +--- packages/astro/src/actions/runtime/utils.ts | 10 ++ .../src/actions/runtime/virtual/client.ts | 4 + .../src/actions/runtime/virtual/get-action.ts | 5 +- .../src/actions/runtime/virtual/server.ts | 129 +++++++++++++- packages/astro/src/actions/utils.ts | 3 +- packages/astro/src/core/middleware/index.ts | 6 +- packages/astro/src/core/render-context.ts | 19 +- packages/astro/src/core/routing/rewrite.ts | 4 +- packages/astro/src/types/public/context.ts | 5 + packages/astro/templates/actions.mjs | 11 +- packages/astro/test/actions.test.js | 72 +++++++- .../fixtures/actions/src/actions/index.ts | 43 +++-- .../test/fixtures/actions/src/middleware.ts | 56 +++++- 26 files changed, 483 insertions(+), 311 deletions(-) create mode 100644 .changeset/tall-waves-impress.md create mode 100644 packages/astro/e2e/fixtures/actions-blog/src/pages/sum.astro diff --git a/.changeset/tall-waves-impress.md b/.changeset/tall-waves-impress.md new file mode 100644 index 000000000000..aecf307e02de --- /dev/null +++ b/.changeset/tall-waves-impress.md @@ -0,0 +1,62 @@ +--- +'astro': minor +--- + +Changes the default behavior for Astro Action form requests to a standard POST submission. + +In Astro 4.x, actions called from an HTML form would trigger a redirect with the result forwarded using cookies. This caused issues for large form errors and return values that exceeded the 4 KB limit of cookie-based storage. + +Astro 5.0 now renders the result of an action as a POST result without any forwarding. This will introduce a "confirm form resubmission?" dialog when a user attempts to refresh the page, though it no longer imposes a 4 KB limit on action return value. + +## Customize form submission behavior + +If you prefer to address the "confirm form resubmission?" dialog on refresh, or to preserve action results across sessions, you can now [customize action result handling from middleware](https://5-0-0-beta.docs.astro.build/en/guides/actions/#advanced-persist-action-results-with-a-session). + +We recommend using a session storage provider [as described in our Netlify Blob example](https://5-0-0-beta.docs.astro.build/en/guides/actions/#advanced-persist-action-results-with-a-session). However, if you prefer the cookie forwarding behavior from 4.X and accept the 4 KB size limit, you can implement the pattern as shown in this sample snippet: + +```ts +// src/middleware.ts +import { defineMiddleware } from 'astro:middleware'; +import { getActionContext } from 'astro:actions'; + +export const onRequest = defineMiddleware(async (context, next) => { + // Skip requests for prerendered pages + if (context.isPrerendered) return next(); + + const { action, setActionResult, serializeActionResult } = getActionContext(context); + + // If an action result was forwarded as a cookie, set the result + // to be accessible from `Astro.getActionResult()` + const payload = context.cookies.get('ACTION_PAYLOAD'); + if (payload) { + const { actionName, actionResult } = payload.json(); + setActionResult(actionName, actionResult); + context.cookies.delete('ACTION_PAYLOAD'); + return next(); + } + + // If an action was called from an HTML form action, + // call the action handler and redirect with the result as a cookie. + if (action?.calledFrom === 'form') { + const actionResult = await action.handler(); + + context.cookies.set('ACTION_PAYLOAD', { + actionName: action.name, + actionResult: serializeActionResult(actionResult), + }); + + if (actionResult.error) { + // Redirect back to the previous page on error + const referer = context.request.headers.get('Referer'); + if (!referer) { + throw new Error('Internal: Referer unexpectedly missing from Action POST request.'); + } + return context.redirect(referer); + } + // Redirect to the destination page on success + return context.redirect(context.originPathname); + } + + return next(); +}) +``` diff --git a/packages/astro/e2e/actions-blog.test.js b/packages/astro/e2e/actions-blog.test.js index a8d9a7fc67b9..7d362334e33e 100644 --- a/packages/astro/e2e/actions-blog.test.js +++ b/packages/astro/e2e/actions-blog.test.js @@ -155,14 +155,14 @@ test.describe('Astro Actions - Blog', () => { await expect(page).toHaveURL(astro.resolveUrl('/blog/')); }); - test('Should redirect to the origin pathname when there is a rewrite', async ({ + test('Should redirect to the origin pathname when there is a rewrite from an Astro page', async ({ page, astro, }) => { await page.goto(astro.resolveUrl('/sum')); const submitButton = page.getByTestId('submit'); await submitButton.click(); - await expect(page).toHaveURL(astro.resolveUrl('/sum')); + await expect(page).toHaveURL(astro.resolveUrl('/sum?_astroAction=sum')); const p = page.locator('p').nth(0); await expect(p).toContainText('Form result: {"data":3}'); }); diff --git a/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs b/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs index 35f7481f230e..c00c4da70df2 100644 --- a/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs +++ b/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs @@ -7,7 +7,7 @@ import node from '@astrojs/node'; export default defineConfig({ site: 'https://example.com', integrations: [db(), react()], - output: 'static', + output: 'server', adapter: node({ mode: 'standalone', }), diff --git a/packages/astro/e2e/fixtures/actions-blog/src/actions/index.ts b/packages/astro/e2e/fixtures/actions-blog/src/actions/index.ts index f3e1f248e515..01b479b2b909 100644 --- a/packages/astro/e2e/fixtures/actions-blog/src/actions/index.ts +++ b/packages/astro/e2e/fixtures/actions-blog/src/actions/index.ts @@ -68,21 +68,21 @@ export const server = { seven: z.string().min(3), eight: z.string().min(3), nine: z.string().min(3), - ten: z.string().min(3) + ten: z.string().min(3), }), handler(form) { return form; - } - }) + }, + }), }, sum: defineAction({ - accept: "form", + accept: 'form', input: z.object({ a: z.number(), b: z.number(), }), async handler({ a, b }) { - return a + b + return a + b; }, - }) + }), }; diff --git a/packages/astro/e2e/fixtures/actions-blog/src/middleware.ts b/packages/astro/e2e/fixtures/actions-blog/src/middleware.ts index 53bb8235ac2a..9e259f4ee988 100644 --- a/packages/astro/e2e/fixtures/actions-blog/src/middleware.ts +++ b/packages/astro/e2e/fixtures/actions-blog/src/middleware.ts @@ -1,9 +1,38 @@ -import { defineMiddleware } from "astro:middleware"; +import { defineMiddleware } from 'astro:middleware'; +import { getActionContext } from 'astro:actions'; -export const onRequest = defineMiddleware((ctx, next) => { - if (ctx.request.method === "GET" && ctx.url.pathname === "/sum") { - return next("/rewritten") +const actionCookieForwarding = defineMiddleware(async (ctx, next) => { + if (ctx.isPrerendered) return next(); + + const { action, setActionResult, serializeActionResult } = getActionContext(ctx); + + const payload = ctx.cookies.get('ACTION_PAYLOAD'); + if (payload) { + const { actionName, actionResult } = payload.json(); + setActionResult(actionName, actionResult); + ctx.cookies.delete('ACTION_PAYLOAD'); + return next(); + } + + if (action?.calledFrom === 'form' && ctx.url.searchParams.has('actionCookieForwarding')) { + const actionResult = await action.handler(); + + ctx.cookies.set('ACTION_PAYLOAD', { + actionName: action.name, + actionResult: serializeActionResult(actionResult), + }); + + if (actionResult.error) { + const referer = ctx.request.headers.get('Referer'); + if (!referer) { + throw new Error('Internal: Referer unexpectedly missing from Action POST request.'); + } + return ctx.redirect(referer); + } + return ctx.redirect(ctx.originPathname); } - - return next() -}) + + return next(); +}); + +export const onRequest = actionCookieForwarding; diff --git a/packages/astro/e2e/fixtures/actions-blog/src/pages/blog/[...slug].astro b/packages/astro/e2e/fixtures/actions-blog/src/pages/blog/[...slug].astro index ad4aea521a98..9dc5404d6488 100644 --- a/packages/astro/e2e/fixtures/actions-blog/src/pages/blog/[...slug].astro +++ b/packages/astro/e2e/fixtures/actions-blog/src/pages/blog/[...slug].astro @@ -1,5 +1,5 @@ --- -import { type CollectionEntry, getCollection, getEntry } from 'astro:content'; +import { type CollectionEntry, getEntry } from 'astro:content'; import BlogPost from '../../layouts/BlogPost.astro'; import { Logout } from '../../components/Logout'; import { db, eq, Comment, Likes } from 'astro:db'; @@ -8,16 +8,6 @@ import { PostComment } from '../../components/PostComment'; import { actions } from 'astro:actions'; import { isInputError } from 'astro:actions'; -export const prerender = false; - -export async function getStaticPaths() { - const posts = await getCollection('blog'); - return posts.map((post) => ({ - params: { slug: post.slug }, - props: post, - })); -} - type Props = CollectionEntry<'blog'>; const post = await getEntry('blog', Astro.params.slug)!; diff --git a/packages/astro/e2e/fixtures/actions-blog/src/pages/lots-of-fields.astro b/packages/astro/e2e/fixtures/actions-blog/src/pages/lots-of-fields.astro index 2b78aee1b869..e260e3b39579 100644 --- a/packages/astro/e2e/fixtures/actions-blog/src/pages/lots-of-fields.astro +++ b/packages/astro/e2e/fixtures/actions-blog/src/pages/lots-of-fields.astro @@ -6,38 +6,42 @@ const result = Astro.getActionResult(actions.blog.lotsOfStuff); --- - - Actions - - - -
- - {result?.error?.fields.one} - - {result?.error?.fields.two} - - {result?.error?.fields.three} - - {result?.error?.fields.four} - - {result?.error?.fields.five} - - {result?.error?.fields.six} - - {result?.error?.fields.seven} - - {result?.error?.fields.eight} - - {result?.error?.fields.nine} - - {result?.error?.fields.ten} - -
- + + Actions + + + +
+ + {result?.error?.fields.one} + + {result?.error?.fields.two} + + {result?.error?.fields.three} + + {result?.error?.fields.four} + + {result?.error?.fields.five} + + {result?.error?.fields.six} + + {result?.error?.fields.seven} + + {result?.error?.fields.eight} + + {result?.error?.fields.nine} + + {result?.error?.fields.ten} + +
+ diff --git a/packages/astro/e2e/fixtures/actions-blog/src/pages/rewritten.astro b/packages/astro/e2e/fixtures/actions-blog/src/pages/rewritten.astro index 72eebf1bb006..0a41c1a8f0f0 100644 --- a/packages/astro/e2e/fixtures/actions-blog/src/pages/rewritten.astro +++ b/packages/astro/e2e/fixtures/actions-blog/src/pages/rewritten.astro @@ -1,8 +1,7 @@ --- -import { actions } from "astro:actions"; - -const result = Astro.getActionResult(actions.sum) +import { actions } from 'astro:actions'; +const result = Astro.getActionResult(actions.sum); --- @@ -13,6 +12,5 @@ const result = Astro.getActionResult(actions.sum)

Form result: {JSON.stringify(result)}

- - + diff --git a/packages/astro/e2e/fixtures/actions-blog/src/pages/sum.astro b/packages/astro/e2e/fixtures/actions-blog/src/pages/sum.astro new file mode 100644 index 000000000000..46856b4ff0f8 --- /dev/null +++ b/packages/astro/e2e/fixtures/actions-blog/src/pages/sum.astro @@ -0,0 +1,3 @@ +--- +return Astro.rewrite('/rewritten' + Astro.url.search); +--- diff --git a/packages/astro/src/actions/consts.ts b/packages/astro/src/actions/consts.ts index 6a55386d869a..35137df59a49 100644 --- a/packages/astro/src/actions/consts.ts +++ b/packages/astro/src/actions/consts.ts @@ -8,5 +8,6 @@ export const NOOP_ACTIONS = '\0noop-actions'; export const ACTION_QUERY_PARAMS = { actionName: '_astroAction', actionPayload: '_astroActionPayload', - actionRedirect: '_astroActionRedirect', }; + +export const ACTION_RPC_ROUTE_PATTERN = '/_actions/[...path]'; diff --git a/packages/astro/src/actions/integration.ts b/packages/astro/src/actions/integration.ts index 830420836a3d..23fbd904a52f 100644 --- a/packages/astro/src/actions/integration.ts +++ b/packages/astro/src/actions/integration.ts @@ -3,7 +3,7 @@ import { AstroError } from '../core/errors/errors.js'; import { viteID } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroIntegration } from '../types/public/integrations.js'; -import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js'; +import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID, ACTION_RPC_ROUTE_PATTERN } from './consts.js'; /** * This integration is applied when the user is using Actions in their project. @@ -19,7 +19,7 @@ export default function astroIntegrationActionsRouteHandler({ hooks: { async 'astro:config:setup'(params) { params.injectRoute({ - pattern: '/_actions/[...path]', + pattern: ACTION_RPC_ROUTE_PATTERN, entrypoint: 'astro/actions/runtime/route.js', prerender: false, }); diff --git a/packages/astro/src/actions/runtime/middleware.ts b/packages/astro/src/actions/runtime/middleware.ts index 881169cb479e..47adc29454cb 100644 --- a/packages/astro/src/actions/runtime/middleware.ts +++ b/packages/astro/src/actions/runtime/middleware.ts @@ -1,166 +1,13 @@ -import { decodeBase64, encodeBase64 } from '@oslojs/encoding'; -import { yellow } from 'kleur/colors'; -import { defineMiddleware } from '../../core/middleware/index.js'; -import { getOriginPathname } from '../../core/routing/rewrite.js'; -import type { MiddlewareNext } from '../../types/public/common.js'; -import type { APIContext } from '../../types/public/context.js'; -import { ACTION_QUERY_PARAMS } from '../consts.js'; -import { formContentTypes, hasContentType } from './utils.js'; -import { getAction } from './virtual/get-action.js'; -import { - type SafeResult, - type SerializedActionResult, - serializeActionResult, -} from './virtual/shared.js'; - -export type ActionPayload = { - actionResult: SerializedActionResult; - actionName: string; -}; - -export type Locals = { - _actionPayload: ActionPayload; -}; - -const decoder = new TextDecoder(); -const encoder = new TextEncoder(); +import { defineMiddleware } from '../../virtual-modules/middleware.js'; +import { getActionContext } from './virtual/server.js'; export const onRequest = defineMiddleware(async (context, next) => { - if (context.isPrerendered) { - if (context.request.method === 'POST') { - console.warn( - yellow('[astro:actions]'), - "POST requests should not be sent to prerendered pages. If you're using Actions, disable prerendering with `export const prerender = false`.", - ); - } - return next(); - } - - const locals = context.locals as Locals; - // Actions middleware may have run already after a path rewrite. - // See https://github.com/withastro/roadmap/blob/main/proposals/0048-rerouting.md#ctxrewrite - // `_actionPayload` is the same for every page, - // so short circuit if already defined. - if (locals._actionPayload) return next(); - - const actionPayloadCookie = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.value; - if (actionPayloadCookie) { - const actionPayload = JSON.parse(decoder.decode(decodeBase64(actionPayloadCookie))); - - if (!isActionPayload(actionPayload)) { - throw new Error('Internal: Invalid action payload in cookie.'); - } - return renderResult({ context, next, ...actionPayload }); - } - - const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName); + if (context.isPrerendered) return next(); + const { action, setActionResult, serializeActionResult } = getActionContext(context); - if (context.request.method === 'POST' && actionName) { - return handlePost({ context, next, actionName }); + if (action?.calledFrom === 'form') { + const actionResult = await action.handler(); + setActionResult(action.name, serializeActionResult(actionResult)); } - return next(); }); - -async function renderResult({ - context, - next, - actionResult, - actionName, -}: { - context: APIContext; - next: MiddlewareNext; - actionResult: SerializedActionResult; - actionName: string; -}) { - const locals = context.locals as Locals; - - locals._actionPayload = { actionResult, actionName }; - const response = await next(); - context.cookies.delete(ACTION_QUERY_PARAMS.actionPayload); - - if (actionResult.type === 'error') { - return new Response(response.body, { - status: actionResult.status, - statusText: actionResult.type, - headers: response.headers, - }); - } - return response; -} - -async function handlePost({ - context, - next, - actionName, -}: { - context: APIContext; - next: MiddlewareNext; - actionName: string; -}) { - const { request } = context; - const baseAction = await getAction(actionName); - - const contentType = request.headers.get('content-type'); - let formData: FormData | undefined; - if (contentType && hasContentType(contentType, formContentTypes)) { - formData = await request.clone().formData(); - } - const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context; - const action = baseAction.bind(actionAPIContext); - const actionResult = await action(formData); - - if (context.url.searchParams.get(ACTION_QUERY_PARAMS.actionRedirect) === 'false') { - return renderResult({ - context, - next, - actionName, - actionResult: serializeActionResult(actionResult), - }); - } - - return redirectWithResult({ context, actionName, actionResult }); -} - -async function redirectWithResult({ - context, - actionName, - actionResult, -}: { - context: APIContext; - actionName: string; - actionResult: SafeResult; -}) { - const cookieValue = encodeBase64( - encoder.encode( - JSON.stringify({ - actionName: actionName, - actionResult: serializeActionResult(actionResult), - }), - ), - ); - context.cookies.set(ACTION_QUERY_PARAMS.actionPayload, cookieValue); - - if (actionResult.error) { - const referer = context.request.headers.get('Referer'); - if (!referer) { - throw new Error('Internal: Referer unexpectedly missing from Action POST request.'); - } - return context.redirect(referer); - } - - const referer = getOriginPathname(context.request); - if (referer) { - return context.redirect(referer); - } - - return context.redirect(context.url.pathname); -} - -function isActionPayload(json: unknown): json is ActionPayload { - if (typeof json !== 'object' || json == null) return false; - - if (!('actionResult' in json) || typeof json.actionResult !== 'object') return false; - if (!('actionName' in json) || typeof json.actionName !== 'string') return false; - return true; -} diff --git a/packages/astro/src/actions/runtime/route.ts b/packages/astro/src/actions/runtime/route.ts index 07e06ee9e62c..c7522328d256 100644 --- a/packages/astro/src/actions/runtime/route.ts +++ b/packages/astro/src/actions/runtime/route.ts @@ -1,35 +1,14 @@ import type { APIRoute } from '../../types/public/common.js'; -import { formContentTypes, hasContentType } from './utils.js'; -import { getAction } from './virtual/get-action.js'; -import { serializeActionResult } from './virtual/shared.js'; +import { getActionContext } from './virtual/server.js'; export const POST: APIRoute = async (context) => { - const { request, url } = context; - let baseAction; - try { - baseAction = await getAction(url.pathname); - } catch (e) { - if (import.meta.env.DEV) throw e; - console.error(e); - return new Response(e instanceof Error ? e.message : null, { status: 404 }); - } - const contentType = request.headers.get('Content-Type'); - const contentLength = request.headers.get('Content-Length'); - let args: unknown; - if (!contentType || contentLength === '0') { - args = undefined; - } else if (contentType && hasContentType(contentType, formContentTypes)) { - args = await request.clone().formData(); - } else if (contentType && hasContentType(contentType, ['application/json'])) { - args = await request.clone().json(); - } else { - // 415: Unsupported media type - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415 - return new Response(null, { status: 415 }); + const { action, serializeActionResult } = getActionContext(context); + + if (action?.calledFrom !== 'rpc') { + return new Response('Not found', { status: 404 }); } - const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context; - const action = baseAction.bind(actionAPIContext); - const result = await action(args); + + const result = await action.handler(); const serialized = serializeActionResult(result); if (serialized.type === 'empty') { diff --git a/packages/astro/src/actions/runtime/utils.ts b/packages/astro/src/actions/runtime/utils.ts index d8b339a093df..7d6a2173054d 100644 --- a/packages/astro/src/actions/runtime/utils.ts +++ b/packages/astro/src/actions/runtime/utils.ts @@ -1,4 +1,14 @@ import type { APIContext } from '../../types/public/context.js'; +import type { SerializedActionResult } from './virtual/shared.js'; + +export type ActionPayload = { + actionResult: SerializedActionResult; + actionName: string; +}; + +export type Locals = { + _actionPayload: ActionPayload; +}; export const formContentTypes = ['application/x-www-form-urlencoded', 'multipart/form-data']; diff --git a/packages/astro/src/actions/runtime/virtual/client.ts b/packages/astro/src/actions/runtime/virtual/client.ts index c80e6778ae7b..68407f4cbf25 100644 --- a/packages/astro/src/actions/runtime/virtual/client.ts +++ b/packages/astro/src/actions/runtime/virtual/client.ts @@ -3,3 +3,7 @@ export * from './shared.js'; export function defineAction() { throw new Error('[astro:action] `defineAction()` unexpectedly used on the client.'); } + +export function getActionContext() { + throw new Error('[astro:action] `getActionContext()` unexpectedly used on the client.'); +} diff --git a/packages/astro/src/actions/runtime/virtual/get-action.ts b/packages/astro/src/actions/runtime/virtual/get-action.ts index 7cd260f8663b..a11e72fc4874 100644 --- a/packages/astro/src/actions/runtime/virtual/get-action.ts +++ b/packages/astro/src/actions/runtime/virtual/get-action.ts @@ -11,10 +11,7 @@ import type { ActionAccept, ActionClient } from './server.js'; export async function getAction( path: string, ): Promise> { - const pathKeys = path - .replace(/^.*\/_actions\//, '') - .split('.') - .map((key) => decodeURIComponent(key)); + const pathKeys = path.split('.').map((key) => decodeURIComponent(key)); // @ts-expect-error virtual module let { server: actionLookup } = await import('astro:internal-actions'); diff --git a/packages/astro/src/actions/runtime/virtual/server.ts b/packages/astro/src/actions/runtime/virtual/server.ts index 8e5e6bb4f1a5..f8fac557a09a 100644 --- a/packages/astro/src/actions/runtime/virtual/server.ts +++ b/packages/astro/src/actions/runtime/virtual/server.ts @@ -1,8 +1,27 @@ import { z } from 'zod'; import { ActionCalledFromServerError } from '../../../core/errors/errors-data.js'; import { AstroError } from '../../../core/errors/errors.js'; -import type { ActionAPIContext, ErrorInferenceObject, MaybePromise } from '../utils.js'; -import { ActionError, ActionInputError, type SafeResult, callSafely } from './shared.js'; +import { + formContentTypes, + hasContentType, + type ActionAPIContext, + type ErrorInferenceObject, + type MaybePromise, +} from '../utils.js'; +import { + ACTION_QUERY_PARAMS, + ActionError, + ActionInputError, + type SafeResult, + type SerializedActionResult, + callSafely, + deserializeActionResult, + serializeActionResult, +} from './shared.js'; +import type { Locals } from '../utils.js'; +import { getAction } from './get-action.js'; +import type { APIContext } from '../../../types/public/index.js'; +import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js'; export * from './shared.js'; @@ -212,3 +231,109 @@ function unwrapBaseObjectSchema(schema: z.ZodType, unparsedInput: FormData) { } return schema; } + +export type ActionMiddlewareContext = { + /** Information about an incoming action request. */ + action?: { + /** Whether an action was called using an RPC function or by using an HTML form action. */ + calledFrom: 'rpc' | 'form'; + /** The name of the action. Useful to track the source of an action result during a redirect. */ + name: string; + /** Programatically call the action to get the result. */ + handler: () => Promise>; + }; + /** + * Manually set the action result accessed via `getActionResult()`. + * Calling this function from middleware will disable Astro's own action result handling. + */ + setActionResult(actionName: string, actionResult: SerializedActionResult): void; + /** + * Serialize an action result to stored in a cookie or session. + * Also used to pass a result to Astro templates via `setActionResult()`. + */ + serializeActionResult: typeof serializeActionResult; + /** + * Deserialize an action result to access data and error objects. + */ + deserializeActionResult: typeof deserializeActionResult; +}; + +/** + * Access information about Action requests from middleware. + */ +export function getActionContext(context: APIContext): ActionMiddlewareContext { + const callerInfo = getCallerInfo(context); + + // Prevents action results from being handled on a rewrite. + // Also prevents our *own* fallback middleware from running + // if the user's middleware has already handled the result. + const actionResultAlreadySet = Boolean((context.locals as Locals)._actionPayload); + + let action: ActionMiddlewareContext['action'] = undefined; + + if (callerInfo && context.request.method === 'POST' && !actionResultAlreadySet) { + action = { + calledFrom: callerInfo.from, + name: callerInfo.name, + handler: async () => { + const baseAction = await getAction(callerInfo.name); + let input; + try { + input = await parseRequestBody(context.request); + } catch (e) { + if (e instanceof TypeError) { + return { data: undefined, error: new ActionError({ code: 'UNSUPPORTED_MEDIA_TYPE' }) }; + } + throw e; + } + const { + props: _props, + getActionResult: _getActionResult, + callAction: _callAction, + redirect: _redirect, + ...actionAPIContext + } = context; + const handler = baseAction.bind(actionAPIContext satisfies ActionAPIContext); + return handler(input); + }, + }; + } + + function setActionResult(actionName: string, actionResult: SerializedActionResult) { + (context.locals as Locals)._actionPayload = { + actionResult, + actionName, + }; + } + return { + action, + setActionResult, + serializeActionResult, + deserializeActionResult, + }; +} + +function getCallerInfo(ctx: APIContext) { + if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) { + return { from: 'rpc', name: ctx.url.pathname.replace(/^.*\/_actions\//, '') } as const; + } + const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName); + if (queryParam) { + return { from: 'form', name: queryParam } as const; + } + return undefined; +} + +async function parseRequestBody(request: Request) { + const contentType = request.headers.get('content-type'); + const contentLength = request.headers.get('Content-Length'); + + if (!contentType) return undefined; + if (hasContentType(contentType, formContentTypes)) { + return await request.clone().formData(); + } + if (hasContentType(contentType, ['application/json'])) { + return contentLength === '0' ? undefined : await request.clone().json(); + } + throw new TypeError('Unsupported content type'); +} diff --git a/packages/astro/src/actions/utils.ts b/packages/astro/src/actions/utils.ts index 3f2f45bfe682..dc0fa4b1486e 100644 --- a/packages/astro/src/actions/utils.ts +++ b/packages/astro/src/actions/utils.ts @@ -1,8 +1,7 @@ import type fsMod from 'node:fs'; import * as eslexer from 'es-module-lexer'; import type { APIContext } from '../types/public/context.js'; -import type { Locals } from './runtime/middleware.js'; -import type { ActionAPIContext } from './runtime/utils.js'; +import type { ActionAPIContext, Locals } from './runtime/utils.js'; import { deserializeActionResult, getActionQueryString } from './runtime/virtual/shared.js'; export function hasActionPayload(locals: APIContext['locals']): locals is Locals { diff --git a/packages/astro/src/core/middleware/index.ts b/packages/astro/src/core/middleware/index.ts index 9e27434dc938..c7ed6e647987 100644 --- a/packages/astro/src/core/middleware/index.ts +++ b/packages/astro/src/core/middleware/index.ts @@ -6,10 +6,11 @@ import { } from '../../i18n/utils.js'; import type { MiddlewareHandler, Params, RewritePayload } from '../../types/public/common.js'; import type { APIContext } from '../../types/public/context.js'; -import { ASTRO_VERSION, clientAddressSymbol, clientLocalsSymbol } from '../constants.js'; +import { ASTRO_VERSION, clientLocalsSymbol } from '../constants.js'; import { AstroCookies } from '../cookies/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; import { getClientIpAddress } from '../routing/request.js'; +import { getOriginPathname } from '../routing/rewrite.js'; import { sequence } from './sequence.js'; function defineMiddleware(fn: MiddlewareHandler) { @@ -89,6 +90,9 @@ function createContext({ return (currentLocale ??= computeCurrentLocale(route, userDefinedLocales, defaultLocale)); }, url, + get originPathname() { + return getOriginPathname(request); + }, get clientAddress() { if (clientIpAddress) { return clientIpAddress; diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index 880d1f618711..49f174c33f01 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -28,7 +28,7 @@ import { callMiddleware } from './middleware/callMiddleware.js'; import { sequence } from './middleware/index.js'; import { renderRedirect } from './redirects/render.js'; import { type Pipeline, Slots, getParams, getProps } from './render/index.js'; -import { copyRequest, setOriginPathname } from './routing/rewrite.js'; +import { copyRequest, getOriginPathname, setOriginPathname } from './routing/rewrite.js'; import { SERVER_ISLAND_COMPONENT } from './server-islands/endpoint.js'; export const apiContextRoutesSymbol = Symbol.for('context.routes'); @@ -299,6 +299,9 @@ export class RenderContext { request: this.request, site: pipeline.site, url, + get originPathname() { + return getOriginPathname(renderContext.request); + }, }; } @@ -311,9 +314,12 @@ export class RenderContext { (await pipeline.componentMetadata(routeData)) ?? manifest.componentMetadata; const headers = new Headers({ 'Content-Type': 'text/html' }); const partial = typeof this.partial === 'boolean' ? this.partial : Boolean(mod.partial); + const actionResult = hasActionPayload(this.locals) + ? deserializeActionResult(this.locals._actionPayload.actionResult) + : undefined; const response = { - status, - statusText: 'OK', + status: actionResult?.error ? actionResult?.error.status : status, + statusText: actionResult?.error ? actionResult?.error.type : 'OK', get headers() { return headers; }, @@ -323,10 +329,6 @@ export class RenderContext { }, } satisfies AstroGlobal['response']; - const actionResult = hasActionPayload(this.locals) - ? deserializeActionResult(this.locals._actionPayload.actionResult) - : undefined; - // Create the result object that will be passed into the renderPage function. // This object starts here as an empty shell (not yet the result) but then // calling the render() function will populate the object with scripts, styles, etc. @@ -478,6 +480,9 @@ export class RenderContext { return createCallAction(this); }, url, + get originPathname() { + return getOriginPathname(renderContext.request); + }, }; } diff --git a/packages/astro/src/core/routing/rewrite.ts b/packages/astro/src/core/routing/rewrite.ts index cb23060374f9..57709892eada 100644 --- a/packages/astro/src/core/routing/rewrite.ts +++ b/packages/astro/src/core/routing/rewrite.ts @@ -108,10 +108,10 @@ export function setOriginPathname(request: Request, pathname: string): void { Reflect.set(request, originPathnameSymbol, encodeURIComponent(pathname)); } -export function getOriginPathname(request: Request): string | undefined { +export function getOriginPathname(request: Request): string { const origin = Reflect.get(request, originPathnameSymbol); if (origin) { return decodeURIComponent(origin); } - return undefined; + return new URL(request.url).pathname; } diff --git a/packages/astro/src/types/public/context.ts b/packages/astro/src/types/public/context.ts index 8d7c4fb3070a..7a6f3b6be0a2 100644 --- a/packages/astro/src/types/public/context.ts +++ b/packages/astro/src/types/public/context.ts @@ -268,6 +268,11 @@ interface AstroSharedContext< * A full URL object of the request URL. */ url: URL; + /** + * The origin pathname of the request URL. + * Useful to track the original URL before rewrites were applied. + */ + originPathname: string; /** * Get action result on the server when using a form POST. */ diff --git a/packages/astro/templates/actions.mjs b/packages/astro/templates/actions.mjs index 0aa22f5084be..82a287448a22 100644 --- a/packages/astro/templates/actions.mjs +++ b/packages/astro/templates/actions.mjs @@ -1,9 +1,4 @@ -import { - ACTION_QUERY_PARAMS, - ActionError, - deserializeActionResult, - getActionQueryString, -} from 'astro:actions'; +import { ActionError, deserializeActionResult, getActionQueryString } from 'astro:actions'; const ENCODED_DOT = '%2E'; @@ -26,10 +21,6 @@ function toActionProxy(actionCallback = {}, aggregatedPath = '') { // Progressive enhancement info for React. $$FORM_ACTION: function () { const searchParams = new URLSearchParams(action.toString()); - // Astro will redirect with a GET request by default. - // Disable this behavior to preserve form state - // for React's progressive enhancement. - searchParams.set(ACTION_QUERY_PARAMS.actionRedirect, 'false'); return { method: 'POST', // `name` creates a hidden input. diff --git a/packages/astro/test/actions.test.js b/packages/astro/test/actions.test.js index 0ed98db9357a..c34b91a7d152 100644 --- a/packages/astro/test/actions.test.js +++ b/packages/astro/test/actions.test.js @@ -213,7 +213,7 @@ describe('Astro Actions', () => { assert.equal(data.isFormData, true, 'Should receive plain FormData'); }); - it('Response middleware fallback', async () => { + it('Response middleware fallback - POST', async () => { const req = new Request('http://example.com/user?_astroAction=getUser', { method: 'POST', body: new FormData(), @@ -221,6 +221,25 @@ describe('Astro Actions', () => { Referer: 'http://example.com/user', }, }); + const res = await app.render(req); + assert.equal(res.ok, true); + + const html = await res.text(); + let $ = cheerio.load(html); + assert.equal($('#user').text(), 'Houston'); + }); + + it('Response middleware fallback - cookie forwarding', async () => { + const req = new Request( + 'http://example.com/user?_astroAction=getUser&actionCookieForwarding=true', + { + method: 'POST', + body: new FormData(), + headers: { + Referer: 'http://example.com/user', + }, + }, + ); const res = await followExpectedRedirect(req, app); assert.equal(res.ok, true); @@ -229,7 +248,7 @@ describe('Astro Actions', () => { assert.equal($('#user').text(), 'Houston'); }); - it('Respects custom errors', async () => { + it('Respects custom errors - POST', async () => { const req = new Request('http://example.com/user-or-throw?_astroAction=getUserOrThrow', { method: 'POST', body: new FormData(), @@ -237,6 +256,26 @@ describe('Astro Actions', () => { Referer: 'http://example.com/user-or-throw', }, }); + const res = await app.render(req); + assert.equal(res.status, 401); + + const html = await res.text(); + let $ = cheerio.load(html); + assert.equal($('#error-message').text(), 'Not logged in'); + assert.equal($('#error-code').text(), 'UNAUTHORIZED'); + }); + + it('Respects custom errors - cookie forwarding', async () => { + const req = new Request( + 'http://example.com/user-or-throw?_astroAction=getUserOrThrow&actionCookieForwarding=true', + { + method: 'POST', + body: new FormData(), + headers: { + Referer: 'http://example.com/user-or-throw', + }, + }, + ); const res = await followExpectedRedirect(req, app); assert.equal(res.status, 401); @@ -246,6 +285,35 @@ describe('Astro Actions', () => { assert.equal($('#error-code').text(), 'UNAUTHORIZED'); }); + it('Respects RPC middleware handling - locked', async () => { + const req = new Request('http://example.com/_actions/locked', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '{}', + }); + const res = await app.render(req); + assert.equal(res.status, 401); + }); + + it('Respects RPC middleware handling - cookie present', async () => { + const req = new Request('http://example.com/_actions/locked', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: 'actionCookie=1234', + }, + body: '{}', + }); + const res = await app.render(req); + assert.equal(res.ok, true); + assert.equal(res.headers.get('Content-Type'), 'application/json+devalue'); + + const data = devalue.parse(await res.text()); + assert.equal('safe' in data, true); + }); + it('Ignores `_astroAction` name for GET requests', async () => { const req = new Request('http://example.com/user-or-throw?_astroAction=getUserOrThrow', { method: 'GET', diff --git a/packages/astro/test/fixtures/actions/src/actions/index.ts b/packages/astro/test/fixtures/actions/src/actions/index.ts index 78cc39620baa..d3cd1b1bba55 100644 --- a/packages/astro/test/fixtures/actions/src/actions/index.ts +++ b/packages/astro/test/fixtures/actions/src/actions/index.ts @@ -161,28 +161,33 @@ export const server = { }; }, }), - "with.dot": defineAction({ - input: z.object({ - name: z.string(), - }), - handler: async (input) => { - return `Hello, ${input.name}!` - } - }), - "with space": defineAction({ - input: z.object({ - name: z.string(), - }), - handler: async (input) => { - return `Hello, ${input.name}!` - } - }), - "with/slash": defineAction({ + locked: defineAction({ + handler: async () => { + return { safe: true }; + }, + }), + 'with.dot': defineAction({ + input: z.object({ + name: z.string(), + }), + handler: async (input) => { + return `Hello, ${input.name}!`; + }, + }), + 'with space': defineAction({ + input: z.object({ + name: z.string(), + }), + handler: async (input) => { + return `Hello, ${input.name}!`; + }, + }), + 'with/slash': defineAction({ input: z.object({ name: z.string(), }), handler: async (input) => { - return `Hello, ${input.name}!` - } + return `Hello, ${input.name}!`; + }, }), }; diff --git a/packages/astro/test/fixtures/actions/src/middleware.ts b/packages/astro/test/fixtures/actions/src/middleware.ts index e630dfb730f9..0730b7e7bed1 100644 --- a/packages/astro/test/fixtures/actions/src/middleware.ts +++ b/packages/astro/test/fixtures/actions/src/middleware.ts @@ -1,8 +1,54 @@ -import { defineMiddleware } from 'astro:middleware'; +import { defineMiddleware, sequence } from 'astro:middleware'; +import { getActionContext } from 'astro:actions'; + +const actionCookieForwarding = defineMiddleware(async (ctx, next) => { + if (ctx.isPrerendered) return next(); + + const { action, setActionResult, serializeActionResult } = getActionContext(ctx); + + const payload = ctx.cookies.get('ACTION_PAYLOAD'); + if (payload) { + const { actionName, actionResult } = payload.json(); + setActionResult(actionName, actionResult); + ctx.cookies.delete('ACTION_PAYLOAD'); + return next(); + } + + if ( + action?.calledFrom === 'rpc' && + action.name === 'locked' && + !ctx.cookies.has('actionCookie') + ) { + return new Response('Unauthorized', { status: 401 }); + } + + if (action?.calledFrom === 'form' && ctx.url.searchParams.has('actionCookieForwarding')) { + const actionResult = await action.handler(); + + ctx.cookies.set('ACTION_PAYLOAD', { + actionName: action.name, + actionResult: serializeActionResult(actionResult), + }); + + if (actionResult.error) { + const referer = ctx.request.headers.get('Referer'); + if (!referer) { + throw new Error('Internal: Referer unexpectedly missing from Action POST request.'); + } + return ctx.redirect(referer); + } + return ctx.redirect(ctx.originPathname); + } -export const onRequest = defineMiddleware((ctx, next) => { - ctx.locals.user = { - name: 'Houston', - }; return next(); }); + +export const onRequest = sequence( + defineMiddleware((ctx, next) => { + ctx.locals.user = { + name: 'Houston', + }; + return next(); + }), + actionCookieForwarding, +); From 95d52a02366713f3cdfc130c05538437d6465325 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Mon, 11 Nov 2024 09:49:46 -0400 Subject: [PATCH 05/20] update markdown config docs (#12382) Co-authored-by: ArmandPhilippot <59021693+ArmandPhilippot@users.noreply.github.com> Co-authored-by: ematipico <602478+ematipico@users.noreply.github.com> Co-authored-by: jsparkdev <39112954+jsparkdev@users.noreply.github.com> Co-authored-by: bluwy <34116392+bluwy@users.noreply.github.com> --- packages/astro/src/@types/astro.ts | 49 +++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 9494605b1d02..f123436e016f 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1438,7 +1438,48 @@ export interface AstroUserConfig { * @name markdown.shikiConfig * @typeraw {Partial} * @description - * Shiki configuration options. See [the Markdown configuration docs](https://docs.astro.build/en/guides/markdown-content/#shiki-configuration) for usage. + * Shiki is our default syntax highlighter. You can configure all options via the `markdown.shikiConfig` object: + * + * ```js title="astro.config.mjs" + * import { defineConfig } from 'astro/config'; + * + * export default defineConfig({ + * markdown: { + * shikiConfig: { + * // Choose from Shiki's built-in themes (or add your own) + * // https://shiki.style/themes + * theme: 'dracula', + * // Alternatively, provide multiple themes + * // See note below for using dual light/dark themes + * themes: { + * light: 'github-light', + * dark: 'github-dark', + * }, + * // Disable the default colors + * // https://shiki.style/guide/dual-themes#without-default-color + * // (Added in v4.12.0) + * defaultColor: false, + * // Add custom languages + * // Note: Shiki has countless langs built-in, including .astro! + * // https://shiki.style/languages + * langs: [], + * // Add custom aliases for languages + * // Map an alias to a Shiki language ID: https://shiki.style/languages#bundled-languages + * // https://shiki.style/guide/load-lang#custom-language-aliases + * langAlias: { + * cjs: "javascript" + * }, + * // Enable word wrap to prevent horizontal scrolling + * wrap: true, + * // Add custom transformers: https://shiki.style/guide/transformers + * // Find common transformers: https://shiki.style/packages/transformers + * transformers: [], + * }, + * }, + * }); + * ``` + * + * See the [code syntax highlighting guide](/en/guides/syntax-highlighting/) for usage and examples. */ shikiConfig?: Partial; @@ -1448,9 +1489,9 @@ export interface AstroUserConfig { * @type {'shiki' | 'prism' | false} * @default `shiki` * @description - * Which syntax highlighter to use, if any. - * - `shiki` - use the [Shiki](https://shiki.style) highlighter - * - `prism` - use the [Prism](https://prismjs.com/) highlighter + * Which syntax highlighter to use for Markdown code blocks (\`\`\`), if any. This determines the CSS classes that Astro will apply to your Markdown code blocks. + * - `shiki` - use the [Shiki](https://shiki.style) highlighter (`github-dark` theme configured by default) + * - `prism` - use the [Prism](https://prismjs.com/) highlighter and [provide your own Prism stylesheet](/en/guides/syntax-highlighting/#add-a-prism-stylesheet) * - `false` - do not apply syntax highlighting. * * ```js From 471e7127a3a92f3008a71aa25a093e5f9b309176 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Mon, 11 Nov 2024 13:50:33 +0000 Subject: [PATCH 06/20] [ci] format --- packages/astro/src/@types/astro.ts | 76 +++++++++++++++--------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index f123436e016f..1dae362df0d5 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1439,47 +1439,47 @@ export interface AstroUserConfig { * @typeraw {Partial} * @description * Shiki is our default syntax highlighter. You can configure all options via the `markdown.shikiConfig` object: - * + * * ```js title="astro.config.mjs" - * import { defineConfig } from 'astro/config'; - * - * export default defineConfig({ + * import { defineConfig } from 'astro/config'; + * + * export default defineConfig({ * markdown: { - * shikiConfig: { + * shikiConfig: { * // Choose from Shiki's built-in themes (or add your own) - * // https://shiki.style/themes - * theme: 'dracula', - * // Alternatively, provide multiple themes - * // See note below for using dual light/dark themes - * themes: { - * light: 'github-light', - * dark: 'github-dark', - * }, - * // Disable the default colors - * // https://shiki.style/guide/dual-themes#without-default-color - * // (Added in v4.12.0) - * defaultColor: false, - * // Add custom languages - * // Note: Shiki has countless langs built-in, including .astro! - * // https://shiki.style/languages - * langs: [], - * // Add custom aliases for languages - * // Map an alias to a Shiki language ID: https://shiki.style/languages#bundled-languages - * // https://shiki.style/guide/load-lang#custom-language-aliases - * langAlias: { - * cjs: "javascript" - * }, - * // Enable word wrap to prevent horizontal scrolling - * wrap: true, - * // Add custom transformers: https://shiki.style/guide/transformers - * // Find common transformers: https://shiki.style/packages/transformers - * transformers: [], - * }, - * }, - * }); - * ``` - * - * See the [code syntax highlighting guide](/en/guides/syntax-highlighting/) for usage and examples. + * // https://shiki.style/themes + * theme: 'dracula', + * // Alternatively, provide multiple themes + * // See note below for using dual light/dark themes + * themes: { + * light: 'github-light', + * dark: 'github-dark', + * }, + * // Disable the default colors + * // https://shiki.style/guide/dual-themes#without-default-color + * // (Added in v4.12.0) + * defaultColor: false, + * // Add custom languages + * // Note: Shiki has countless langs built-in, including .astro! + * // https://shiki.style/languages + * langs: [], + * // Add custom aliases for languages + * // Map an alias to a Shiki language ID: https://shiki.style/languages#bundled-languages + * // https://shiki.style/guide/load-lang#custom-language-aliases + * langAlias: { + * cjs: "javascript" + * }, + * // Enable word wrap to prevent horizontal scrolling + * wrap: true, + * // Add custom transformers: https://shiki.style/guide/transformers + * // Find common transformers: https://shiki.style/packages/transformers + * transformers: [], + * }, + * }, + * }); + * ``` + * + * See the [code syntax highlighting guide](/en/guides/syntax-highlighting/) for usage and examples. */ shikiConfig?: Partial; From bdb75a87f24d7f032797483164fb2f82aa691fee Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 11 Nov 2024 15:03:27 +0000 Subject: [PATCH 07/20] fix(routing): emit error for forbidden rewrite (#12339) Co-authored-by: Reuben Tier <64310361+TheOtterlord@users.noreply.github.com> Co-authored-by: Sarah Rainsberger Co-authored-by: Bjorn Lu Co-authored-by: Florian Lefebvre Co-authored-by: Reuben Tier Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> --- .changeset/proud-games-repair.md | 7 ++++ packages/astro/src/core/errors/errors-data.ts | 17 ++++++++++ .../astro/src/core/middleware/sequence.ts | 18 +++++++++++ packages/astro/src/core/render-context.ts | 32 +++++++++++++++++++ .../src/pages/forbidden/dynamic.astro | 4 +++ .../src/pages/forbidden/static.astro | 3 ++ packages/astro/test/rewrite.test.js | 7 ++++ 7 files changed, 88 insertions(+) create mode 100644 .changeset/proud-games-repair.md create mode 100644 packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro create mode 100644 packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro diff --git a/.changeset/proud-games-repair.md b/.changeset/proud-games-repair.md new file mode 100644 index 000000000000..8c342e2a3985 --- /dev/null +++ b/.changeset/proud-games-repair.md @@ -0,0 +1,7 @@ +--- +'astro': patch +--- + +Adds an error when `Astro.rewrite()` is used to rewrite an on-demand route with a static route when using the `"server"` output. + +This is a forbidden rewrite because Astro can't retrieve the emitted static route at runtime. This route is served by the hosting platform, and not Astro itself. diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 6b3c7c141ab2..7c5479a665f5 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -1260,6 +1260,23 @@ export const RewriteWithBodyUsed = { 'Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request.', } satisfies ErrorData; +/** + * @docs + * @description + * `Astro.rewrite()` can't be used to rewrite an on-demand route with a static route when using the `"server"` output. + * + */ +export const ForbiddenRewrite = { + name: 'ForbiddenRewrite', + title: 'Forbidden rewrite to a static route.', + message: (from: string, to: string, component: string) => + `You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. \n\nThe static route '${to}' is rendered by the component +'${component}', which is marked as prerendered. This is a forbidden operation because during the build the component '${component}' is compiled to an +HTML file, which can't be retrieved at runtime by Astro.`, + hint: (component: string) => + `Add \`export const prerender = false\` to the component '${component}', or use a Astro.redirect().`, +} satisfies ErrorData; + /** * @docs * @description diff --git a/packages/astro/src/core/middleware/sequence.ts b/packages/astro/src/core/middleware/sequence.ts index 1fbba7c6600c..99d506695ea3 100644 --- a/packages/astro/src/core/middleware/sequence.ts +++ b/packages/astro/src/core/middleware/sequence.ts @@ -1,6 +1,8 @@ import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js'; import type { APIContext } from '../../types/public/context.js'; import { AstroCookies } from '../cookies/cookies.js'; +import { ForbiddenRewrite } from '../errors/errors-data.js'; +import { AstroError } from '../errors/index.js'; import { apiContextRoutesSymbol } from '../render-context.js'; import { type Pipeline, getParams } from '../render/index.js'; import { defineMiddleware } from './index.js'; @@ -49,6 +51,22 @@ export function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler { payload, handleContext.request, ); + + // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG). + // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file, + // so Astro can't retrieve it from the emitted manifest. + if ( + pipeline.serverLike === true && + handleContext.isPrerendered === false && + routeData.prerender === true + ) { + throw new AstroError({ + ...ForbiddenRewrite, + message: ForbiddenRewrite.message(pathname, pathname, routeData.component), + hint: ForbiddenRewrite.hint(routeData.component), + }); + } + carriedPayload = payload; handleContext.request = newRequest; handleContext.url = new URL(newRequest.url); diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index 49f174c33f01..691613412e91 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -23,6 +23,7 @@ import { } from './constants.js'; import { AstroCookies, attachCookiesToResponse } from './cookies/index.js'; import { getCookiesFromResponse } from './cookies/response.js'; +import { ForbiddenRewrite } from './errors/errors-data.js'; import { AstroError, AstroErrorData } from './errors/index.js'; import { callMiddleware } from './middleware/callMiddleware.js'; import { sequence } from './middleware/index.js'; @@ -145,6 +146,22 @@ export class RenderContext { pathname, newUrl, } = await pipeline.tryRewrite(payload, this.request); + + // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG). + // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file, + // so Astro can't retrieve it from the emitted manifest. + if ( + this.pipeline.serverLike === true && + this.routeData.prerender === false && + routeData.prerender === true + ) { + throw new AstroError({ + ...ForbiddenRewrite, + message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component), + hint: ForbiddenRewrite.hint(routeData.component), + }); + } + this.routeData = routeData; componentInstance = newComponent; if (payload instanceof Request) { @@ -246,6 +263,21 @@ export class RenderContext { reroutePayload, this.request, ); + // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG). + // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file, + // so Astro can't retrieve it from the emitted manifest. + if ( + this.pipeline.serverLike === true && + this.routeData.prerender === false && + routeData.prerender === true + ) { + throw new AstroError({ + ...ForbiddenRewrite, + message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component), + hint: ForbiddenRewrite.hint(routeData.component), + }); + } + this.routeData = routeData; if (reroutePayload instanceof Request) { this.request = reroutePayload; diff --git a/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro new file mode 100644 index 000000000000..3c23c9539196 --- /dev/null +++ b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro @@ -0,0 +1,4 @@ +--- +return Astro.rewrite("/forbidden/static") +export const prerender = false +--- diff --git a/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro new file mode 100644 index 000000000000..3a91bda712f1 --- /dev/null +++ b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro @@ -0,0 +1,3 @@ +--- +export const prerender = true +--- diff --git a/packages/astro/test/rewrite.test.js b/packages/astro/test/rewrite.test.js index 273e11d0730d..cc7508081092 100644 --- a/packages/astro/test/rewrite.test.js +++ b/packages/astro/test/rewrite.test.js @@ -149,6 +149,13 @@ describe('Dev rewrite, hybrid/server', () => { assert.equal($('title').text(), 'RewriteWithBodyUsed'); }); + + it('should error when rewriting from a SSR route to a SSG route', async () => { + const html = await fixture.fetch('/forbidden/dynamic').then((res) => res.text()); + const $ = cheerioLoad(html); + + assert.match($('title').text(), /ForbiddenRewrite/); + }); }); describe('Build reroute', () => { From e723e9e8ea21531b5200b22a14aca333b3a0580d Mon Sep 17 00:00:00 2001 From: "Houston (Bot)" <108291165+astrobot-houston@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:00:58 -0800 Subject: [PATCH 08/20] [ci] release (#12403) Co-authored-by: github-actions[bot] --- .changeset/breezy-plums-clap.md | 5 --- .changeset/dull-lemons-check.md | 14 ------ .changeset/nine-mayflies-film.md | 5 --- examples/basics/package.json | 2 +- examples/blog/package.json | 2 +- examples/component/package.json | 2 +- examples/container-with-vitest/package.json | 2 +- examples/framework-alpine/package.json | 2 +- examples/framework-lit/package.json | 2 +- examples/framework-multiple/package.json | 2 +- examples/framework-preact/package.json | 2 +- examples/framework-react/package.json | 2 +- examples/framework-solid/package.json | 2 +- examples/framework-svelte/package.json | 2 +- examples/framework-vue/package.json | 2 +- examples/hackernews/package.json | 2 +- examples/integration/package.json | 2 +- examples/minimal/package.json | 2 +- examples/portfolio/package.json | 2 +- examples/ssr/package.json | 2 +- examples/starlog/package.json | 2 +- examples/toolbar-app/package.json | 2 +- examples/with-markdoc/package.json | 2 +- examples/with-mdx/package.json | 2 +- examples/with-nanostores/package.json | 2 +- examples/with-tailwindcss/package.json | 2 +- examples/with-vitest/package.json | 2 +- packages/astro/CHANGELOG.md | 19 ++++++++ packages/astro/package.json | 2 +- pnpm-lock.yaml | 48 ++++++++++----------- 30 files changed, 68 insertions(+), 73 deletions(-) delete mode 100644 .changeset/breezy-plums-clap.md delete mode 100644 .changeset/dull-lemons-check.md delete mode 100644 .changeset/nine-mayflies-film.md diff --git a/.changeset/breezy-plums-clap.md b/.changeset/breezy-plums-clap.md deleted file mode 100644 index d6d3f4b26aa8..000000000000 --- a/.changeset/breezy-plums-clap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'astro': patch ---- - -Fixes a case where the error overlay would not escape the message diff --git a/.changeset/dull-lemons-check.md b/.changeset/dull-lemons-check.md deleted file mode 100644 index c2c51c412088..000000000000 --- a/.changeset/dull-lemons-check.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'astro': patch ---- - -Fixes a case where Astro allowed to call an action without using `Astro.callAction`. This is now invalid, and Astro will show a proper error. - -```diff ---- -import { actions } from "astro:actions"; - --const result = actions.getUser({ userId: 123 }); -+const result = Astro.callAction(actions.getUser, { userId: 123 }); ---- -``` diff --git a/.changeset/nine-mayflies-film.md b/.changeset/nine-mayflies-film.md deleted file mode 100644 index c782c73bc443..000000000000 --- a/.changeset/nine-mayflies-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'astro': patch ---- - -Fixes unexpected 200 status in dev server logs for action errors and redirects. diff --git a/examples/basics/package.json b/examples/basics/package.json index 84d2260ded8d..9c64cd2b8bc0 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/blog/package.json b/examples/blog/package.json index 4a36ac4be146..3207e29547ef 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -14,6 +14,6 @@ "@astrojs/mdx": "^3.1.9", "@astrojs/rss": "^4.0.9", "@astrojs/sitemap": "^3.2.1", - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/component/package.json b/examples/component/package.json index b3d65ba7654f..00b21725de4c 100644 --- a/examples/component/package.json +++ b/examples/component/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" }, "peerDependencies": { "astro": "^4.0.0" diff --git a/examples/container-with-vitest/package.json b/examples/container-with-vitest/package.json index 9b8ad347a3d5..209517b3c109 100644 --- a/examples/container-with-vitest/package.json +++ b/examples/container-with-vitest/package.json @@ -12,7 +12,7 @@ "test": "vitest run" }, "dependencies": { - "astro": "^4.16.10", + "astro": "^4.16.11", "@astrojs/react": "^3.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-alpine/package.json b/examples/framework-alpine/package.json index 63b0ab0d2281..7216c73d14fb 100644 --- a/examples/framework-alpine/package.json +++ b/examples/framework-alpine/package.json @@ -14,6 +14,6 @@ "@astrojs/alpinejs": "^0.4.0", "@types/alpinejs": "^3.13.10", "alpinejs": "^3.14.3", - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/framework-lit/package.json b/examples/framework-lit/package.json index 951632a5397f..c57bfb792c25 100644 --- a/examples/framework-lit/package.json +++ b/examples/framework-lit/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/lit": "^4.3.0", "@webcomponents/template-shadowroot": "^0.2.1", - "astro": "^4.16.10", + "astro": "^4.16.11", "lit": "^3.2.1" } } diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json index d742d06097a9..ae33ce0cffb9 100644 --- a/examples/framework-multiple/package.json +++ b/examples/framework-multiple/package.json @@ -18,7 +18,7 @@ "@astrojs/vue": "^4.5.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.10", + "astro": "^4.16.11", "preact": "^10.24.3", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-preact/package.json b/examples/framework-preact/package.json index 3f0d52d1ccf5..6ab39cf91ed2 100644 --- a/examples/framework-preact/package.json +++ b/examples/framework-preact/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@preact/signals": "^1.3.0", - "astro": "^4.16.10", + "astro": "^4.16.11", "preact": "^10.24.3" } } diff --git a/examples/framework-react/package.json b/examples/framework-react/package.json index b5491d77cc0e..f1e5c4f217c1 100644 --- a/examples/framework-react/package.json +++ b/examples/framework-react/package.json @@ -14,7 +14,7 @@ "@astrojs/react": "^3.6.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.10", + "astro": "^4.16.11", "react": "^18.3.1", "react-dom": "^18.3.1" } diff --git a/examples/framework-solid/package.json b/examples/framework-solid/package.json index e67c3f573c93..5565c2ccf01a 100644 --- a/examples/framework-solid/package.json +++ b/examples/framework-solid/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/solid-js": "^4.4.2", - "astro": "^4.16.10", + "astro": "^4.16.11", "solid-js": "^1.9.3" } } diff --git a/examples/framework-svelte/package.json b/examples/framework-svelte/package.json index 266dfb85a7c1..1f29535e7511 100644 --- a/examples/framework-svelte/package.json +++ b/examples/framework-svelte/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/svelte": "^5.7.3", - "astro": "^4.16.10", + "astro": "^4.16.11", "svelte": "^4.2.19" } } diff --git a/examples/framework-vue/package.json b/examples/framework-vue/package.json index 10da8e798381..a5e7b2689586 100644 --- a/examples/framework-vue/package.json +++ b/examples/framework-vue/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/vue": "^4.5.2", - "astro": "^4.16.10", + "astro": "^4.16.11", "vue": "^3.5.12" } } diff --git a/examples/hackernews/package.json b/examples/hackernews/package.json index 534898a39de5..ebfff074be9c 100644 --- a/examples/hackernews/package.json +++ b/examples/hackernews/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/node": "^8.3.4", - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/integration/package.json b/examples/integration/package.json index 63697a0e24c3..846ac11773ab 100644 --- a/examples/integration/package.json +++ b/examples/integration/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" }, "peerDependencies": { "astro": "^4.0.0" diff --git a/examples/minimal/package.json b/examples/minimal/package.json index 5ba444a9e410..9c01f84ee732 100644 --- a/examples/minimal/package.json +++ b/examples/minimal/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/portfolio/package.json b/examples/portfolio/package.json index 861fb95c69c4..f20d2eae917f 100644 --- a/examples/portfolio/package.json +++ b/examples/portfolio/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/ssr/package.json b/examples/ssr/package.json index aa06d247e2c6..e79a746c4591 100644 --- a/examples/ssr/package.json +++ b/examples/ssr/package.json @@ -14,7 +14,7 @@ "dependencies": { "@astrojs/node": "^8.3.4", "@astrojs/svelte": "^5.7.3", - "astro": "^4.16.10", + "astro": "^4.16.11", "svelte": "^4.2.19" } } diff --git a/examples/starlog/package.json b/examples/starlog/package.json index 70106978bf71..df175b24ff04 100644 --- a/examples/starlog/package.json +++ b/examples/starlog/package.json @@ -10,7 +10,7 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.10", + "astro": "^4.16.11", "sass": "^1.80.6", "sharp": "^0.33.3" } diff --git a/examples/toolbar-app/package.json b/examples/toolbar-app/package.json index 043e6a0647d0..1dc223fa6628 100644 --- a/examples/toolbar-app/package.json +++ b/examples/toolbar-app/package.json @@ -15,6 +15,6 @@ "./app": "./dist/app.js" }, "devDependencies": { - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index fb6abf4f9b63..ebe40b589520 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/markdoc": "^0.11.5", - "astro": "^4.16.10" + "astro": "^4.16.11" } } diff --git a/examples/with-mdx/package.json b/examples/with-mdx/package.json index a49d5d1292c6..8f486fb801f1 100644 --- a/examples/with-mdx/package.json +++ b/examples/with-mdx/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/mdx": "^3.1.9", "@astrojs/preact": "^3.5.3", - "astro": "^4.16.10", + "astro": "^4.16.11", "preact": "^10.24.3" } } diff --git a/examples/with-nanostores/package.json b/examples/with-nanostores/package.json index dfbdf14e597b..759a8e5e1cd5 100644 --- a/examples/with-nanostores/package.json +++ b/examples/with-nanostores/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@nanostores/preact": "^0.5.2", - "astro": "^4.16.10", + "astro": "^4.16.11", "nanostores": "^0.11.3", "preact": "^10.24.3" } diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json index 85e9efa0b322..4d8860e94a7e 100644 --- a/examples/with-tailwindcss/package.json +++ b/examples/with-tailwindcss/package.json @@ -14,7 +14,7 @@ "@astrojs/mdx": "^3.1.9", "@astrojs/tailwind": "^5.1.2", "@types/canvas-confetti": "^1.6.4", - "astro": "^4.16.10", + "astro": "^4.16.11", "autoprefixer": "^10.4.20", "canvas-confetti": "^1.9.3", "postcss": "^8.4.47", diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json index 4c7391979a10..7778c8de2c9b 100644 --- a/examples/with-vitest/package.json +++ b/examples/with-vitest/package.json @@ -12,7 +12,7 @@ "test": "vitest" }, "dependencies": { - "astro": "^4.16.10", + "astro": "^4.16.11", "vitest": "^2.1.4" } } diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index d96ac516ba2e..50d2f4263142 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,24 @@ # astro +## 4.16.11 + +### Patch Changes + +- [#12305](https://github.com/withastro/astro/pull/12305) [`f5f7109`](https://github.com/withastro/astro/commit/f5f71094ec74961b4cca2ee451798abd830c617a) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Fixes a case where the error overlay would not escape the message + +- [#12402](https://github.com/withastro/astro/pull/12402) [`823e73b`](https://github.com/withastro/astro/commit/823e73b164eab4115af31b1de8e978f2b4e0a95d) Thanks [@ematipico](https://github.com/ematipico)! - Fixes a case where Astro allowed to call an action without using `Astro.callAction`. This is now invalid, and Astro will show a proper error. + + ```diff + --- + import { actions } from "astro:actions"; + + -const result = actions.getUser({ userId: 123 }); + +const result = Astro.callAction(actions.getUser, { userId: 123 }); + --- + ``` + +- [#12401](https://github.com/withastro/astro/pull/12401) [`9cca108`](https://github.com/withastro/astro/commit/9cca10843912698e13d35f1bc3c493e2c96a06ee) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Fixes unexpected 200 status in dev server logs for action errors and redirects. + ## 4.16.10 ### Patch Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index c13e8f7fb85f..4534f1ce057b 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "4.16.10", + "version": "4.16.11", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "type": "module", "author": "withastro", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f21e9c712c55..3d5ed93de55d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,7 +139,7 @@ importers: examples/basics: dependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/blog: @@ -154,13 +154,13 @@ importers: specifier: ^3.2.1 version: link:../../packages/integrations/sitemap astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/component: devDependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/container-with-vitest: @@ -169,7 +169,7 @@ importers: specifier: ^3.6.2 version: link:../../packages/integrations/react astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -200,7 +200,7 @@ importers: specifier: ^3.14.3 version: 3.14.3 astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/framework-lit: @@ -212,7 +212,7 @@ importers: specifier: ^0.2.1 version: 0.2.1 astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro lit: specifier: ^3.2.1 @@ -242,7 +242,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -272,7 +272,7 @@ importers: specifier: ^1.3.0 version: 1.3.0(preact@10.24.3) astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -290,7 +290,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -305,7 +305,7 @@ importers: specifier: ^4.4.2 version: link:../../packages/integrations/solid astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro solid-js: specifier: ^1.9.3 @@ -317,7 +317,7 @@ importers: specifier: ^5.7.3 version: link:../../packages/integrations/svelte astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -329,7 +329,7 @@ importers: specifier: ^4.5.2 version: link:../../packages/integrations/vue astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro vue: specifier: ^3.5.12 @@ -341,25 +341,25 @@ importers: specifier: ^8.3.4 version: 8.3.4(astro@packages+astro) astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/integration: devDependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/minimal: dependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/portfolio: dependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/ssr: @@ -371,7 +371,7 @@ importers: specifier: ^5.7.3 version: link:../../packages/integrations/svelte astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -380,7 +380,7 @@ importers: examples/starlog: dependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro sass: specifier: ^1.80.6 @@ -392,7 +392,7 @@ importers: examples/toolbar-app: devDependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/with-markdoc: @@ -401,7 +401,7 @@ importers: specifier: ^0.11.5 version: link:../../packages/integrations/markdoc astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro examples/with-mdx: @@ -413,7 +413,7 @@ importers: specifier: ^3.5.3 version: link:../../packages/integrations/preact astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -428,7 +428,7 @@ importers: specifier: ^0.5.2 version: 0.5.2(nanostores@0.11.3)(preact@10.24.3) astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro nanostores: specifier: ^0.11.3 @@ -449,7 +449,7 @@ importers: specifier: ^1.6.4 version: 1.6.4 astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro autoprefixer: specifier: ^10.4.20 @@ -467,7 +467,7 @@ importers: examples/with-vitest: dependencies: astro: - specifier: ^4.16.10 + specifier: ^4.16.11 version: link:../../packages/astro vitest: specifier: ^2.1.4 From acac0af53466f8a381ccdac29ed2ad735d7b4e79 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 13 Nov 2024 13:34:35 +0000 Subject: [PATCH 09/20] fix(routing): middleware in dev (#12420) --- .changeset/spicy-ties-matter.md | 5 +++++ packages/astro/src/core/constants.ts | 5 +++++ .../src/core/middleware/noop-middleware.ts | 6 +++++- .../src/vite-plugin-astro-server/route.ts | 19 ++++++++++++------- .../middleware space/src/middleware.js | 11 ++++++++++- packages/astro/test/middleware.test.js | 15 +++++++++++++++ 6 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 .changeset/spicy-ties-matter.md diff --git a/.changeset/spicy-ties-matter.md b/.changeset/spicy-ties-matter.md new file mode 100644 index 000000000000..48173596af10 --- /dev/null +++ b/.changeset/spicy-ties-matter.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue where the dev server returns a 404 status code when a user middleware returns a valid `Response`. diff --git a/packages/astro/src/core/constants.ts b/packages/astro/src/core/constants.ts index 6183e155721c..dc09a1f69474 100644 --- a/packages/astro/src/core/constants.ts +++ b/packages/astro/src/core/constants.ts @@ -31,6 +31,11 @@ export const REWRITE_DIRECTIVE_HEADER_KEY = 'X-Astro-Rewrite'; export const REWRITE_DIRECTIVE_HEADER_VALUE = 'yes'; +/** + * This header is set by the no-op Astro middleware. + */ +export const NOOP_MIDDLEWARE_HEADER = 'X-Astro-Noop'; + /** * The name for the header used to help i18n middleware, which only needs to act on "page" and "fallback" route types. */ diff --git a/packages/astro/src/core/middleware/noop-middleware.ts b/packages/astro/src/core/middleware/noop-middleware.ts index bf5f10d89054..b141285f699c 100644 --- a/packages/astro/src/core/middleware/noop-middleware.ts +++ b/packages/astro/src/core/middleware/noop-middleware.ts @@ -1,3 +1,7 @@ import type { MiddlewareHandler } from '../../@types/astro.js'; +import { NOOP_MIDDLEWARE_HEADER } from '../constants.js'; -export const NOOP_MIDDLEWARE_FN: MiddlewareHandler = (_, next) => next(); +export const NOOP_MIDDLEWARE_FN: MiddlewareHandler = (ctx, next) => { + ctx.request.headers.set(NOOP_MIDDLEWARE_HEADER, 'true'); + return next(); +}; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 9f6d434b55e8..8de52d158bb0 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -2,6 +2,7 @@ import type http from 'node:http'; import type { ComponentInstance, ManifestData, RouteData } from '../@types/astro.js'; import { DEFAULT_404_COMPONENT, + NOOP_MIDDLEWARE_HEADER, REROUTE_DIRECTIVE_HEADER, REWRITE_DIRECTIVE_HEADER_KEY, clientLocalsSymbol, @@ -191,16 +192,11 @@ export async function handleRoute({ mod = preloadedComponent; - const isDefaultPrerendered404 = - matchedRoute.route.route === '/404' && - matchedRoute.route.prerender && - matchedRoute.route.component === DEFAULT_404_COMPONENT; - renderContext = await RenderContext.create({ locals, pipeline, pathname, - middleware: isDefaultPrerendered404 ? undefined : middleware, + middleware: isDefaultPrerendered404(matchedRoute.route) ? undefined : middleware, request, routeData: route, }); @@ -213,10 +209,15 @@ export async function handleRoute({ response = await renderContext.render(mod); isReroute = response.headers.has(REROUTE_DIRECTIVE_HEADER); isRewrite = response.headers.has(REWRITE_DIRECTIVE_HEADER_KEY); + const statusCodedMatched = getStatusByMatchedRoute(matchedRoute); statusCode = isRewrite ? // Ignore `matchedRoute` status for rewrites response.status - : (getStatusByMatchedRoute(matchedRoute) ?? response.status); + : // Our internal noop middleware sets a particular header. If the header isn't present, it means that the user have + // their own middleware, so we need to return what the user returns. + !response.headers.has(NOOP_MIDDLEWARE_HEADER) && !isReroute + ? response.status + : (statusCodedMatched ?? response.status); } catch (err: any) { const custom500 = getCustom500Route(manifestData); if (!custom500) { @@ -308,3 +309,7 @@ function getStatusByMatchedRoute(matchedRoute?: MatchedRoute) { if (matchedRoute?.route.route === '/500') return 500; return undefined; } + +function isDefaultPrerendered404(route: RouteData) { + return route.route === '/404' && route.prerender && route.component === DEFAULT_404_COMPONENT; +} diff --git a/packages/astro/test/fixtures/middleware space/src/middleware.js b/packages/astro/test/fixtures/middleware space/src/middleware.js index 6310994554ea..185fc31168e7 100644 --- a/packages/astro/test/fixtures/middleware space/src/middleware.js +++ b/packages/astro/test/fixtures/middleware space/src/middleware.js @@ -74,4 +74,13 @@ const third = defineMiddleware(async (context, next) => { return next(); }); -export const onRequest = sequence(first, second, third); +const fourth = defineMiddleware((context, next) => { + if (context.request.url.includes('/no-route-but-200')) { + return new Response("It's OK!", { + status: 200 + }); + } + return next() +}) + +export const onRequest = sequence(first, second, third, fourth); diff --git a/packages/astro/test/middleware.test.js b/packages/astro/test/middleware.test.js index 612937d80433..2bcfe8d57d41 100644 --- a/packages/astro/test/middleware.test.js +++ b/packages/astro/test/middleware.test.js @@ -70,6 +70,13 @@ describe('Middleware in DEV mode', () => { assert.equal($('title').html(), 'MiddlewareNoDataOrNextCalled'); }); + it('should return 200 if the middleware returns a 200 Response', async () => { + const response = await fixture.fetch('/no-route-but-200'); + assert.equal(response.status, 200); + const html = await response.text(); + assert.match(html, /It's OK!/); + }); + it('should allow setting cookies', async () => { const res = await fixture.fetch('/'); assert.equal(res.headers.get('set-cookie'), 'foo=bar'); @@ -239,6 +246,14 @@ describe('Middleware API in PROD mode, SSR', () => { assert.notEqual($('title').html(), 'MiddlewareNoDataReturned'); }); + it('should return 200 if the middleware returns a 200 Response', async () => { + const request = new Request('http://example.com/no-route-but-200'); + const response = await app.render(request); + assert.equal(response.status, 200); + const html = await response.text(); + assert.match(html, /It's OK!/); + }); + it('should correctly work for API endpoints that return a Response object', async () => { const request = new Request('http://example.com/api/endpoint'); const response = await app.render(request); From 3b3bc9b8cd050bb27b5e4d51dd02cbcbd27c76f0 Mon Sep 17 00:00:00 2001 From: "Houston (Bot)" <108291165+astrobot-houston@users.noreply.github.com> Date: Wed, 13 Nov 2024 06:21:47 -0800 Subject: [PATCH 10/20] [ci] release (#12422) Co-authored-by: github-actions[bot] --- .changeset/spicy-ties-matter.md | 5 --- examples/basics/package.json | 2 +- examples/blog/package.json | 2 +- examples/component/package.json | 2 +- examples/container-with-vitest/package.json | 2 +- examples/framework-alpine/package.json | 2 +- examples/framework-lit/package.json | 2 +- examples/framework-multiple/package.json | 2 +- examples/framework-preact/package.json | 2 +- examples/framework-react/package.json | 2 +- examples/framework-solid/package.json | 2 +- examples/framework-svelte/package.json | 2 +- examples/framework-vue/package.json | 2 +- examples/hackernews/package.json | 2 +- examples/integration/package.json | 2 +- examples/minimal/package.json | 2 +- examples/portfolio/package.json | 2 +- examples/ssr/package.json | 2 +- examples/starlog/package.json | 2 +- examples/toolbar-app/package.json | 2 +- examples/with-markdoc/package.json | 2 +- examples/with-mdx/package.json | 2 +- examples/with-nanostores/package.json | 2 +- examples/with-tailwindcss/package.json | 2 +- examples/with-vitest/package.json | 2 +- packages/astro/CHANGELOG.md | 6 +++ packages/astro/package.json | 2 +- pnpm-lock.yaml | 48 ++++++++++----------- 28 files changed, 55 insertions(+), 54 deletions(-) delete mode 100644 .changeset/spicy-ties-matter.md diff --git a/.changeset/spicy-ties-matter.md b/.changeset/spicy-ties-matter.md deleted file mode 100644 index 48173596af10..000000000000 --- a/.changeset/spicy-ties-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'astro': patch ---- - -Fixes an issue where the dev server returns a 404 status code when a user middleware returns a valid `Response`. diff --git a/examples/basics/package.json b/examples/basics/package.json index 9c64cd2b8bc0..bb6ca0e5dbfa 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/blog/package.json b/examples/blog/package.json index 3207e29547ef..d7e3755bfb21 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -14,6 +14,6 @@ "@astrojs/mdx": "^3.1.9", "@astrojs/rss": "^4.0.9", "@astrojs/sitemap": "^3.2.1", - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/component/package.json b/examples/component/package.json index 00b21725de4c..bef28130fe32 100644 --- a/examples/component/package.json +++ b/examples/component/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" }, "peerDependencies": { "astro": "^4.0.0" diff --git a/examples/container-with-vitest/package.json b/examples/container-with-vitest/package.json index 209517b3c109..a67eefdad308 100644 --- a/examples/container-with-vitest/package.json +++ b/examples/container-with-vitest/package.json @@ -12,7 +12,7 @@ "test": "vitest run" }, "dependencies": { - "astro": "^4.16.11", + "astro": "^4.16.12", "@astrojs/react": "^3.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-alpine/package.json b/examples/framework-alpine/package.json index 7216c73d14fb..f9d0bf2340e6 100644 --- a/examples/framework-alpine/package.json +++ b/examples/framework-alpine/package.json @@ -14,6 +14,6 @@ "@astrojs/alpinejs": "^0.4.0", "@types/alpinejs": "^3.13.10", "alpinejs": "^3.14.3", - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/framework-lit/package.json b/examples/framework-lit/package.json index c57bfb792c25..66730f1ad759 100644 --- a/examples/framework-lit/package.json +++ b/examples/framework-lit/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/lit": "^4.3.0", "@webcomponents/template-shadowroot": "^0.2.1", - "astro": "^4.16.11", + "astro": "^4.16.12", "lit": "^3.2.1" } } diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json index ae33ce0cffb9..1e63e0247928 100644 --- a/examples/framework-multiple/package.json +++ b/examples/framework-multiple/package.json @@ -18,7 +18,7 @@ "@astrojs/vue": "^4.5.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.11", + "astro": "^4.16.12", "preact": "^10.24.3", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-preact/package.json b/examples/framework-preact/package.json index 6ab39cf91ed2..075f2287c9d8 100644 --- a/examples/framework-preact/package.json +++ b/examples/framework-preact/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@preact/signals": "^1.3.0", - "astro": "^4.16.11", + "astro": "^4.16.12", "preact": "^10.24.3" } } diff --git a/examples/framework-react/package.json b/examples/framework-react/package.json index f1e5c4f217c1..607ffdf2e64a 100644 --- a/examples/framework-react/package.json +++ b/examples/framework-react/package.json @@ -14,7 +14,7 @@ "@astrojs/react": "^3.6.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.11", + "astro": "^4.16.12", "react": "^18.3.1", "react-dom": "^18.3.1" } diff --git a/examples/framework-solid/package.json b/examples/framework-solid/package.json index 5565c2ccf01a..4445b4a102d7 100644 --- a/examples/framework-solid/package.json +++ b/examples/framework-solid/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/solid-js": "^4.4.2", - "astro": "^4.16.11", + "astro": "^4.16.12", "solid-js": "^1.9.3" } } diff --git a/examples/framework-svelte/package.json b/examples/framework-svelte/package.json index 1f29535e7511..9c1adf7cf9c6 100644 --- a/examples/framework-svelte/package.json +++ b/examples/framework-svelte/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/svelte": "^5.7.3", - "astro": "^4.16.11", + "astro": "^4.16.12", "svelte": "^4.2.19" } } diff --git a/examples/framework-vue/package.json b/examples/framework-vue/package.json index a5e7b2689586..6f958985a460 100644 --- a/examples/framework-vue/package.json +++ b/examples/framework-vue/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/vue": "^4.5.2", - "astro": "^4.16.11", + "astro": "^4.16.12", "vue": "^3.5.12" } } diff --git a/examples/hackernews/package.json b/examples/hackernews/package.json index ebfff074be9c..da868174d01c 100644 --- a/examples/hackernews/package.json +++ b/examples/hackernews/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/node": "^8.3.4", - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/integration/package.json b/examples/integration/package.json index 846ac11773ab..86bfeaa8decd 100644 --- a/examples/integration/package.json +++ b/examples/integration/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" }, "peerDependencies": { "astro": "^4.0.0" diff --git a/examples/minimal/package.json b/examples/minimal/package.json index 9c01f84ee732..3af139efdc3c 100644 --- a/examples/minimal/package.json +++ b/examples/minimal/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/portfolio/package.json b/examples/portfolio/package.json index f20d2eae917f..02c2c8384e2e 100644 --- a/examples/portfolio/package.json +++ b/examples/portfolio/package.json @@ -11,6 +11,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/ssr/package.json b/examples/ssr/package.json index e79a746c4591..6d937bf2f408 100644 --- a/examples/ssr/package.json +++ b/examples/ssr/package.json @@ -14,7 +14,7 @@ "dependencies": { "@astrojs/node": "^8.3.4", "@astrojs/svelte": "^5.7.3", - "astro": "^4.16.11", + "astro": "^4.16.12", "svelte": "^4.2.19" } } diff --git a/examples/starlog/package.json b/examples/starlog/package.json index df175b24ff04..cf9aee8bad53 100644 --- a/examples/starlog/package.json +++ b/examples/starlog/package.json @@ -10,7 +10,7 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.16.11", + "astro": "^4.16.12", "sass": "^1.80.6", "sharp": "^0.33.3" } diff --git a/examples/toolbar-app/package.json b/examples/toolbar-app/package.json index 1dc223fa6628..e8a23892ec5e 100644 --- a/examples/toolbar-app/package.json +++ b/examples/toolbar-app/package.json @@ -15,6 +15,6 @@ "./app": "./dist/app.js" }, "devDependencies": { - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index ebe40b589520..bca2443b1c2c 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/markdoc": "^0.11.5", - "astro": "^4.16.11" + "astro": "^4.16.12" } } diff --git a/examples/with-mdx/package.json b/examples/with-mdx/package.json index 8f486fb801f1..70ba88b2fbb1 100644 --- a/examples/with-mdx/package.json +++ b/examples/with-mdx/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/mdx": "^3.1.9", "@astrojs/preact": "^3.5.3", - "astro": "^4.16.11", + "astro": "^4.16.12", "preact": "^10.24.3" } } diff --git a/examples/with-nanostores/package.json b/examples/with-nanostores/package.json index 759a8e5e1cd5..557adbaf1445 100644 --- a/examples/with-nanostores/package.json +++ b/examples/with-nanostores/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@nanostores/preact": "^0.5.2", - "astro": "^4.16.11", + "astro": "^4.16.12", "nanostores": "^0.11.3", "preact": "^10.24.3" } diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json index 4d8860e94a7e..901f494c6546 100644 --- a/examples/with-tailwindcss/package.json +++ b/examples/with-tailwindcss/package.json @@ -14,7 +14,7 @@ "@astrojs/mdx": "^3.1.9", "@astrojs/tailwind": "^5.1.2", "@types/canvas-confetti": "^1.6.4", - "astro": "^4.16.11", + "astro": "^4.16.12", "autoprefixer": "^10.4.20", "canvas-confetti": "^1.9.3", "postcss": "^8.4.47", diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json index 7778c8de2c9b..21ca1eac0a06 100644 --- a/examples/with-vitest/package.json +++ b/examples/with-vitest/package.json @@ -12,7 +12,7 @@ "test": "vitest" }, "dependencies": { - "astro": "^4.16.11", + "astro": "^4.16.12", "vitest": "^2.1.4" } } diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 50d2f4263142..87d6178e1b2e 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,11 @@ # astro +## 4.16.12 + +### Patch Changes + +- [#12420](https://github.com/withastro/astro/pull/12420) [`acac0af`](https://github.com/withastro/astro/commit/acac0af53466f8a381ccdac29ed2ad735d7b4e79) Thanks [@ematipico](https://github.com/ematipico)! - Fixes an issue where the dev server returns a 404 status code when a user middleware returns a valid `Response`. + ## 4.16.11 ### Patch Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index 4534f1ce057b..e7ddcb616e6e 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "4.16.11", + "version": "4.16.12", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "type": "module", "author": "withastro", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d5ed93de55d..ea794e1bff92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,7 +139,7 @@ importers: examples/basics: dependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/blog: @@ -154,13 +154,13 @@ importers: specifier: ^3.2.1 version: link:../../packages/integrations/sitemap astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/component: devDependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/container-with-vitest: @@ -169,7 +169,7 @@ importers: specifier: ^3.6.2 version: link:../../packages/integrations/react astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -200,7 +200,7 @@ importers: specifier: ^3.14.3 version: 3.14.3 astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/framework-lit: @@ -212,7 +212,7 @@ importers: specifier: ^0.2.1 version: 0.2.1 astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro lit: specifier: ^3.2.1 @@ -242,7 +242,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -272,7 +272,7 @@ importers: specifier: ^1.3.0 version: 1.3.0(preact@10.24.3) astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -290,7 +290,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -305,7 +305,7 @@ importers: specifier: ^4.4.2 version: link:../../packages/integrations/solid astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro solid-js: specifier: ^1.9.3 @@ -317,7 +317,7 @@ importers: specifier: ^5.7.3 version: link:../../packages/integrations/svelte astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -329,7 +329,7 @@ importers: specifier: ^4.5.2 version: link:../../packages/integrations/vue astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro vue: specifier: ^3.5.12 @@ -341,25 +341,25 @@ importers: specifier: ^8.3.4 version: 8.3.4(astro@packages+astro) astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/integration: devDependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/minimal: dependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/portfolio: dependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/ssr: @@ -371,7 +371,7 @@ importers: specifier: ^5.7.3 version: link:../../packages/integrations/svelte astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -380,7 +380,7 @@ importers: examples/starlog: dependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro sass: specifier: ^1.80.6 @@ -392,7 +392,7 @@ importers: examples/toolbar-app: devDependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/with-markdoc: @@ -401,7 +401,7 @@ importers: specifier: ^0.11.5 version: link:../../packages/integrations/markdoc astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro examples/with-mdx: @@ -413,7 +413,7 @@ importers: specifier: ^3.5.3 version: link:../../packages/integrations/preact astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -428,7 +428,7 @@ importers: specifier: ^0.5.2 version: 0.5.2(nanostores@0.11.3)(preact@10.24.3) astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro nanostores: specifier: ^0.11.3 @@ -449,7 +449,7 @@ importers: specifier: ^1.6.4 version: 1.6.4 astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro autoprefixer: specifier: ^10.4.20 @@ -467,7 +467,7 @@ importers: examples/with-vitest: dependencies: astro: - specifier: ^4.16.11 + specifier: ^4.16.12 version: link:../../packages/astro vitest: specifier: ^2.1.4 From b745e382f1c0d4d230aaba139294d86bbcec0371 Mon Sep 17 00:00:00 2001 From: "Houston (Bot)" <108291165+astrobot-houston@users.noreply.github.com> Date: Wed, 13 Nov 2024 06:32:01 -0800 Subject: [PATCH 11/20] [ci] release (beta) (#12405) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 2 + examples/basics/package.json | 2 +- examples/blog/package.json | 2 +- examples/component/package.json | 2 +- examples/container-with-vitest/package.json | 2 +- examples/framework-alpine/package.json | 2 +- examples/framework-multiple/package.json | 2 +- examples/framework-preact/package.json | 2 +- examples/framework-react/package.json | 2 +- examples/framework-solid/package.json | 2 +- examples/framework-svelte/package.json | 2 +- examples/framework-vue/package.json | 2 +- examples/hackernews/package.json | 2 +- examples/integration/package.json | 2 +- examples/minimal/package.json | 2 +- examples/portfolio/package.json | 2 +- examples/ssr/package.json | 2 +- examples/starlog/package.json | 2 +- examples/toolbar-app/package.json | 2 +- examples/with-markdoc/package.json | 2 +- examples/with-mdx/package.json | 2 +- examples/with-nanostores/package.json | 2 +- examples/with-tailwindcss/package.json | 2 +- examples/with-vitest/package.json | 2 +- packages/astro/CHANGELOG.md | 69 +++++++++++++++++++ packages/astro/package.json | 2 +- packages/astro/src/actions/integration.ts | 2 +- .../src/actions/runtime/virtual/server.ts | 12 ++-- pnpm-lock.yaml | 46 ++++++------- 29 files changed, 125 insertions(+), 54 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 7b422da6859c..a08df62b1c29 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -94,6 +94,7 @@ "poor-frogs-dream", "poor-seals-clap", "pretty-walls-camp", + "proud-games-repair", "quick-ads-exercise", "quick-onions-leave", "rotten-phones-scream", @@ -110,6 +111,7 @@ "strange-sheep-film", "strong-months-grab", "sweet-timers-smash", + "tall-waves-impress", "tame-pumpkins-swim", "tame-rats-cross", "ten-students-repair", diff --git a/examples/basics/package.json b/examples/basics/package.json index a36bc626e2ee..7073ff822afe 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -10,6 +10,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/blog/package.json b/examples/blog/package.json index 83896b87ddda..0e13ddef6dcb 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -13,6 +13,6 @@ "@astrojs/mdx": "^4.0.0-beta.3", "@astrojs/rss": "^4.0.9", "@astrojs/sitemap": "^3.2.1", - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/component/package.json b/examples/component/package.json index ada92f61dc03..27c20d0592e8 100644 --- a/examples/component/package.json +++ b/examples/component/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" }, "peerDependencies": { "astro": "^4.0.0 || ^5.0.0" diff --git a/examples/container-with-vitest/package.json b/examples/container-with-vitest/package.json index 17bbe0847818..5cb0084ac1c6 100644 --- a/examples/container-with-vitest/package.json +++ b/examples/container-with-vitest/package.json @@ -11,7 +11,7 @@ "test": "vitest run" }, "dependencies": { - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "@astrojs/react": "^3.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-alpine/package.json b/examples/framework-alpine/package.json index 7cd3d314c1bf..698088fcfe5c 100644 --- a/examples/framework-alpine/package.json +++ b/examples/framework-alpine/package.json @@ -13,6 +13,6 @@ "@astrojs/alpinejs": "^0.4.0", "@types/alpinejs": "^3.13.10", "alpinejs": "^3.14.3", - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json index f184bb9c5fb1..4bbb934655c4 100644 --- a/examples/framework-multiple/package.json +++ b/examples/framework-multiple/package.json @@ -17,7 +17,7 @@ "@astrojs/vue": "^5.0.0-beta.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "preact": "^10.24.3", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/framework-preact/package.json b/examples/framework-preact/package.json index 476d94318d53..445c836fcfff 100644 --- a/examples/framework-preact/package.json +++ b/examples/framework-preact/package.json @@ -12,7 +12,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@preact/signals": "^1.3.0", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "preact": "^10.24.3" } } diff --git a/examples/framework-react/package.json b/examples/framework-react/package.json index 084ceb7e390a..b0541cba7cd4 100644 --- a/examples/framework-react/package.json +++ b/examples/framework-react/package.json @@ -13,7 +13,7 @@ "@astrojs/react": "^3.6.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "react": "^18.3.1", "react-dom": "^18.3.1" } diff --git a/examples/framework-solid/package.json b/examples/framework-solid/package.json index b38380ec7a71..31b0691ca386 100644 --- a/examples/framework-solid/package.json +++ b/examples/framework-solid/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@astrojs/solid-js": "^4.4.2", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "solid-js": "^1.9.2" } } diff --git a/examples/framework-svelte/package.json b/examples/framework-svelte/package.json index f7ccc842d38b..c7308a1ab374 100644 --- a/examples/framework-svelte/package.json +++ b/examples/framework-svelte/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@astrojs/svelte": "^6.0.0-beta.2", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "svelte": "^4.2.19" } } diff --git a/examples/framework-vue/package.json b/examples/framework-vue/package.json index 6ca9585e5a36..ae8a1403f34d 100644 --- a/examples/framework-vue/package.json +++ b/examples/framework-vue/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@astrojs/vue": "^5.0.0-beta.1", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "vue": "^3.5.12" } } diff --git a/examples/hackernews/package.json b/examples/hackernews/package.json index b26d8ce2b465..71bdd46ea7ff 100644 --- a/examples/hackernews/package.json +++ b/examples/hackernews/package.json @@ -11,6 +11,6 @@ }, "dependencies": { "@astrojs/node": "^9.0.0-alpha.1", - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/integration/package.json b/examples/integration/package.json index 2e2e6d47698e..b591fedb5812 100644 --- a/examples/integration/package.json +++ b/examples/integration/package.json @@ -15,7 +15,7 @@ ], "scripts": {}, "devDependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" }, "peerDependencies": { "astro": "^4.0.0" diff --git a/examples/minimal/package.json b/examples/minimal/package.json index e671b25fb4b7..f792c9b96a9c 100644 --- a/examples/minimal/package.json +++ b/examples/minimal/package.json @@ -10,6 +10,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/portfolio/package.json b/examples/portfolio/package.json index 8c2e2d0695fe..fe41095bdd51 100644 --- a/examples/portfolio/package.json +++ b/examples/portfolio/package.json @@ -10,6 +10,6 @@ "astro": "astro" }, "dependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/ssr/package.json b/examples/ssr/package.json index e588e5575bc5..63d27d3b2250 100644 --- a/examples/ssr/package.json +++ b/examples/ssr/package.json @@ -13,7 +13,7 @@ "dependencies": { "@astrojs/node": "^9.0.0-alpha.1", "@astrojs/svelte": "^6.0.0-beta.2", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "svelte": "^4.2.19" } } diff --git a/examples/starlog/package.json b/examples/starlog/package.json index a9c76c4b49e9..ffcae20cca4c 100644 --- a/examples/starlog/package.json +++ b/examples/starlog/package.json @@ -9,7 +9,7 @@ "astro": "astro" }, "dependencies": { - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "sass": "^1.80.6", "sharp": "^0.33.3" } diff --git a/examples/toolbar-app/package.json b/examples/toolbar-app/package.json index 340d4f576cc9..5b03d6f85e2e 100644 --- a/examples/toolbar-app/package.json +++ b/examples/toolbar-app/package.json @@ -15,6 +15,6 @@ "./app": "./dist/app.js" }, "devDependencies": { - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index aa7473c2310b..9916a485102d 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -11,6 +11,6 @@ }, "dependencies": { "@astrojs/markdoc": "^0.12.0-beta.0", - "astro": "^5.0.0-beta.7" + "astro": "^5.0.0-beta.8" } } diff --git a/examples/with-mdx/package.json b/examples/with-mdx/package.json index a1f517effe35..4642aa9ef490 100644 --- a/examples/with-mdx/package.json +++ b/examples/with-mdx/package.json @@ -12,7 +12,7 @@ "dependencies": { "@astrojs/mdx": "^4.0.0-beta.3", "@astrojs/preact": "^3.5.3", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "preact": "^10.24.3" } } diff --git a/examples/with-nanostores/package.json b/examples/with-nanostores/package.json index 8ff59c197c9f..19cd884f955f 100644 --- a/examples/with-nanostores/package.json +++ b/examples/with-nanostores/package.json @@ -12,7 +12,7 @@ "dependencies": { "@astrojs/preact": "^3.5.3", "@nanostores/preact": "^0.5.2", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "nanostores": "^0.11.3", "preact": "^10.24.3" } diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json index 0a7be2f9aeb3..fcfe676f1784 100644 --- a/examples/with-tailwindcss/package.json +++ b/examples/with-tailwindcss/package.json @@ -13,7 +13,7 @@ "@astrojs/mdx": "^4.0.0-beta.3", "@astrojs/tailwind": "^5.1.2", "@types/canvas-confetti": "^1.6.4", - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "autoprefixer": "^10.4.20", "canvas-confetti": "^1.9.3", "postcss": "^8.4.47", diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json index e907caae43f8..24c9cb1b02d3 100644 --- a/examples/with-vitest/package.json +++ b/examples/with-vitest/package.json @@ -11,7 +11,7 @@ "test": "vitest" }, "dependencies": { - "astro": "^5.0.0-beta.7", + "astro": "^5.0.0-beta.8", "vitest": "^2.1.4" } } diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index aa3d02e32fa5..2d5bd7070206 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,74 @@ # astro +## 5.0.0-beta.8 + +### Minor Changes + +- [#12373](https://github.com/withastro/astro/pull/12373) [`d10f918`](https://github.com/withastro/astro/commit/d10f91815e63f169cff3d1daef5505aef077c76c) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Changes the default behavior for Astro Action form requests to a standard POST submission. + + In Astro 4.x, actions called from an HTML form would trigger a redirect with the result forwarded using cookies. This caused issues for large form errors and return values that exceeded the 4 KB limit of cookie-based storage. + + Astro 5.0 now renders the result of an action as a POST result without any forwarding. This will introduce a "confirm form resubmission?" dialog when a user attempts to refresh the page, though it no longer imposes a 4 KB limit on action return value. + + ## Customize form submission behavior + + If you prefer to address the "confirm form resubmission?" dialog on refresh, or to preserve action results across sessions, you can now [customize action result handling from middleware](https://5-0-0-beta.docs.astro.build/en/guides/actions/#advanced-persist-action-results-with-a-session). + + We recommend using a session storage provider [as described in our Netlify Blob example](https://5-0-0-beta.docs.astro.build/en/guides/actions/#advanced-persist-action-results-with-a-session). However, if you prefer the cookie forwarding behavior from 4.X and accept the 4 KB size limit, you can implement the pattern as shown in this sample snippet: + + ```ts + // src/middleware.ts + import { defineMiddleware } from 'astro:middleware'; + import { getActionContext } from 'astro:actions'; + + export const onRequest = defineMiddleware(async (context, next) => { + // Skip requests for prerendered pages + if (context.isPrerendered) return next(); + + const { action, setActionResult, serializeActionResult } = getActionContext(context); + + // If an action result was forwarded as a cookie, set the result + // to be accessible from `Astro.getActionResult()` + const payload = context.cookies.get('ACTION_PAYLOAD'); + if (payload) { + const { actionName, actionResult } = payload.json(); + setActionResult(actionName, actionResult); + context.cookies.delete('ACTION_PAYLOAD'); + return next(); + } + + // If an action was called from an HTML form action, + // call the action handler and redirect with the result as a cookie. + if (action?.calledFrom === 'form') { + const actionResult = await action.handler(); + + context.cookies.set('ACTION_PAYLOAD', { + actionName: action.name, + actionResult: serializeActionResult(actionResult), + }); + + if (actionResult.error) { + // Redirect back to the previous page on error + const referer = context.request.headers.get('Referer'); + if (!referer) { + throw new Error('Internal: Referer unexpectedly missing from Action POST request.'); + } + return context.redirect(referer); + } + // Redirect to the destination page on success + return context.redirect(context.originPathname); + } + + return next(); + }); + ``` + +### Patch Changes + +- [#12339](https://github.com/withastro/astro/pull/12339) [`bdb75a8`](https://github.com/withastro/astro/commit/bdb75a87f24d7f032797483164fb2f82aa691fee) Thanks [@ematipico](https://github.com/ematipico)! - Adds an error when `Astro.rewrite()` is used to rewrite an on-demand route with a static route when using the `"server"` output. + + This is a forbidden rewrite because Astro can't retrieve the emitted static route at runtime. This route is served by the hosting platform, and not Astro itself. + ## 5.0.0-beta.7 ### Minor Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index a678ba60b2ca..d29b01932b5c 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "5.0.0-beta.7", + "version": "5.0.0-beta.8", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "type": "module", "author": "withastro", diff --git a/packages/astro/src/actions/integration.ts b/packages/astro/src/actions/integration.ts index 23fbd904a52f..d7c99fc421e3 100644 --- a/packages/astro/src/actions/integration.ts +++ b/packages/astro/src/actions/integration.ts @@ -3,7 +3,7 @@ import { AstroError } from '../core/errors/errors.js'; import { viteID } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroIntegration } from '../types/public/integrations.js'; -import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID, ACTION_RPC_ROUTE_PATTERN } from './consts.js'; +import { ACTIONS_TYPES_FILE, ACTION_RPC_ROUTE_PATTERN, VIRTUAL_MODULE_ID } from './consts.js'; /** * This integration is applied when the user is using Actions in their project. diff --git a/packages/astro/src/actions/runtime/virtual/server.ts b/packages/astro/src/actions/runtime/virtual/server.ts index f8fac557a09a..f28b3c2393b9 100644 --- a/packages/astro/src/actions/runtime/virtual/server.ts +++ b/packages/astro/src/actions/runtime/virtual/server.ts @@ -1,13 +1,17 @@ import { z } from 'zod'; import { ActionCalledFromServerError } from '../../../core/errors/errors-data.js'; import { AstroError } from '../../../core/errors/errors.js'; +import type { APIContext } from '../../../types/public/index.js'; +import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js'; import { - formContentTypes, - hasContentType, type ActionAPIContext, type ErrorInferenceObject, type MaybePromise, + formContentTypes, + hasContentType, } from '../utils.js'; +import type { Locals } from '../utils.js'; +import { getAction } from './get-action.js'; import { ACTION_QUERY_PARAMS, ActionError, @@ -18,10 +22,6 @@ import { deserializeActionResult, serializeActionResult, } from './shared.js'; -import type { Locals } from '../utils.js'; -import { getAction } from './get-action.js'; -import type { APIContext } from '../../../types/public/index.js'; -import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js'; export * from './shared.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 953ead5c23a7..b4099b989b74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,7 +143,7 @@ importers: examples/basics: dependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/blog: @@ -158,13 +158,13 @@ importers: specifier: ^3.2.1 version: link:../../packages/integrations/sitemap astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/component: devDependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/container-with-vitest: @@ -173,7 +173,7 @@ importers: specifier: ^3.6.2 version: link:../../packages/integrations/react astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -204,7 +204,7 @@ importers: specifier: ^3.14.3 version: 3.14.3 astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/framework-multiple: @@ -231,7 +231,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -261,7 +261,7 @@ importers: specifier: ^1.3.0 version: 1.3.0(preact@10.24.3) astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -279,7 +279,7 @@ importers: specifier: ^18.3.1 version: 18.3.1 astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro react: specifier: ^18.3.1 @@ -294,7 +294,7 @@ importers: specifier: ^4.4.2 version: link:../../packages/integrations/solid astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro solid-js: specifier: ^1.9.2 @@ -306,7 +306,7 @@ importers: specifier: ^6.0.0-beta.2 version: link:../../packages/integrations/svelte astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -318,7 +318,7 @@ importers: specifier: ^5.0.0-beta.1 version: link:../../packages/integrations/vue astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro vue: specifier: ^3.5.12 @@ -330,25 +330,25 @@ importers: specifier: ^9.0.0-alpha.1 version: 9.0.0-alpha.1(astro@packages+astro) astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/integration: devDependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/minimal: dependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/portfolio: dependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/ssr: @@ -360,7 +360,7 @@ importers: specifier: ^6.0.0-beta.2 version: link:../../packages/integrations/svelte astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro svelte: specifier: ^4.2.19 @@ -369,7 +369,7 @@ importers: examples/starlog: dependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro sass: specifier: ^1.80.6 @@ -381,7 +381,7 @@ importers: examples/toolbar-app: devDependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/with-markdoc: @@ -390,7 +390,7 @@ importers: specifier: ^0.12.0-beta.0 version: link:../../packages/integrations/markdoc astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro examples/with-mdx: @@ -402,7 +402,7 @@ importers: specifier: ^3.5.3 version: link:../../packages/integrations/preact astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro preact: specifier: ^10.24.3 @@ -417,7 +417,7 @@ importers: specifier: ^0.5.2 version: 0.5.2(nanostores@0.11.3)(preact@10.24.3) astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro nanostores: specifier: ^0.11.3 @@ -438,7 +438,7 @@ importers: specifier: ^1.6.4 version: 1.6.4 astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro autoprefixer: specifier: ^10.4.20 @@ -456,7 +456,7 @@ importers: examples/with-vitest: dependencies: astro: - specifier: ^5.0.0-beta.7 + specifier: ^5.0.0-beta.8 version: link:../../packages/astro vitest: specifier: ^2.1.4 From 0462219612183b65867aaaef9fa538d89f201999 Mon Sep 17 00:00:00 2001 From: Arpan Patel Date: Thu, 14 Nov 2024 03:33:57 -0500 Subject: [PATCH 12/20] Fix script injection during build (#12392) Co-authored-by: Emanuele Stoppa --- .changeset/slimy-pets-lick.md | 5 +++ .../astro/src/core/build/plugins/index.ts | 2 +- .../build/plugins/plugin-hoisted-scripts.ts | 38 +++++++------------ .../src/to-inject.astro | 14 ++----- .../test/reuse-injected-entrypoint.test.js | 26 ++++++++++++- 5 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 .changeset/slimy-pets-lick.md diff --git a/.changeset/slimy-pets-lick.md b/.changeset/slimy-pets-lick.md new file mode 100644 index 000000000000..31065a6b6d7e --- /dev/null +++ b/.changeset/slimy-pets-lick.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue where scripts were not correctly injected during the build. The issue was triggered when there were injected routes with the same `entrypoint` and different `pattern` diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts index 1ccb7c6c3884..4d30303f8b12 100644 --- a/packages/astro/src/core/build/plugins/index.ts +++ b/packages/astro/src/core/build/plugins/index.ts @@ -32,7 +32,7 @@ export function registerAllPlugins({ internals, options, register }: AstroBuildP if (options.settings.config.experimental.directRenderScript) { register(pluginScripts(internals)); } else { - register(pluginHoistedScripts(options, internals)); + register(pluginHoistedScripts(internals)); } register(pluginSSR(options, internals)); register(pluginSSRSplit(options, internals)); diff --git a/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts index 7c378490938a..93bf44681aac 100644 --- a/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts +++ b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts @@ -1,10 +1,7 @@ import type { BuildOptions, Rollup, Plugin as VitePlugin } from 'vite'; -import type { AstroSettings } from '../../../@types/astro.js'; -import { viteID } from '../../util.js'; import type { BuildInternals } from '../internal.js'; -import { getPageDataByViteID } from '../internal.js'; +import { getPageDatasByHoistedScriptId } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin.js'; -import type { StaticBuildOptions } from '../types.js'; import { shouldInlineAsset } from './util.js'; function virtualHoistedEntry(id: string) { @@ -12,7 +9,6 @@ function virtualHoistedEntry(id: string) { } export function vitePluginHoistedScripts( - settings: AstroSettings, internals: BuildInternals, ): VitePlugin { let assetsInlineLimit: NonNullable; @@ -73,23 +69,18 @@ export function vitePluginHoistedScripts( shouldInlineAsset(output.code, output.fileName, assetsInlineLimit); let removeFromBundle = false; const facadeId = output.facadeModuleId!; - const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!; - for (const pathname of pages) { - const vid = viteID(new URL('.' + pathname, settings.config.root)); - const pageInfo = getPageDataByViteID(internals, vid); - if (pageInfo) { - if (canBeInlined) { - pageInfo.hoistedScript = { - type: 'inline', - value: output.code, - }; - removeFromBundle = true; - } else { - pageInfo.hoistedScript = { - type: 'external', - value: id, - }; - } + for (const pageData of getPageDatasByHoistedScriptId(internals, facadeId)) { + if (canBeInlined) { + pageData.hoistedScript = { + type: 'inline', + value: output.code, + }; + removeFromBundle = true; + } else { + pageData.hoistedScript = { + type: 'external', + value: id, + }; } } @@ -103,7 +94,6 @@ export function vitePluginHoistedScripts( } export function pluginHoistedScripts( - options: StaticBuildOptions, internals: BuildInternals, ): AstroBuildPlugin { return { @@ -111,7 +101,7 @@ export function pluginHoistedScripts( hooks: { 'build:before': () => { return { - vitePlugin: vitePluginHoistedScripts(options.settings, internals), + vitePlugin: vitePluginHoistedScripts(internals), }; }, }, diff --git a/packages/astro/test/fixtures/reuse-injected-entrypoint/src/to-inject.astro b/packages/astro/test/fixtures/reuse-injected-entrypoint/src/to-inject.astro index 13d5bac25d98..c8cc21da04c7 100644 --- a/packages/astro/test/fixtures/reuse-injected-entrypoint/src/to-inject.astro +++ b/packages/astro/test/fixtures/reuse-injected-entrypoint/src/to-inject.astro @@ -1,12 +1,6 @@ --- --- - - - - - Routing - - -

to-inject.astro

- - \ No newline at end of file +

to-inject.astro

+ diff --git a/packages/astro/test/reuse-injected-entrypoint.test.js b/packages/astro/test/reuse-injected-entrypoint.test.js index 10b8e528f852..72e90dbde92b 100644 --- a/packages/astro/test/reuse-injected-entrypoint.test.js +++ b/packages/astro/test/reuse-injected-entrypoint.test.js @@ -13,11 +13,13 @@ const routes = [ description: 'matches /injected-a to to-inject.astro', url: '/injected-a', h1: 'to-inject.astro', + scriptContent: 'console.log("to-inject.astro");', }, { description: 'matches /injected-b to to-inject.astro', url: '/injected-b', h1: 'to-inject.astro', + scriptContent: 'console.log("to-inject.astro");', }, { description: 'matches /dynamic-a/id-1 to [id].astro', @@ -60,7 +62,7 @@ describe('Reuse injected entrypoint', () => { await fixture.build(); }); - routes.forEach(({ description, url, fourOhFour, h1, p, htmlMatch }) => { + routes.forEach(({ description, url, fourOhFour, h1, p, htmlMatch, scriptContent }) => { const isEndpoint = htmlMatch && !h1 && !p; it(description, async () => { @@ -85,6 +87,15 @@ describe('Reuse injected entrypoint', () => { if (htmlMatch) { assert.equal(html, htmlMatch); } + + if (scriptContent) { + const scriptTags = $('script[type="module"]').toArray(); + const scriptFound = scriptTags.some((script) => { + const scriptText = $(script).text(); + return scriptText.includes(scriptContent.trim()); + }); + assert(scriptFound, `Expected script content to be injected in SSG ${url}`); + } }); }); }); @@ -105,7 +116,7 @@ describe('Reuse injected entrypoint', () => { await devServer.stop(); }); - routes.forEach(({ description, url, fourOhFour, h1, p, htmlMatch }) => { + routes.forEach(({ description, url, fourOhFour, h1, p, htmlMatch, scriptContent }) => { // checks URLs as written above it(description, async () => { const html = await fixture.fetch(url).then((res) => res.text()); @@ -127,6 +138,17 @@ describe('Reuse injected entrypoint', () => { if (htmlMatch) { assert.equal(html, htmlMatch); } + + if (scriptContent) { + const scriptTags = $('script[type="module"]').toArray(); + const scriptFound = scriptTags.some((script) => { + const scriptSrc = $(script).attr('src'); + return ( + scriptSrc && scriptSrc.includes('/to-inject.astro?astro&type=script&index=0&lang.ts') + ); + }); + assert(scriptFound, `Expected script content to be injected in dev ${url}`); + } }); }); }); From bdc0890061533466da19660ff83a331a3136f6c4 Mon Sep 17 00:00:00 2001 From: Arpan Patel Date: Thu, 14 Nov 2024 08:34:43 +0000 Subject: [PATCH 13/20] [ci] format --- .../src/core/build/plugins/plugin-hoisted-scripts.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts index 93bf44681aac..e442c7463755 100644 --- a/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts +++ b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts @@ -8,9 +8,7 @@ function virtualHoistedEntry(id: string) { return id.startsWith('/astro/hoisted.js?q='); } -export function vitePluginHoistedScripts( - internals: BuildInternals, -): VitePlugin { +export function vitePluginHoistedScripts(internals: BuildInternals): VitePlugin { let assetsInlineLimit: NonNullable; return { @@ -93,9 +91,7 @@ export function vitePluginHoistedScripts( }; } -export function pluginHoistedScripts( - internals: BuildInternals, -): AstroBuildPlugin { +export function pluginHoistedScripts(internals: BuildInternals): AstroBuildPlugin { return { targets: ['client'], hooks: { From 4364bff27332e52f92da72392620a36110daee42 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 14 Nov 2024 12:41:16 +0000 Subject: [PATCH 14/20] fix(actions): internal symbol check (#12424) --- .changeset/brown-bulldogs-share.md | 5 +++++ packages/astro/src/actions/runtime/virtual/server.ts | 6 +++++- packages/astro/src/actions/utils.ts | 3 ++- .../astro/test/fixtures/actions/src/pages/invalid.astro | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .changeset/brown-bulldogs-share.md diff --git a/.changeset/brown-bulldogs-share.md b/.changeset/brown-bulldogs-share.md new file mode 100644 index 000000000000..c8cadb11b28c --- /dev/null +++ b/.changeset/brown-bulldogs-share.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue where an incorrect usage of Astro actions was lost when porting the fix from v4 to v5 diff --git a/packages/astro/src/actions/runtime/virtual/server.ts b/packages/astro/src/actions/runtime/virtual/server.ts index f28b3c2393b9..47decf18e5d4 100644 --- a/packages/astro/src/actions/runtime/virtual/server.ts +++ b/packages/astro/src/actions/runtime/virtual/server.ts @@ -4,11 +4,13 @@ import { AstroError } from '../../../core/errors/errors.js'; import type { APIContext } from '../../../types/public/index.js'; import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js'; import { + ACTION_API_CONTEXT_SYMBOL, type ActionAPIContext, type ErrorInferenceObject, type MaybePromise, formContentTypes, hasContentType, + isActionAPIContext, } from '../utils.js'; import type { Locals } from '../utils.js'; import { getAction } from './get-action.js'; @@ -79,7 +81,8 @@ export function defineAction< : getJsonServerHandler(handler, inputSchema); async function safeServerHandler(this: ActionAPIContext, unparsedInput: unknown) { - if (typeof this === 'function') { + // The ActionAPIContext should always contain the `params` property + if (typeof this === 'function' || !isActionAPIContext(this)) { throw new AstroError(ActionCalledFromServerError); } return callSafely(() => serverHandler(unparsedInput, this)); @@ -293,6 +296,7 @@ export function getActionContext(context: APIContext): ActionMiddlewareContext { redirect: _redirect, ...actionAPIContext } = context; + Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true); const handler = baseAction.bind(actionAPIContext satisfies ActionAPIContext); return handler(input); }, diff --git a/packages/astro/src/actions/utils.ts b/packages/astro/src/actions/utils.ts index dc0fa4b1486e..b6fe63f6caca 100644 --- a/packages/astro/src/actions/utils.ts +++ b/packages/astro/src/actions/utils.ts @@ -1,7 +1,7 @@ import type fsMod from 'node:fs'; import * as eslexer from 'es-module-lexer'; import type { APIContext } from '../types/public/context.js'; -import type { ActionAPIContext, Locals } from './runtime/utils.js'; +import { ACTION_API_CONTEXT_SYMBOL, type ActionAPIContext, type Locals } from './runtime/utils.js'; import { deserializeActionResult, getActionQueryString } from './runtime/virtual/shared.js'; export function hasActionPayload(locals: APIContext['locals']): locals is Locals { @@ -22,6 +22,7 @@ export function createGetActionResult(locals: APIContext['locals']): APIContext[ export function createCallAction(context: ActionAPIContext): APIContext['callAction'] { return (baseAction, input) => { + Reflect.set(context, ACTION_API_CONTEXT_SYMBOL, true); const action = baseAction.bind(context); return action(input) as any; }; diff --git a/packages/astro/test/fixtures/actions/src/pages/invalid.astro b/packages/astro/test/fixtures/actions/src/pages/invalid.astro index 908eee853bd4..fe152dbba0a7 100644 --- a/packages/astro/test/fixtures/actions/src/pages/invalid.astro +++ b/packages/astro/test/fixtures/actions/src/pages/invalid.astro @@ -2,5 +2,5 @@ import { actions } from "astro:actions"; // this is invalid, it should fail -const result = await actions.imageUploadInChunks(); +const result = await actions.subscribe({ channel: "hey" }); --- From 9fc2ab8cc848739a21bfa3f754e9bec4926dc034 Mon Sep 17 00:00:00 2001 From: Jacob Jenkins Date: Thu, 14 Nov 2024 15:31:51 +0000 Subject: [PATCH 15/20] Update to svelte 5 (#12364) Co-authored-by: bluwy --- .changeset/five-dolls-smash.md | 20 ++ .changeset/tasty-coats-repair.md | 5 + examples/framework-multiple/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 15 +- examples/framework-svelte/package.json | 2 +- .../src/components/Counter.svelte | 15 +- examples/ssr/package.json | 2 +- examples/ssr/src/components/AddToCart.svelte | 5 +- examples/ssr/src/components/Cart.svelte | 4 +- .../e2e/fixtures/client-only/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../astro/e2e/fixtures/errors/package.json | 2 +- .../svelte/SvelteRuntimeError.svelte | 2 +- .../fixtures/multiple-frameworks/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 10 +- .../fixtures/nested-in-preact/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../e2e/fixtures/nested-in-react/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../e2e/fixtures/nested-in-solid/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../fixtures/nested-in-svelte/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../e2e/fixtures/nested-in-vue/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../fixtures/nested-recursive/package.json | 2 +- .../components/svelte/SvelteCounter.svelte | 11 +- .../fixtures/svelte-component/package.json | 2 +- .../src/components/Counter.svelte | 17 +- .../src/components/Stuff.svelte | 4 +- .../src/components/SvelteComponent.svelte | 2 +- .../src/components/ToggleSlots.svelte | 4 +- .../fixtures/view-transitions/package.json | 8 +- .../src/components/SvelteCounter.svelte | 10 +- .../astro/test/fixtures/0-css/package.json | 2 +- .../test/fixtures/0-css/svelte.config.mjs | 5 + .../alias-tsconfig-baseurl-only/package.json | 2 +- .../test/fixtures/alias-tsconfig/package.json | 2 +- .../astro/test/fixtures/alias/package.json | 2 +- .../test/fixtures/astro-children/package.json | 2 +- .../src/components/Component.svelte | 6 +- .../fixtures/astro-client-only/package.json | 2 +- .../src/components/PersistentCounter.svelte | 8 +- .../PersistentCounterStandalone.svelte | 8 +- .../test/fixtures/astro-dynamic/package.json | 2 +- .../src/components/PersistentCounter.svelte | 8 +- .../src/components/SvelteCounter.svelte | 10 +- .../fixtures/astro-markdown/astro.config.mjs | 6 +- .../test/fixtures/astro-markdown/package.json | 1 - .../src/components/Counter.svelte | 5 - .../fixtures/astro-slots-nested/package.json | 2 +- .../component-library-shared/Counter.svelte | 9 +- .../fixtures/component-library/package.json | 2 +- .../css-dangling-references/package.json | 2 +- .../src/components/Wrapper.svelte | 3 +- .../astro/test/fixtures/fetch/package.json | 2 +- packages/astro/test/fixtures/jsx/package.json | 2 +- .../jsx/src/components/SvelteCounter.svelte | 6 +- .../astro/test/fixtures/postcss/package.json | 2 +- .../server-islands/hybrid/package.json | 2 +- .../fixtures/server-islands/ssr/package.json | 2 +- .../test/fixtures/slots-svelte/package.json | 2 +- .../src/components/Counter.svelte | 8 +- .../fixtures/svelte-component/package.json | 2 +- .../src/components/TypeScript.svelte | 2 +- .../vue-with-multi-renderer/package.json | 2 +- packages/astro/test/postcss.test.js | 6 +- packages/integrations/solid/src/server.ts | 4 + packages/integrations/svelte/client-v5.js | 60 ----- packages/integrations/svelte/client.js | 125 --------- packages/integrations/svelte/client.svelte.js | 79 ++++++ packages/integrations/svelte/package.json | 18 +- packages/integrations/svelte/server-v5.d.ts | 2 - packages/integrations/svelte/server-v5.js | 57 ---- packages/integrations/svelte/server.js | 48 +++- packages/integrations/svelte/src/index.ts | 99 +------ pnpm-lock.yaml | 252 +++++++++--------- 77 files changed, 459 insertions(+), 636 deletions(-) create mode 100644 .changeset/five-dolls-smash.md create mode 100644 .changeset/tasty-coats-repair.md create mode 100644 packages/astro/test/fixtures/0-css/svelte.config.mjs delete mode 100644 packages/astro/test/fixtures/astro-markdown/src/components/Counter.svelte delete mode 100644 packages/integrations/svelte/client-v5.js delete mode 100644 packages/integrations/svelte/client.js create mode 100644 packages/integrations/svelte/client.svelte.js delete mode 100644 packages/integrations/svelte/server-v5.d.ts delete mode 100644 packages/integrations/svelte/server-v5.js diff --git a/.changeset/five-dolls-smash.md b/.changeset/five-dolls-smash.md new file mode 100644 index 000000000000..ad5e83fd9a84 --- /dev/null +++ b/.changeset/five-dolls-smash.md @@ -0,0 +1,20 @@ +--- +'@astrojs/svelte': major +--- + +Adds support for Svelte 5. Svelte 3 and 4 are no longer supported. + +The integration will now also no longer add `vitePreprocess()` by default if a preprocessor is not set up in `svelte.config.js`. It is recommended to set up the Svelte config manually so that features like IDE completion and syntax highlighting work properly. + +If you're using SCSS, Stylus, etc in your Svelte component style tags, make sure that the preprocessor is also set up in `svelte.config.js`. For example: + +```js +// svelte.config.js +import { vitePreprocess } from '@astrojs/svelte'; + +export default { + preprocess: vitePreprocess(), +}; +``` + +Refer to the [Svelte 5 migration guide](https://svelte.dev/docs/svelte/v5-migration-guide) and [`@sveltejs/vite-plugin-svelte` changelog](https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/vite-plugin-svelte/CHANGELOG.md#400) for details of their respective breaking changes. diff --git a/.changeset/tasty-coats-repair.md b/.changeset/tasty-coats-repair.md new file mode 100644 index 000000000000..10b97e1c3d45 --- /dev/null +++ b/.changeset/tasty-coats-repair.md @@ -0,0 +1,5 @@ +--- +'@astrojs/solid-js': patch +--- + +Handles checking Svelte 5 component functions to avoid processing them as Solid components diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json index 1e63e0247928..fa0af0805e23 100644 --- a/examples/framework-multiple/package.json +++ b/examples/framework-multiple/package.json @@ -23,7 +23,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "solid-js": "^1.9.3", - "svelte": "^4.2.19", + "svelte": "^5.1.16", "vue": "^3.5.12" } } diff --git a/examples/framework-multiple/src/components/svelte/SvelteCounter.svelte b/examples/framework-multiple/src/components/svelte/SvelteCounter.svelte index 01e58574a6ec..641312ae1b9f 100644 --- a/examples/framework-multiple/src/components/svelte/SvelteCounter.svelte +++ b/examples/framework-multiple/src/components/svelte/SvelteCounter.svelte @@ -2,7 +2,14 @@ A counter written with Svelte -->
- +
{count}
- +
- + {@render children?.()}
diff --git a/examples/framework-svelte/package.json b/examples/framework-svelte/package.json index 9c1adf7cf9c6..779267dd64ba 100644 --- a/examples/framework-svelte/package.json +++ b/examples/framework-svelte/package.json @@ -13,6 +13,6 @@ "dependencies": { "@astrojs/svelte": "^5.7.3", "astro": "^4.16.12", - "svelte": "^4.2.19" + "svelte": "^5.1.16" } } diff --git a/examples/framework-svelte/src/components/Counter.svelte b/examples/framework-svelte/src/components/Counter.svelte index 1353736aaab7..a11538645e6c 100644 --- a/examples/framework-svelte/src/components/Counter.svelte +++ b/examples/framework-svelte/src/components/Counter.svelte @@ -1,5 +1,12 @@
- +
{count}
- +
- + {@render children?.()}
- diff --git a/examples/ssr/src/components/Cart.svelte b/examples/ssr/src/components/Cart.svelte index 74db0bc7947c..5d4b7d2510c1 100644 --- a/examples/ssr/src/components/Cart.svelte +++ b/examples/ssr/src/components/Cart.svelte @@ -1,5 +1,5 @@
- +
{ count }
- +
- + {@render children?.()}
diff --git a/packages/astro/e2e/fixtures/errors/package.json b/packages/astro/e2e/fixtures/errors/package.json index 3730d61f1bd0..77c40677853c 100644 --- a/packages/astro/e2e/fixtures/errors/package.json +++ b/packages/astro/e2e/fixtures/errors/package.json @@ -14,7 +14,7 @@ "react-dom": "^18.3.1", "sass": "^1.80.6", "solid-js": "^1.9.3", - "svelte": "^4.2.19", + "svelte": "^5.1.16", "vue": "^3.5.12" } } diff --git a/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte index 54dbab6a7540..69d62a884f73 100644 --- a/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte +++ b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte @@ -1,5 +1,5 @@
- +
{ count }
- +
- + {@render children?.()}
- +
{ count }
- +
diff --git a/packages/astro/test/fixtures/astro-client-only/src/components/PersistentCounterStandalone.svelte b/packages/astro/test/fixtures/astro-client-only/src/components/PersistentCounterStandalone.svelte index c851a42f86c6..cb2d5149649f 100644 --- a/packages/astro/test/fixtures/astro-client-only/src/components/PersistentCounterStandalone.svelte +++ b/packages/astro/test/fixtures/astro-client-only/src/components/PersistentCounterStandalone.svelte @@ -1,8 +1,8 @@
- +
{ count }
- +
\ No newline at end of file diff --git a/packages/astro/test/fixtures/astro-dynamic/src/components/SvelteCounter.svelte b/packages/astro/test/fixtures/astro-dynamic/src/components/SvelteCounter.svelte index 8d6b3f5e1688..d4972b7f92a8 100644 --- a/packages/astro/test/fixtures/astro-dynamic/src/components/SvelteCounter.svelte +++ b/packages/astro/test/fixtures/astro-dynamic/src/components/SvelteCounter.svelte @@ -1,7 +1,7 @@
- +
{ count }
- +
- + {@render children?.()}
diff --git a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs index ac3f1ab6439e..201ce52f26ef 100644 --- a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs @@ -1,8 +1,6 @@ -import svelte from "@astrojs/svelte"; -import { defineConfig } from 'astro/config'; +import { defineConfig } from "astro/config"; // https://astro.build/config export default defineConfig({ - integrations: [svelte()], - site: 'https://astro.build/', + site: "https://astro.build/", }); diff --git a/packages/astro/test/fixtures/astro-markdown/package.json b/packages/astro/test/fixtures/astro-markdown/package.json index c1903a941ea1..9fe9dae0124d 100644 --- a/packages/astro/test/fixtures/astro-markdown/package.json +++ b/packages/astro/test/fixtures/astro-markdown/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "dependencies": { - "@astrojs/svelte": "workspace:*", "astro": "workspace:*" } } diff --git a/packages/astro/test/fixtures/astro-markdown/src/components/Counter.svelte b/packages/astro/test/fixtures/astro-markdown/src/components/Counter.svelte deleted file mode 100644 index 4e91b26596c4..000000000000 --- a/packages/astro/test/fixtures/astro-markdown/src/components/Counter.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/packages/astro/test/fixtures/astro-slots-nested/package.json b/packages/astro/test/fixtures/astro-slots-nested/package.json index 114e369d6cd6..6d541872ec11 100644 --- a/packages/astro/test/fixtures/astro-slots-nested/package.json +++ b/packages/astro/test/fixtures/astro-slots-nested/package.json @@ -13,7 +13,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "solid-js": "^1.9.3", - "svelte": "^4.2.19", + "svelte": "^5.1.16", "vue": "^3.5.12" } } diff --git a/packages/astro/test/fixtures/component-library-shared/Counter.svelte b/packages/astro/test/fixtures/component-library-shared/Counter.svelte index 2f4c07339060..0ca9f08b0d96 100644 --- a/packages/astro/test/fixtures/component-library-shared/Counter.svelte +++ b/packages/astro/test/fixtures/component-library-shared/Counter.svelte @@ -1,5 +1,6 @@
- +
{ count }
- +
- + {@render children?.()}
diff --git a/packages/astro/test/fixtures/core-image-layout/src/pages/build.astro b/packages/astro/test/fixtures/core-image-layout/src/pages/build.astro new file mode 100644 index 000000000000..a4a0cc9083da --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/src/pages/build.astro @@ -0,0 +1,66 @@ +--- +import { Image } from "astro:assets"; +import penguin from "../assets/penguin.jpg"; +--- + +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
diff --git a/packages/astro/test/fixtures/core-image-layout/src/pages/fit.astro b/packages/astro/test/fixtures/core-image-layout/src/pages/fit.astro new file mode 100644 index 000000000000..442f4ffb0e17 --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/src/pages/fit.astro @@ -0,0 +1,35 @@ +--- +import { Image } from "astro:assets"; +import penguin from "../assets/penguin.jpg"; +--- + +
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
+
+ a penguin +
diff --git a/packages/astro/test/fixtures/core-image-layout/src/pages/index.astro b/packages/astro/test/fixtures/core-image-layout/src/pages/index.astro new file mode 100644 index 000000000000..7fe5b5626e17 --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/src/pages/index.astro @@ -0,0 +1,56 @@ +--- +import { Image, Picture } from "astro:assets"; +import penguin from "../assets/penguin.jpg"; +--- + + +
+ a penguin +
+
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ + diff --git a/packages/astro/test/fixtures/core-image-layout/src/pages/picture.astro b/packages/astro/test/fixtures/core-image-layout/src/pages/picture.astro new file mode 100644 index 000000000000..88d0310ef7a3 --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/src/pages/picture.astro @@ -0,0 +1,63 @@ +--- +import { Picture } from "astro:assets"; +import myImage from "../assets/penguin.jpg"; +--- + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + diff --git a/packages/astro/test/fixtures/core-image-layout/src/pages/remote.astro b/packages/astro/test/fixtures/core-image-layout/src/pages/remote.astro new file mode 100644 index 000000000000..60aa916c818e --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/src/pages/remote.astro @@ -0,0 +1,25 @@ +--- +import { Image, Picture } from "astro:assets"; + +const penguin = "https://images.unsplash.com/photo-1670392957807-b0504fc5160a?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + +--- + + + +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ +
+ a penguin +
+ diff --git a/packages/astro/test/fixtures/core-image-layout/tsconfig.json b/packages/astro/test/fixtures/core-image-layout/tsconfig.json new file mode 100644 index 000000000000..c193287fccd6 --- /dev/null +++ b/packages/astro/test/fixtures/core-image-layout/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "astro/tsconfigs/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "~/assets/*": ["src/assets/*"] + }, + }, + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} diff --git a/packages/astro/test/ssr-assets.test.js b/packages/astro/test/ssr-assets.test.js index d56ad1686b32..d11fc8673d6c 100644 --- a/packages/astro/test/ssr-assets.test.js +++ b/packages/astro/test/ssr-assets.test.js @@ -22,7 +22,7 @@ describe('SSR Assets', () => { const app = await fixture.loadTestAdapterApp(); /** @type {Set} */ const assets = app.manifest.assets; - assert.equal(assets.size, 1); + assert.equal(assets.size, 2); assert.equal(Array.from(assets)[0].endsWith('.css'), true); }); }); diff --git a/packages/astro/test/test-remote-image-service.js b/packages/astro/test/test-remote-image-service.js new file mode 100644 index 000000000000..2534b4085ec5 --- /dev/null +++ b/packages/astro/test/test-remote-image-service.js @@ -0,0 +1,26 @@ +import { fileURLToPath } from 'node:url'; +import { baseService } from '../dist/assets/services/service.js'; + +/** + * stub remote image service + * @param {{ foo?: string }} [config] + */ +export function testRemoteImageService(config = {}) { + return { + entrypoint: fileURLToPath(import.meta.url), + config, + }; +} + +/** @type {import("../dist/types/public/index.js").LocalImageService} */ +export default { + ...baseService, + propertiesToHash: [...baseService.propertiesToHash, 'data-custom'], + getHTMLAttributes(options, serviceConfig) { + options['data-service'] = 'my-custom-service'; + if (serviceConfig.service.config.foo) { + options['data-service-config'] = serviceConfig.service.config.foo; + } + return baseService.getHTMLAttributes(options); + }, +}; diff --git a/packages/astro/test/units/dev/collections-renderentry.test.js b/packages/astro/test/units/dev/collections-renderentry.test.js index 5af4a1b1d105..42e11c2a224f 100644 --- a/packages/astro/test/units/dev/collections-renderentry.test.js +++ b/packages/astro/test/units/dev/collections-renderentry.test.js @@ -101,7 +101,7 @@ describe('Content Collections - render()', () => { assert.equal($('ul li').length, 3); // Rendered the styles - assert.equal($('style').length, 1); + assert.equal($('style').length, 2); }, ); }); @@ -158,7 +158,7 @@ describe('Content Collections - render()', () => { assert.equal($('ul li').length, 3); // Rendered the styles - assert.equal($('style').length, 1); + assert.equal($('style').length, 2); }, ); }); @@ -225,7 +225,7 @@ describe('Content Collections - render()', () => { assert.equal($('ul li').length, 3); // Rendered the styles - assert.equal($('style').length, 1); + assert.equal($('style').length, 2); }, ); }); @@ -291,7 +291,7 @@ describe('Content Collections - render()', () => { assert.equal($('ul li').length, 3); // Rendered the styles - assert.equal($('style').length, 1); + assert.equal($('style').length, 2); }, ); }); diff --git a/packages/integrations/markdoc/test/image-assets.test.js b/packages/integrations/markdoc/test/image-assets.test.js index 0f98af4f1677..793bf1be6de2 100644 --- a/packages/integrations/markdoc/test/image-assets.test.js +++ b/packages/integrations/markdoc/test/image-assets.test.js @@ -38,7 +38,7 @@ describe('Markdoc - Image assets', () => { const { document } = parseHTML(html); assert.match( document.querySelector('#relative > img')?.src, - /\/_image\?href=.*%2Fsrc%2Fassets%2Frelative%2Foar.jpg%3ForigWidth%3D420%26origHeight%3D630%26origFormat%3Djpg&f=webp/, + /\/_image\?href=.*%2Fsrc%2Fassets%2Frelative%2Foar.jpg%3ForigWidth%3D420%26origHeight%3D630%26origFormat%3Djpg&w=420&h=630&f=webp/, ); }); @@ -48,7 +48,7 @@ describe('Markdoc - Image assets', () => { const { document } = parseHTML(html); assert.match( document.querySelector('#alias > img')?.src, - /\/_image\?href=.*%2Fsrc%2Fassets%2Falias%2Fcityscape.jpg%3ForigWidth%3D420%26origHeight%3D280%26origFormat%3Djpg&f=webp/, + /\/_image\?href=.*%2Fsrc%2Fassets%2Falias%2Fcityscape.jpg%3ForigWidth%3D420%26origHeight%3D280%26origFormat%3Djpg&w=420&h=280&f=webp/, ); }); diff --git a/packages/integrations/markdoc/test/propagated-assets.test.js b/packages/integrations/markdoc/test/propagated-assets.test.js index a0768448f1d9..5fe7369ceb52 100644 --- a/packages/integrations/markdoc/test/propagated-assets.test.js +++ b/packages/integrations/markdoc/test/propagated-assets.test.js @@ -45,12 +45,12 @@ describe('Markdoc - propagated assets', () => { let styleContents; if (mode === 'dev') { const styles = stylesDocument.querySelectorAll('style'); - assert.equal(styles.length, 1); - styleContents = styles[0].textContent; + assert.equal(styles.length, 2); + styleContents = styles[1].textContent; } else { const links = stylesDocument.querySelectorAll('link[rel="stylesheet"]'); - assert.equal(links.length, 1); - styleContents = await fixture.readFile(links[0].href); + assert.equal(links.length, 2); + styleContents = await fixture.readFile(links[1].href); } assert.equal(styleContents.includes('--color-base-purple: 269, 79%;'), true); }); @@ -58,10 +58,10 @@ describe('Markdoc - propagated assets', () => { it('[fails] Does not bleed styles to other page', async () => { if (mode === 'dev') { const styles = scriptsDocument.querySelectorAll('style'); - assert.equal(styles.length, 0); + assert.equal(styles.length, 1); } else { const links = scriptsDocument.querySelectorAll('link[rel="stylesheet"]'); - assert.equal(links.length, 0); + assert.equal(links.length, 1); } }); }); diff --git a/packages/integrations/mdx/test/css-head-mdx.test.js b/packages/integrations/mdx/test/css-head-mdx.test.js index 96ee7c9001b3..d55e2f52ac4e 100644 --- a/packages/integrations/mdx/test/css-head-mdx.test.js +++ b/packages/integrations/mdx/test/css-head-mdx.test.js @@ -28,7 +28,7 @@ describe('Head injection w/ MDX', () => { const { document } = parseHTML(html); const links = document.querySelectorAll('head link[rel=stylesheet]'); - assert.equal(links.length, 1); + assert.equal(links.length, 2); const scripts = document.querySelectorAll('script[type=module]'); assert.equal(scripts.length, 1); @@ -39,7 +39,7 @@ describe('Head injection w/ MDX', () => { const { document } = parseHTML(html); const links = document.querySelectorAll('head link[rel=stylesheet]'); - assert.equal(links.length, 1); + assert.equal(links.length, 2); }); it('injects content from a component using Content#render()', async () => { @@ -47,7 +47,7 @@ describe('Head injection w/ MDX', () => { const { document } = parseHTML(html); const links = document.querySelectorAll('head link[rel=stylesheet]'); - assert.equal(links.length, 1); + assert.equal(links.length, 2); const scripts = document.querySelectorAll('script[type=module]'); assert.equal(scripts.length, 1); @@ -67,7 +67,7 @@ describe('Head injection w/ MDX', () => { const $ = cheerio.load(html); const headLinks = $('head link[rel=stylesheet]'); - assert.equal(headLinks.length, 1); + assert.equal(headLinks.length, 2); const bodyLinks = $('body link[rel=stylesheet]'); assert.equal(bodyLinks.length, 0); @@ -79,7 +79,7 @@ describe('Head injection w/ MDX', () => { const $ = cheerio.load(html); const headLinks = $('head link[rel=stylesheet]'); - assert.equal(headLinks.length, 1); + assert.equal(headLinks.length, 2); const bodyLinks = $('body link[rel=stylesheet]'); assert.equal(bodyLinks.length, 0); @@ -92,7 +92,7 @@ describe('Head injection w/ MDX', () => { const $ = cheerio.load(html); const headLinks = $('head link[rel=stylesheet]'); - assert.equal(headLinks.length, 1); + assert.equal(headLinks.length, 2); const bodyLinks = $('body link[rel=stylesheet]'); assert.equal(bodyLinks.length, 0); diff --git a/packages/integrations/mdx/test/mdx-math.test.js b/packages/integrations/mdx/test/mdx-math.test.js index 5352eca68c8b..a68c5cbe745c 100644 --- a/packages/integrations/mdx/test/mdx-math.test.js +++ b/packages/integrations/mdx/test/mdx-math.test.js @@ -28,7 +28,7 @@ describe('MDX math', () => { const mjxContainer = document.querySelector('mjx-container[jax="SVG"]'); assert.notEqual(mjxContainer, null); - const mjxStyle = document.querySelector('style').innerHTML; + const mjxStyle = document.querySelectorAll('style')[1].innerHTML; assert.equal( mjxStyle.includes('mjx-container[jax="SVG"]'), true, @@ -62,7 +62,7 @@ describe('MDX math', () => { const mjxContainer = document.querySelector('mjx-container[jax="CHTML"]'); assert.notEqual(mjxContainer, null); - const mjxStyle = document.querySelector('style').innerHTML; + const mjxStyle = document.querySelectorAll('style')[1].innerHTML; assert.equal( mjxStyle.includes('mjx-container[jax="CHTML"]'), true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bae575df25a4..dfe8af20346a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2719,6 +2719,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/core-image-layout: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/core-image-remark-imgattr: dependencies: astro: