From 05139ef8b46de96539cc1d08148489eaf3cfd837 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 27 Aug 2024 15:45:16 +0100 Subject: [PATCH] feat(next): add `Astro.routePattern` (#11698) * feat: add `Astro.route` * change logic and add test * rebase * rebase * rename to `Astro.routePattern` * chore: added more tests * update test * add leading slash --- .changeset/nasty-crabs-worry.md | 25 +++++++++++ packages/astro/src/core/middleware/index.ts | 1 + packages/astro/src/core/render-context.ts | 35 ++++++++++++++++ packages/astro/src/types/public/context.ts | 20 +++++++++ packages/astro/test/astro-global.test.js | 42 +++++++++++++++++++ .../astro-global/src/components/Route.astro | 6 +++ .../fixtures/astro-global/src/middleware.js | 8 ++++ .../astro-global/src/pages/index.astro | 2 + .../src/pages/omit-markdown-extensions.astro | 3 ++ .../astro-global/src/pages/posts/[page].astro | 3 ++ 10 files changed, 145 insertions(+) create mode 100644 .changeset/nasty-crabs-worry.md create mode 100644 packages/astro/test/fixtures/astro-global/src/components/Route.astro create mode 100644 packages/astro/test/fixtures/astro-global/src/middleware.js diff --git a/.changeset/nasty-crabs-worry.md b/.changeset/nasty-crabs-worry.md new file mode 100644 index 0000000000..10ad6ed33f --- /dev/null +++ b/.changeset/nasty-crabs-worry.md @@ -0,0 +1,25 @@ +--- +'astro': minor +--- + +Adds a new property to the globals `Astro` and `APIContext` called `routePattern`. The `routePattern` represents the current route (component) +that is being rendered by Astro. It's usually a path pattern will look like this: `blog/[slug]`: + +```asto +--- +// src/pages/blog/[slug].astro +const route = Astro.routePattern; +console.log(route); // it will log "blog/[slug]" +--- +``` + +```js +// src/pages/index.js + +export const GET = (ctx) => { + console.log(ctx.routePattern) // it will log src/pages/index.js + return new Response.json({ loreum: "ipsum" }) +} +``` + + diff --git a/packages/astro/src/core/middleware/index.ts b/packages/astro/src/core/middleware/index.ts index 31988ae02e..6364f8c9e2 100644 --- a/packages/astro/src/core/middleware/index.ts +++ b/packages/astro/src/core/middleware/index.ts @@ -61,6 +61,7 @@ function createContext({ generator: `Astro v${ASTRO_VERSION}`, props: {}, rewrite, + routePattern: "", redirect(path, status) { return new Response(null, { status: status || 302, diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index ab0554d0c0..6e40c93241 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -37,6 +37,9 @@ import { type Pipeline, Slots, getParams, getProps } from './render/index.js'; export class RenderContext { // The first route that this instance of the context attempts to render originalRoute: RouteData; + + // The component pattern to send to the users + routePattern: string; private constructor( readonly pipeline: Pipeline, @@ -52,6 +55,7 @@ export class RenderContext { public props: Props = {}, ) { this.originalRoute = routeData; + this.routePattern = getAstroRoutePattern(routeData.component); } /** @@ -234,6 +238,7 @@ export class RenderContext { this.isRewriting = true; // we found a route and a component, we can change the status code to 200 this.status = 200; + this.routePattern = getAstroRoutePattern(routeData.component); return await this.render(component); } @@ -250,6 +255,7 @@ export class RenderContext { return { cookies, + routePattern: this.routePattern, get clientAddress() { return renderContext.clientAddress(); }, @@ -435,6 +441,7 @@ export class RenderContext { return { generator: astroStaticPartial.generator, glob: astroStaticPartial.glob, + routePattern: this.routePattern, cookies, get clientAddress() { return renderContext.clientAddress(); @@ -566,3 +573,31 @@ export class RenderContext { }); } } + +/** + * Return the component path without the `srcDir` and `pages` + * @param component + */ +function getAstroRoutePattern(component: RouteData['component']): string { + let splitComponent = component.split("/"); + while (true) { + const currentPart = splitComponent.shift(); + if (!currentPart) {break} + + // "pages" isn't configurable, so it's safe to stop here + if (currentPart === "pages") { + break + } + } + + const pathWithoutPages = splitComponent.join("/"); + // This covers cases where routes don't have extensions, so they can be: [slug] or [...slug] + if (pathWithoutPages.endsWith("]")) { + return pathWithoutPages; + } + splitComponent = splitComponent.join("/").split("."); + + // this should remove the extension + splitComponent.pop(); + return "/" + splitComponent.join("/"); +} diff --git a/packages/astro/src/types/public/context.ts b/packages/astro/src/types/public/context.ts index 5f05b7a662..e8b4f7e38b 100644 --- a/packages/astro/src/types/public/context.ts +++ b/packages/astro/src/types/public/context.ts @@ -137,6 +137,16 @@ export interface AstroGlobal< * ``` */ rewrite: AstroSharedContext['rewrite']; + + /** + * The route currently rendered. It's stripped of the `srcDir` and the `pages` folder, and it doesn't contain the extension. + * + * ## Example + * - The value when rendering `src/pages/index.astro` will `index`. + * - The value when rendering `src/pages/blog/[slug].astro` will `blog/[slug]`. + * - The value when rendering `src/pages/[...path].astro` will `[...path]`. + */ + routePattern: string; /** * The element allows a component to reference itself recursively. * @@ -498,4 +508,14 @@ export interface APIContext< * The current locale computed from the URL of the request. It matches the locales in `i18n.locales`, and returns `undefined` otherwise. */ currentLocale: string | undefined; + + /** + * The route currently rendered. It's stripped of the `srcDir` and the `pages` folder, and it doesn't contain the extension. + * + * ## Example + * - The value when rendering `src/pages/index.astro` will `index`. + * - The value when rendering `src/pages/blog/[slug].astro` will `blog/[slug]`. + * - The value when rendering `src/pages/[...path].astro` will `[...path]`. + */ + routePattern: string } diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js index e2fbfd50bb..424f8011f3 100644 --- a/packages/astro/test/astro-global.test.js +++ b/packages/astro/test/astro-global.test.js @@ -46,6 +46,17 @@ describe('Astro Global', () => { false, ); }); + + it("Astro.route.pattern has the right value in pages and components", async () => { + let html = await fixture.fetch('/blog').then((res) => res.text()); + let $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/index/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/index/); + html = await fixture.fetch('/blog/omit-markdown-extensions/').then((res) => res.text()); + $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/omit-markdown-extensions/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/omit-markdown-extensions/); + }) }); describe('build', () => { @@ -81,6 +92,24 @@ describe('Astro Global', () => { assert.equal($('[data-file]').length, 8); assert.equal($('.post-url[href]').length, 8); }); + + it("Astro.route.pattern has the right value in pages and components", async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/index/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/index/); + + html =await fixture.readFile('/omit-markdown-extensions/index.html'); + $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/omit-markdown-extensions/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/omit-markdown-extensions/); + + html = await fixture.readFile('/posts/1/index.html'); + $ = cheerio.load(html); + assert.equal($("#pattern").text(), "Astro route pattern: /posts/[page]"); + assert.equal($("#pattern-middleware").text(), "Astro route pattern middleware: /posts/[page]"); + + }) }); describe('app', () => { @@ -105,6 +134,19 @@ describe('Astro Global', () => { const $ = cheerio.load(html); assert.equal($('#site').attr('href'), 'https://mysite.dev/subsite/'); }); + + it("Astro.route.pattern has the right value in pages and components", async () => { + let response = await app.render(new Request('https://example.com/')); + let html = await response.text(); + let $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/index/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/index/); + response = await app.render(new Request('https://example.com/omit-markdown-extensions')); + html = await response.text(); + $ = cheerio.load(html); + assert.match($("#pattern").text(), /Astro route pattern: \/omit-markdown-extensions/); + assert.match($("#pattern-middleware").text(), /Astro route pattern middleware: \/omit-markdown-extensions/); + }) }); }); diff --git a/packages/astro/test/fixtures/astro-global/src/components/Route.astro b/packages/astro/test/fixtures/astro-global/src/components/Route.astro new file mode 100644 index 0000000000..9dea56df0c --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/components/Route.astro @@ -0,0 +1,6 @@ +--- +const pattern = Astro.routePattern; +const localsPattern = Astro.locals.localsPattern; +--- +

Astro route pattern: {pattern}

+

Astro route pattern middleware: {localsPattern}

diff --git a/packages/astro/test/fixtures/astro-global/src/middleware.js b/packages/astro/test/fixtures/astro-global/src/middleware.js new file mode 100644 index 0000000000..ee6e8a4e84 --- /dev/null +++ b/packages/astro/test/fixtures/astro-global/src/middleware.js @@ -0,0 +1,8 @@ + + +export function onRequest(ctx, next) { + ctx.locals = { + localsPattern: ctx.routePattern + }; + return next() +} diff --git a/packages/astro/test/fixtures/astro-global/src/pages/index.astro b/packages/astro/test/fixtures/astro-global/src/pages/index.astro index ad6012ce68..6cc7be8b1d 100644 --- a/packages/astro/test/fixtures/astro-global/src/pages/index.astro +++ b/packages/astro/test/fixtures/astro-global/src/pages/index.astro @@ -1,5 +1,6 @@ --- import Child from '../components/Child.astro'; +import Route from '../components/Route.astro'; const canonicalURL = new URL(Astro.url.pathname, Astro.site ?? `http://example.com`); --- @@ -13,5 +14,6 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site ?? `http://example.c Home + diff --git a/packages/astro/test/fixtures/astro-global/src/pages/omit-markdown-extensions.astro b/packages/astro/test/fixtures/astro-global/src/pages/omit-markdown-extensions.astro index 67f307b0f0..68101d447d 100644 --- a/packages/astro/test/fixtures/astro-global/src/pages/omit-markdown-extensions.astro +++ b/packages/astro/test/fixtures/astro-global/src/pages/omit-markdown-extensions.astro @@ -1,4 +1,6 @@ --- +import Route from "../components/Route.astro"; + const markdownPosts = await Astro.glob('./post/**/*.{markdown,mdown,mkdn,mkd,mdwn,md}'); const markdownExtensions = /(\.(markdown|mdown|mkdn|mkd|mdwn|md))$/g const aUrlContainsExtension = markdownPosts.some((page:any)=> { @@ -12,5 +14,6 @@ const aUrlContainsExtension = markdownPosts.some((page:any)=> {

Placeholder

+ diff --git a/packages/astro/test/fixtures/astro-global/src/pages/posts/[page].astro b/packages/astro/test/fixtures/astro-global/src/pages/posts/[page].astro index 0950ebe26f..7bdaa3b364 100644 --- a/packages/astro/test/fixtures/astro-global/src/pages/posts/[page].astro +++ b/packages/astro/test/fixtures/astro-global/src/pages/posts/[page].astro @@ -1,4 +1,6 @@ --- +import Route from "../../components/Route.astro"; + export async function getStaticPaths({paginate}) { const data = await Astro.glob('../post/*.md'); return paginate(data, {pageSize: 1}); @@ -19,5 +21,6 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site ?? `http://example.c Read ))} +