UNPKG

@sls-next/core

Version:
154 lines (130 loc) 5.33 kB
// copied as is from https://github.com/zeit/next.js/blob/canary/packages/next/next-server/lib/router/utils/sorted-routes.ts /* eslint-disable */ // @ts-nocheck class UrlNode { placeholder: boolean = true; children: Map<string, UrlNode> = new Map(); slugName: string | null = null; restSlugName: string | null = null; insert(urlPath: string): void { this._insert(urlPath.split("/").filter(Boolean), [], false); } smoosh(): string[] { return this._smoosh(); } private _smoosh(prefix: string = "/"): string[] { const childrenPaths = [...this.children.keys()].sort(); if (this.slugName !== null) { childrenPaths.splice(childrenPaths.indexOf("[]"), 1); } if (this.restSlugName !== null) { childrenPaths.splice(childrenPaths.indexOf("[...]"), 1); } const routes = childrenPaths .map((c) => this.children.get(c)!._smoosh(`${prefix}${c}/`)) .reduce((prev, curr) => [...prev, ...curr], []); if (this.slugName !== null) { routes.push( ...this.children.get("[]")!._smoosh(`${prefix}[${this.slugName}]/`) ); } if (!this.placeholder) { routes.unshift(prefix === "/" ? "/" : prefix.slice(0, -1)); } if (this.restSlugName !== null) { routes.push( ...this.children .get("[...]")! ._smoosh(`${prefix}[...${this.restSlugName}]/`) ); } return routes; } private _insert( urlPaths: string[], slugNames: string[], isCatchAll: boolean ): void { if (urlPaths.length === 0) { this.placeholder = false; return; } if (isCatchAll) { throw new Error(`Catch-all must be the last part of the URL.`); } // The next segment in the urlPaths list let nextSegment = urlPaths[0]; // Check if the segment matches `[something]` if (nextSegment.startsWith("[") && nextSegment.endsWith("]")) { // Strip `[` and `]`, leaving only `something` let segmentName = nextSegment.slice(1, -1); if (segmentName.startsWith("...")) { segmentName = segmentName.substring(3); isCatchAll = true; } if (segmentName.startsWith(".")) { throw new Error( `Segment names may not start with erroneous periods ('${segmentName}').` ); } function handleSlug(previousSlug: string | null, nextSlug: string) { if (previousSlug !== null) { // If the specific segment already has a slug but the slug is not `something` // This prevents collisions like: // pages/[post]/index.js // pages/[id]/index.js // Because currently multiple dynamic params on the same segment level are not supported if (previousSlug !== nextSlug) { // TODO: This error seems to be confusing for users, needs an err.sh link, the description can be based on above comment. throw new Error( `You cannot use different slug names for the same dynamic path ('${previousSlug}' !== '${nextSlug}').` ); } } if (slugNames.indexOf(nextSlug) !== -1) { throw new Error( `You cannot have the same slug name "${nextSlug}" repeat within a single dynamic path` ); } slugNames.push(nextSlug); } if (isCatchAll) { handleSlug(this.restSlugName, segmentName); // slugName is kept as it can only be one particular slugName this.restSlugName = segmentName; // nextSegment is overwritten to [] so that it can later be sorted specifically nextSegment = "[...]"; } else { handleSlug(this.slugName, segmentName); // slugName is kept as it can only be one particular slugName this.slugName = segmentName; // nextSegment is overwritten to [] so that it can later be sorted specifically nextSegment = "[]"; } } // If this UrlNode doesn't have the nextSegment yet we create a new child UrlNode if (!this.children.has(nextSegment)) { this.children.set(nextSegment, new UrlNode()); } this.children .get(nextSegment)! ._insert(urlPaths.slice(1), slugNames, isCatchAll); } } export function getSortedRoutes(normalizedPages: string[]): string[] { // First the UrlNode is created, and every UrlNode can have only 1 dynamic segment // Eg you can't have pages/[post]/abc.js and pages/[hello]/something-else.js // Only 1 dynamic segment per nesting level // So in the case that is test/integration/dynamic-routing it'll be this: // pages/[post]/comments.js // pages/blog/[post]/comment/[id].js // Both are fine because `pages/[post]` and `pages/blog` are on the same level // So in this case `UrlNode` created here has `this.slugName === 'post'` // And since your PR passed through `slugName` as an array basically it'd including it in too many possibilities // Instead what has to be passed through is the upwards path's dynamic names const root = new UrlNode(); // Here the `root` gets injected multiple paths, and insert will break them up into sublevels normalizedPages.forEach((pagePath) => root.insert(pagePath)); // Smoosh will then sort those sublevels up to the point where you get the correct route definition priority return root.smoosh(); }