From a71e9b93b317edc0ded49d4d50f1b7841c8cd428 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 6 Dec 2024 18:08:39 +0800 Subject: [PATCH 1/4] Fix frontmatter parsing with utf8 bom (#12664) --- .changeset/unlucky-wasps-refuse.md | 5 ++++ packages/markdown/remark/src/frontmatter.ts | 7 ++--- .../markdown/remark/test/frontmatter.test.js | 26 ++++++++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 .changeset/unlucky-wasps-refuse.md diff --git a/.changeset/unlucky-wasps-refuse.md b/.changeset/unlucky-wasps-refuse.md new file mode 100644 index 000000000000..5e0d4d664be2 --- /dev/null +++ b/.changeset/unlucky-wasps-refuse.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdown-remark': patch +--- + +Fixes frontmatter parsing if file is encoded in UTF8 with BOM diff --git a/packages/markdown/remark/src/frontmatter.ts b/packages/markdown/remark/src/frontmatter.ts index 94fbec2b3312..fe878df662b5 100644 --- a/packages/markdown/remark/src/frontmatter.ts +++ b/packages/markdown/remark/src/frontmatter.ts @@ -11,9 +11,10 @@ export function isFrontmatterValid(frontmatter: Record) { } // Capture frontmatter wrapped with `---`, including any characters and new lines within it. -// Only capture if it exists near the top of the file (whitespaces between the start of file and -// the start of `---` are allowed) -const frontmatterRE = /^\s*---([\s\S]*?\n)---/; +// Only capture if `---` exists near the top of the file, including: +// 1. Start of file (including if has BOM encoding) +// 2. Start of file with any whitespace (but `---` must still start on a new line) +const frontmatterRE = /(?:^\uFEFF?|^\s*\n)---([\s\S]*?\n)---/; export function extractFrontmatter(code: string): string | undefined { return frontmatterRE.exec(code)?.[1]; } diff --git a/packages/markdown/remark/test/frontmatter.test.js b/packages/markdown/remark/test/frontmatter.test.js index 6a8b6b37ca47..155368e59f2e 100644 --- a/packages/markdown/remark/test/frontmatter.test.js +++ b/packages/markdown/remark/test/frontmatter.test.js @@ -2,16 +2,21 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { extractFrontmatter, parseFrontmatter } from '../dist/index.js'; +const bom = '\uFEFF'; + describe('extractFrontmatter', () => { it('works', () => { const yaml = `\nfoo: bar\n`; assert.equal(extractFrontmatter(`---${yaml}---`), yaml); - assert.equal(extractFrontmatter(` ---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`${bom}---${yaml}---`), yaml); assert.equal(extractFrontmatter(`\n---${yaml}---`), yaml); assert.equal(extractFrontmatter(`\n \n---${yaml}---`), yaml); assert.equal(extractFrontmatter(`---${yaml}---\ncontent`), yaml); + assert.equal(extractFrontmatter(`${bom}---${yaml}---\ncontent`), yaml); assert.equal(extractFrontmatter(`\n\n---${yaml}---\n\ncontent`), yaml); assert.equal(extractFrontmatter(`\n \n---${yaml}---\n\ncontent`), yaml); + assert.equal(extractFrontmatter(` ---${yaml}---`), undefined); + assert.equal(extractFrontmatter(`---${yaml} ---`), undefined); assert.equal(extractFrontmatter(`text\n---${yaml}---\n\ncontent`), undefined); }); }); @@ -24,10 +29,10 @@ describe('parseFrontmatter', () => { rawFrontmatter: yaml, content: '', }); - assert.deepEqual(parseFrontmatter(` ---${yaml}---`), { + assert.deepEqual(parseFrontmatter(`${bom}---${yaml}---`), { frontmatter: { foo: 'bar' }, rawFrontmatter: yaml, - content: ' ', + content: bom, }); assert.deepEqual(parseFrontmatter(`\n---${yaml}---`), { frontmatter: { foo: 'bar' }, @@ -44,6 +49,11 @@ describe('parseFrontmatter', () => { rawFrontmatter: yaml, content: '\ncontent', }); + assert.deepEqual(parseFrontmatter(`${bom}---${yaml}---\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: `${bom}\ncontent`, + }); assert.deepEqual(parseFrontmatter(`\n\n---${yaml}---\n\ncontent`), { frontmatter: { foo: 'bar' }, rawFrontmatter: yaml, @@ -54,6 +64,16 @@ describe('parseFrontmatter', () => { rawFrontmatter: yaml, content: '\n \n\n\ncontent', }); + assert.deepEqual(parseFrontmatter(` ---${yaml}---`), { + frontmatter: {}, + rawFrontmatter: '', + content: ` ---${yaml}---`, + }); + assert.deepEqual(parseFrontmatter(`---${yaml} ---`), { + frontmatter: {}, + rawFrontmatter: '', + content: `---${yaml} ---`, + }); assert.deepEqual(parseFrontmatter(`text\n---${yaml}---\n\ncontent`), { frontmatter: {}, rawFrontmatter: '', From 8f8f15ca1d51a112b7344787ac02180e0aeb04bb Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 6 Dec 2024 10:31:23 +0000 Subject: [PATCH 2/4] fix(routing): don't attach locals to request (#12647) * fix(routing): don't attach locals to request * apply feedback --- packages/astro/src/core/app/index.ts | 1 - packages/astro/src/core/base-pipeline.ts | 3 --- packages/astro/src/core/middleware/index.ts | 16 +++++++++++----- packages/astro/src/core/request.ts | 5 ----- .../astro/src/vite-plugin-astro-server/route.ts | 3 ++- .../test/units/routing/api-context.test.js | 17 +++++++++++++++++ .../vite-plugin-astro-server/response.test.js | 12 +++++++----- 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 26a6523d8ec9..36543b5cac9c 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -259,7 +259,6 @@ export class App { this.#logger.error(null, error.stack!); return this.#renderError(request, { status: 500, error, clientAddress }); } - Reflect.set(request, clientLocalsSymbol, locals); } if (!routeData) { routeData = this.match(request); diff --git a/packages/astro/src/core/base-pipeline.ts b/packages/astro/src/core/base-pipeline.ts index 545cf5ed285b..81cc553ba714 100644 --- a/packages/astro/src/core/base-pipeline.ts +++ b/packages/astro/src/core/base-pipeline.ts @@ -1,4 +1,3 @@ -import { setGetEnv } from '../env/runtime.js'; import { createI18nMiddleware } from '../i18n/middleware.js'; import type { ComponentInstance } from '../types/astro.js'; import type { MiddlewareHandler, RewritePayload } from '../types/public/common.js'; @@ -10,8 +9,6 @@ import type { SSRResult, } from '../types/public/internal.js'; import { createOriginCheckMiddleware } from './app/middlewares.js'; -import { AstroError } from './errors/errors.js'; -import { AstroErrorData } from './errors/index.js'; import type { Logger } from './logger/core.js'; import { NOOP_MIDDLEWARE_FN } from './middleware/noop-middleware.js'; import { sequence } from './middleware/sequence.js'; diff --git a/packages/astro/src/core/middleware/index.ts b/packages/astro/src/core/middleware/index.ts index c7ed6e647987..7f4c2867edf9 100644 --- a/packages/astro/src/core/middleware/index.ts +++ b/packages/astro/src/core/middleware/index.ts @@ -39,6 +39,11 @@ export type CreateContext = { * User defined default locale */ defaultLocale: string; + + /** + * Initial value of the locals + */ + locals: App.Locals; }; /** @@ -49,6 +54,7 @@ function createContext({ params = {}, userDefinedLocales = [], defaultLocale = '', + locals, }: CreateContext): APIContext { let preferredLocale: string | undefined = undefined; let preferredLocaleList: string[] | undefined = undefined; @@ -104,15 +110,15 @@ function createContext({ return clientIpAddress; }, get locals() { - let locals = Reflect.get(request, clientLocalsSymbol); + // TODO: deprecate this usage. This is used only by the edge middleware for now, so its usage should be basically none. + let _locals = locals ?? Reflect.get(request, clientLocalsSymbol); if (locals === undefined) { - locals = {}; - Reflect.set(request, clientLocalsSymbol, locals); + _locals = {}; } - if (typeof locals !== 'object') { + if (typeof _locals !== 'object') { throw new AstroError(AstroErrorData.LocalsNotAnObject); } - return locals; + return _locals; }, set locals(_) { throw new AstroError(AstroErrorData.LocalsReassigned); diff --git a/packages/astro/src/core/request.ts b/packages/astro/src/core/request.ts index b8cc89fb8464..302370ba8d61 100644 --- a/packages/astro/src/core/request.ts +++ b/packages/astro/src/core/request.ts @@ -24,8 +24,6 @@ export interface CreateRequestOptions { routePattern: string; } -const clientLocalsSymbol = Symbol.for('astro.locals'); - /** * Used by astro internals to create a web standard request object. * @@ -39,7 +37,6 @@ export function createRequest({ method = 'GET', body = undefined, logger, - locals, isPrerendered = false, routePattern, }: CreateRequestOptions): Request { @@ -93,7 +90,5 @@ export function createRequest({ }); } - Reflect.set(request, clientLocalsSymbol, locals ?? {}); - return request; } diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index a3a5bc93cca3..b4b659805408 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -160,6 +160,7 @@ export async function handleRoute({ let mod: ComponentInstance | undefined = undefined; let route: RouteData; const middleware = (await loadMiddleware(loader)).onRequest; + // This is required for adapters to set locals in dev mode. They use a dev server middleware to inject locals to the `http.IncomingRequest` object. const locals = Reflect.get(incomingRequest, clientLocalsSymbol); const { preloadedComponent } = matchedRoute; @@ -242,7 +243,7 @@ export async function handleRoute({ const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline); if (fourOhFourRoute) { renderContext = await RenderContext.create({ - locals, + locals: {}, pipeline, pathname, middleware: isDefaultPrerendered404(fourOhFourRoute.route) ? undefined : middleware, diff --git a/packages/astro/test/units/routing/api-context.test.js b/packages/astro/test/units/routing/api-context.test.js index 1e955e50cc46..027a70f8bf13 100644 --- a/packages/astro/test/units/routing/api-context.test.js +++ b/packages/astro/test/units/routing/api-context.test.js @@ -16,4 +16,21 @@ describe('createAPIContext', () => { assert.equal(context.clientAddress, '192.0.2.43'); }); + + it('should return the correct locals', () => { + const request = new Request('http://example.com', { + headers: { + 'x-forwarded-for': '192.0.2.43, 172.16.58.3', + }, + }); + + const context = createContext({ + request, + locals: { + foo: 'bar', + }, + }); + + assert.deepEqual(context.locals, { foo: 'bar' }); + }); }); diff --git a/packages/astro/test/units/vite-plugin-astro-server/response.test.js b/packages/astro/test/units/vite-plugin-astro-server/response.test.js index 2d256fb71ae2..60f583939949 100644 --- a/packages/astro/test/units/vite-plugin-astro-server/response.test.js +++ b/packages/astro/test/units/vite-plugin-astro-server/response.test.js @@ -87,16 +87,18 @@ describe('endpoints', () => { url: '/streaming', }); - const locals = { cancelledByTheServer: false }; - req[Symbol.for('astro.locals')] = locals; - container.handle(req, res); await new Promise((resolve) => setTimeout(resolve, 500)); res.emit('close'); - await done; + try { + await done; - assert.equal(locals.cancelledByTheServer, true); + assert.ok(true); + + } catch (err) { + assert.fail(err) + } }); }); From 72c1d5d531d1fb59e29707986405a6aa7f9ca835 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 6 Dec 2024 10:32:09 +0000 Subject: [PATCH 3/4] [ci] format --- .../astro/test/units/vite-plugin-astro-server/response.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/astro/test/units/vite-plugin-astro-server/response.test.js b/packages/astro/test/units/vite-plugin-astro-server/response.test.js index 60f583939949..912ef4102fe4 100644 --- a/packages/astro/test/units/vite-plugin-astro-server/response.test.js +++ b/packages/astro/test/units/vite-plugin-astro-server/response.test.js @@ -96,9 +96,8 @@ describe('endpoints', () => { await done; assert.ok(true); - } catch (err) { - assert.fail(err) + assert.fail(err); } }); }); From e21c7e67fde1155cf593fd2b40010c5e2c2cd3f2 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger <5098874+sarah11918@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:14:06 -0400 Subject: [PATCH 4/4] [error message] updates docs link (#12653) --- .changeset/bright-gifts-prove.md | 5 +++++ packages/astro/src/core/errors/errors-data.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/bright-gifts-prove.md diff --git a/.changeset/bright-gifts-prove.md b/.changeset/bright-gifts-prove.md new file mode 100644 index 000000000000..14b1a3bde187 --- /dev/null +++ b/.changeset/bright-gifts-prove.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Updates a reference in an error message diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 7d096ac8a707..398c956ea8e3 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -955,7 +955,7 @@ export const AstroGlobNoMatch = { /** * @docs * @see - * - [Astro.redirect](https://docs.astro.build/en/reference/api-reference/#astroredirect) + * - [Astro.redirect](https://docs.astro.build/en/reference/api-reference/#redirect) * @description * A redirect must be given a location with the `Location` header. */