Created
February 12, 2025 22:46
-
-
Save nicolas-zozol/93348cd51bba1f0a9d455e5e11aaa872 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| I want a discriminate for post (or product) so that we don't look into a database: | |
| Also '/learn' is the equivalent of '/blog'. It's a parameter named homeUrl | |
| We have 3 discriminant for routes: | |
| - locale. If empty, 'en'. Can be 'fr'. | |
| - categories : ['blockchain'] or ['blockchain','security'] : can be undefined for blogroll | |
| - page/number : Blog roll current page | |
| - /s/slug : last part of the url, if present ; /s/ precises there will be a slug (s for slug) | |
| Discriminants are needed so that NextJS can do pregeneration at | |
| build time with ISR. If not, we would require searchParams | |
| than can be done only at runtime. | |
| - /: Home page | |
| - /portfolio : Portfolio page | |
| - /fr/portfolio/ : Portfolio page in French | |
| - /learn : Blog homepage | |
| - /learn/page/2 : Second page of the Blog homepage | |
| - /learn/fr : Blog homepage in French | |
| - /learn/blockchain : Blog category page | |
| - /learn/blockchain/page/2 : Blog category page | |
| - /learn/blockchain/s/what-is-blockchain : Blog post page | |
| - /learn/fr/blockchain/ : Blog category page in French | |
| - /learn/fr/blockchain/page/2 : Blog category page in French | |
| - /learn/fr/blockchain/s/voici-la-blockchain/ : Blog post page in French | |
| - /learn/blockchain/security : Blog category page | |
| - /learn/blockchain/security/page/2 : Blog category page | |
| */ | |
| import { C, N, F, SingleParser, Tuple } from '@masala/parser'; | |
| import { X } from '@masala/x'; | |
| type RouteType = 'BLOG_ROLL' | 'CATEGORY' | 'POST'; | |
| interface ParseResultNoLocale { | |
| type: RouteType; | |
| categories?: string[] | undefined; // e.g. ['blockchain', 'security'] | |
| slug?: string | undefined; | |
| page?: number; // 2 if | |
| } | |
| interface ParseResult extends ParseResultNoLocale { | |
| isDefaultLocale: boolean; | |
| locale: string; // 'en' or 'fr' | |
| } | |
| type PR = Partial<ParseResult>; | |
| const slugDiscriminant = 's'; | |
| const slash = C.string('/'); | |
| const word = F.not(slash) | |
| .rep() | |
| .map((c) => c.join('')); | |
| const categoryWord = word.filter((w) => w !== 'page' && w !== slugDiscriminant); | |
| export const firstWordParser = C.char('/').then(word).last(); | |
| export const pageParser = C.string('/page/') | |
| .then(N.integer()) | |
| .last() | |
| .map((n) => ({ page: n })) | |
| .then(F.eos()) | |
| .first() as SingleParser<{ page: number }>; | |
| const singleCategory = (allCategories: string[][]) => | |
| slash | |
| .then( | |
| categoryWord.filter((w) => | |
| allCategories.some((cats) => cats.includes(w)), | |
| ), | |
| ) | |
| .last(); | |
| const categoriesParser = (allCategories: string[][]) => | |
| singleCategory(allCategories) | |
| .rep() | |
| .array() | |
| .filter((found) => | |
| allCategories.some((cats) => { | |
| return JSON.stringify(cats) === JSON.stringify(found); | |
| }), | |
| ) | |
| .map((list) => ({ categories: list })); | |
| const categories = (allCategories: string[][]) => | |
| X.split(slash.drop()) | |
| .filter((urlCategories: string[]) => | |
| allCategories.some( | |
| (list) => JSON.stringify(list) == JSON.stringify(urlCategories), | |
| ), | |
| ) | |
| .map((list: string[]) => ({ categories: list })); | |
| const slugParser = C.string(`/${slugDiscriminant}/`) | |
| .then(word) | |
| .last() | |
| .map((slug) => ({ slug })); | |
| export const categoriesHome = (allCategories: string[][]) => | |
| categoriesParser(allCategories).then(F.eos()).first() as SingleParser<{ | |
| categories: string[]; | |
| }>; | |
| export const categoriesHomeParser = (allCategories: string[][]) => | |
| categoriesParser(allCategories) | |
| .then(F.eos()) | |
| .map((tuple) => { | |
| const data = tuple.array() as PR[]; | |
| const categories = data[0]; | |
| return { | |
| type: 'CATEGORY', | |
| ...categories, | |
| page: 1, | |
| } as ParseResultNoLocale; | |
| }); | |
| export const categoriesPageParser = (allCategories: string[][]) => | |
| categoriesParser(allCategories) | |
| .then(pageParser) | |
| .map((tuple) => { | |
| const data = tuple.array() as PR[]; | |
| const categories = data[0]; | |
| const page = data[1]; | |
| return { | |
| type: 'CATEGORY', | |
| ...categories, | |
| ...page, | |
| } as ParseResultNoLocale; | |
| }); | |
| export const postParser = (allCategories: string[][]) => | |
| categoriesParser(allCategories) | |
| .then(slugParser) | |
| .map((tuple) => { | |
| const data = tuple.array() as PR[]; | |
| const categories = data[0]; | |
| const slug = data[1]; | |
| return { | |
| type: 'POST', | |
| ...slug, | |
| ...categories, | |
| } as ParseResultNoLocale; | |
| }) | |
| .then(F.eos().drop()) | |
| .single(); | |
| export const routeParsersForSpecs = { | |
| categoriesParser, | |
| slugParser, | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment