Skip to content

Instantly share code, notes, and snippets.

@gabrielhamel
Last active August 29, 2023 09:08
Show Gist options
  • Save gabrielhamel/dcdef4d4f2992cc42816ffcbbe7013cc to your computer and use it in GitHub Desktop.
Save gabrielhamel/dcdef4d4f2992cc42816ffcbbe7013cc to your computer and use it in GitHub Desktop.
Validate NextJS url by reading the Page API directory
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");
});
});
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