diff --git a/src/app.ts b/src/app.ts index 1f67789b460..0594cb8b8b2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,12 +7,7 @@ import { DENO_DEPLOYMENT_ID } from "./runtime/build_id.ts"; import * as colors from "@std/fmt/colors"; import { type MiddlewareFn, runMiddlewares } from "./middlewares/mod.ts"; import { FreshReqContext } from "./context.ts"; -import { - mergePaths, - type Method, - type Router, - UrlPatternRouter, -} from "./router.ts"; +import { type Method, type Router, UrlPatternRouter } from "./router.ts"; import { type FreshConfig, normalizeConfig, @@ -22,6 +17,7 @@ import { type BuildCache, ProdBuildCache } from "./build_cache.ts"; import type { ServerIslandRegistry } from "./context.ts"; import { FinishSetup, ForgotBuild } from "./finish_setup.tsx"; import { HttpError } from "./error.ts"; +import { mergePaths } from "./utils.ts"; // TODO: Completed type clashes in older Deno versions // deno-lint-ignore no-explicit-any diff --git a/src/router.ts b/src/router.ts index 34e85bcdbd7..e128bacfd80 100644 --- a/src/router.ts +++ b/src/router.ts @@ -28,17 +28,6 @@ export interface Router { export const IS_PATTERN = /[*:{}+?()]/; -export function mergePaths(a: string, b: string) { - if (a === "" || a === "/" || a === "/*") return b; - if (b === "/") return a; - if (a.endsWith("/")) { - return a.slice(0, -1) + b; - } else if (!b.startsWith("/")) { - return a + "/" + b; - } - return a + b; -} - export class UrlPatternRouter implements Router { readonly _routes: Route[] = []; readonly _middlewares: T[] = []; diff --git a/src/router_test.ts b/src/router_test.ts index 97addf3c0f7..f930b13e31a 100644 --- a/src/router_test.ts +++ b/src/router_test.ts @@ -1,10 +1,5 @@ import { expect } from "@std/expect"; -import { - IS_PATTERN, - mergePaths, - pathToPattern, - UrlPatternRouter, -} from "./router.ts"; +import { IS_PATTERN, pathToPattern, UrlPatternRouter } from "./router.ts"; Deno.test("IS_PATTERN", () => { expect(IS_PATTERN.test("/foo")).toEqual(false); @@ -176,12 +171,3 @@ Deno.test("pathToPattern", async (t) => { expect(() => pathToPattern("foo/[[name]]-bar")).toThrow(); }); }); - -Deno.test("mergePaths", () => { - expect(mergePaths("", "")).toEqual(""); - expect(mergePaths("/", "/foo")).toEqual("/foo"); - expect(mergePaths("/*", "/foo")).toEqual("/foo"); - expect(mergePaths("/foo/bar", "/baz")).toEqual("/foo/bar/baz"); - expect(mergePaths("/foo/bar/", "/baz")).toEqual("/foo/bar/baz"); - expect(mergePaths("/foo/bar", "baz")).toEqual("/foo/bar/baz"); -}); diff --git a/src/utils.ts b/src/utils.ts index 2c610c4832b..a780c9c606e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,3 +13,22 @@ export function assertInDir( throw new Error(`Path "${tmp}" resolved outside of "${dir}"`); } } + +/** + * Joins two path segments into a single normalized path. + * @example + * ```ts + * mergePaths("/api", "users"); // "/api/users" + * mergePaths("/api/", "/users"); // "/api/users" + * mergePaths("/", "/users"); // "/users" + * mergePaths("", "/users"); // "/users" + * mergePaths("/api", "/users"); // "/api/users" + * ``` + */ +export function mergePaths(a: string, b: string) { + if (a === "" || a === "/" || a === "/*") return b; + if (b === "/") return a; + if (a.endsWith("/")) return a.slice(0, -1) + b; + if (!b.startsWith("/")) return a + "/" + b; + return a + b; +} diff --git a/src/utils_test.ts b/src/utils_test.ts new file mode 100644 index 00000000000..e51b38eff48 --- /dev/null +++ b/src/utils_test.ts @@ -0,0 +1,11 @@ +import { expect } from "@std/expect"; +import { mergePaths } from "./utils.ts"; + +Deno.test("mergePaths", () => { + expect(mergePaths("", "")).toEqual(""); + expect(mergePaths("/", "/foo")).toEqual("/foo"); + expect(mergePaths("/*", "/foo")).toEqual("/foo"); + expect(mergePaths("/foo/bar", "/baz")).toEqual("/foo/bar/baz"); + expect(mergePaths("/foo/bar/", "/baz")).toEqual("/foo/bar/baz"); + expect(mergePaths("/foo/bar", "baz")).toEqual("/foo/bar/baz"); +});