Last active
August 29, 2023 09:08
-
-
Save gabrielhamel/dcdef4d4f2992cc42816ffcbbe7013cc to your computer and use it in GitHub Desktop.
Validate NextJS url by reading the Page API directory
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
import { describe, expect, test } from "vitest"; | |
import { url as urlWithoutRoot } from "@/utils/url"; | |
const url = (path: string) => | |
urlWithoutRoot(path, "src/utils/url/__tests__/pages"); | |
describe("Check if url exist", () => { | |
test("Index at the root", () => { | |
expect(url("/")).toEqual("/"); | |
}); | |
test("Index in a directory", () => { | |
expect(url("/directory")).toEqual("/directory"); | |
}); | |
test("Query params", () => { | |
expect(url("/directory?key=value")).toEqual("/directory?key=value"); | |
}); | |
test("No index in a directory", () => { | |
expect(() => url("/empty")).toThrowError("Url incomplete"); | |
}); | |
test("Simple url with a static file", () => { | |
expect(url("/test")).toEqual("/test"); | |
}); | |
test("Dynamic url with folder", () => { | |
expect(url("/affair/123/fed")).toEqual("/affair/123/fed"); | |
}); | |
test("Dynamic url with file", () => { | |
expect(url("/documents/123")).toEqual("/documents/123"); | |
}); | |
test("Multiple dynamic folder in url", () => { | |
expect(url("/affair/123/fed/321/create")).toEqual( | |
"/affair/123/fed/321/create", | |
); | |
}); | |
test("Multiple dynamic file and folder in url", () => { | |
expect(url("/tree/first/second/third")).toEqual("/tree/first/second/third"); | |
}); | |
test("Not existing url", () => { | |
expect(() => url("/report/123")).toThrowError("Url doesn't exists"); | |
}); | |
test("Url incomplete", () => { | |
expect(() => url("/affair")).toThrowError("Url incomplete"); | |
}); | |
test("Dynamic Url incomplete", () => { | |
expect(() => url("/affair/123")).toThrowError("Url incomplete"); | |
}); | |
test("Catch-all segment", () => { | |
expect(url("/catch-all/no-optional/a/b/c")).toEqual( | |
"/catch-all/no-optional/a/b/c", | |
); | |
expect(() => url("/catch-all/non-optional")).toThrowError( | |
"Url doesn't exists", | |
); | |
}); | |
test("Optional Catch-all segment", () => { | |
expect(url("/catch-all/optional/a/b/c")).toEqual( | |
"/catch-all/optional/a/b/c", | |
); | |
expect(url("/catch-all/optional")).toEqual("/catch-all/optional"); | |
}); | |
}); |
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
import fs from "fs"; | |
import path from "path"; | |
const getDynamicPath = ( | |
root: string, | |
leftPath: string, | |
rightPath: string, | |
): string => { | |
const newLeftPath = | |
leftPath === "/" | |
? leftPath | |
: getDynamicPath( | |
root, | |
path.dirname(leftPath), | |
path.join(path.basename(leftPath), rightPath), | |
); | |
if (newLeftPath.endsWith(".tsx")) { | |
return newLeftPath; | |
} | |
const rightFile = rightPath.split(path.sep)[0]; | |
const possibleDirectory = path.join(newLeftPath, rightFile); | |
if (fs.existsSync(path.join(root, possibleDirectory))) { | |
return possibleDirectory; | |
} | |
const possibleFile = path.join(newLeftPath, `${rightFile}.tsx`); | |
if (fs.existsSync(path.join(root, possibleFile))) { | |
return possibleFile; | |
} | |
const possibleDynamic = fs | |
.readdirSync(path.join(root, newLeftPath)) | |
.find((item) => /^\[.+](\.tsx)?$/.test(item)); | |
if (possibleDynamic) { | |
return path.join(newLeftPath, possibleDynamic); | |
} | |
throw new Error("Url doesn't exists"); | |
}; | |
/** | |
* Validates a Next.js URL by searching for the corresponding page in the pages API directory. | |
* | |
* @param urlToCheck - The URL to validate. | |
* @param pagesDirectory - The directory where Next.js pages are located. Default is "src/pages". | |
* @returns The validated URL if it corresponds to an existing Next.js page, otherwise throws an error. | |
* @throws {Error} If the URL doesn't exist or is incomplete. | |
*/ | |
export const url = ( | |
urlToCheck: string, | |
pagesDirectory = "src/pages", | |
): string => { | |
const urlWithoutQueryParam = new URL(urlToCheck, "http://origin"); | |
const dynamicPath = getDynamicPath( | |
pagesDirectory, | |
urlWithoutQueryParam.pathname, | |
"/", | |
); | |
const pathTest = path.join(pagesDirectory, dynamicPath); | |
const indexTest = path.join(pathTest, "index.tsx"); | |
if ( | |
fs.statSync(pathTest).isFile() || | |
(fs.existsSync(indexTest) && fs.statSync(indexTest).isFile()) | |
) { | |
return urlToCheck; | |
} | |
const possibleOptionalCatchAll = fs | |
.readdirSync(path.join(pagesDirectory, dynamicPath)) | |
.find((item) => /^\[\[\.\.\..+]]\.tsx$/.test(item)); | |
if (possibleOptionalCatchAll) { | |
return urlToCheck; | |
} | |
throw new Error("Url incomplete"); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment