Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(next): add Astro.routePattern #11698

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/nasty-crabs-worry.md
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ematipico I know this has already merged, but according to the line in docs about index.* showing only /, is this code comment about what's logged correct? It also includes the file extension here.

We will proofread all the changesets before 5.0 beta release anyway, so I'm not concerned about updating this right now. but I just want to make sure I'm understanding correctly!

return new Response.json({ loreum: "ipsum" })
}
```


1 change: 1 addition & 0 deletions packages/astro/src/core/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function createContext({
generator: `Astro v${ASTRO_VERSION}`,
props: {},
rewrite,
routePattern: "",
redirect(path, status) {
return new Response(null, {
status: status || 302,
Expand Down
35 changes: 35 additions & 0 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -52,6 +55,7 @@ export class RenderContext {
public props: Props = {},
) {
this.originalRoute = routeData;
this.routePattern = getAstroRoutePattern(routeData.component);
}

/**
Expand Down Expand Up @@ -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);
}

Expand All @@ -250,6 +255,7 @@ export class RenderContext {

return {
cookies,
routePattern: this.routePattern,
get clientAddress() {
return renderContext.clientAddress();
},
Expand Down Expand Up @@ -435,6 +441,7 @@ export class RenderContext {
return {
generator: astroStaticPartial.generator,
glob: astroStaticPartial.glob,
routePattern: this.routePattern,
cookies,
get clientAddress() {
return renderContext.clientAddress();
Expand Down Expand Up @@ -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") {
florian-lefebvre marked this conversation as resolved.
Show resolved Hide resolved
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("/");
}
20 changes: 20 additions & 0 deletions packages/astro/src/types/public/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Astro.self /> element allows a component to reference itself recursively.
*
Expand Down Expand Up @@ -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
}
42 changes: 42 additions & 0 deletions packages/astro/test/astro-global.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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/);
})
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
const pattern = Astro.routePattern;
const localsPattern = Astro.locals.localsPattern;
---
<p id="pattern">Astro route pattern: {pattern}</p>
<p id="pattern-middleware">Astro route pattern middleware: {localsPattern}</p>
8 changes: 8 additions & 0 deletions packages/astro/test/fixtures/astro-global/src/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


export function onRequest(ctx, next) {
ctx.locals = {
localsPattern: ctx.routePattern
};
return next()
}
Original file line number Diff line number Diff line change
@@ -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`);
---
<html>
Expand All @@ -13,5 +14,6 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site ?? `http://example.c
<a id="site" href={Astro.site}>Home</a>

<Child />
<Route />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -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)=> {
Expand All @@ -12,5 +14,6 @@ const aUrlContainsExtension = markdownPosts.some((page:any)=> {
</head>
<body>
<p data-any-url-contains-extension={JSON.stringify(aUrlContainsExtension)}>Placeholder</p>
<Route />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -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});
Expand All @@ -19,5 +21,6 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site ?? `http://example.c
<a class="post-url" href={data.url}>Read</a>
</div>
))}
<Route />
</body>
</html>
Loading