From aae0ba76852d50ad500208196d701dc0f2b87228 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 28 Mar 2026 10:36:24 +0000 Subject: [PATCH] refactor: consolidate fileURLToPath path resolution into resolveEntryPath() Replace the repeated `fileURLToPath(new URL(rel, import.meta.url)).replace(/\\/g, "/")` idiom across entry generators with a single shared utility. Add `resolveEntryPath(rel, base)` to runtime-entry-module.ts, then use it in app-rsc-entry.ts (16 call sites), pages-server-entry.ts (8 call sites), and have resolveRuntimeEntryModule delegate to it. Replace `const __dirname = path.dirname(fileURLToPath(import.meta.url))` in index.ts and tpr.ts with `import.meta.dirname` (Node 22+, already used throughout the test suite). --- packages/vinext/src/cloudflare/tpr.ts | 4 +- packages/vinext/src/entries/app-rsc-entry.ts | 82 ++++++++----------- .../vinext/src/entries/pages-server-entry.ts | 42 +++------- .../src/entries/runtime-entry-module.ts | 21 ++++- packages/vinext/src/index.ts | 4 +- 5 files changed, 68 insertions(+), 85 deletions(-) diff --git a/packages/vinext/src/cloudflare/tpr.ts b/packages/vinext/src/cloudflare/tpr.ts index 5dcfc1300..019fccd1b 100644 --- a/packages/vinext/src/cloudflare/tpr.ts +++ b/packages/vinext/src/cloudflare/tpr.ts @@ -22,7 +22,6 @@ import fs from "node:fs"; import path from "node:path"; -import { fileURLToPath } from "node:url"; import { spawn, type ChildProcess } from "node:child_process"; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -608,8 +607,7 @@ async function prerenderRoutes( * to the current module (works whether vinext is installed or linked). */ function startLocalServer(root: string, port: number): ChildProcess { - const thisDir = fileURLToPath(new URL(".", import.meta.url)); - const prodServerPath = path.resolve(thisDir, "..", "server", "prod-server.js"); + const prodServerPath = path.resolve(import.meta.dirname, "..", "server", "prod-server.js"); const outDir = path.join(root, "dist"); // Escape backslashes for Windows paths inside the JS string diff --git a/packages/vinext/src/entries/app-rsc-entry.ts b/packages/vinext/src/entries/app-rsc-entry.ts index c5a39bcb5..e24c79607 100644 --- a/packages/vinext/src/entries/app-rsc-entry.ts +++ b/packages/vinext/src/entries/app-rsc-entry.ts @@ -8,7 +8,7 @@ * Previously housed in server/app-dev-server.ts. */ import fs from "node:fs"; -import { fileURLToPath } from "node:url"; +import { resolveEntryPath } from "./runtime-entry-module.js"; import type { NextHeader, NextI18nConfig, @@ -29,52 +29,40 @@ import { isProxyFile } from "../server/middleware.js"; // Pre-computed absolute paths for generated-code imports. The virtual RSC // entry can't use relative imports (it has no real file location), so we // resolve these at code-generation time and embed them as absolute paths. -const configMatchersPath = fileURLToPath( - new URL("../config/config-matchers.js", import.meta.url), -).replace(/\\/g, "/"); -const requestPipelinePath = fileURLToPath( - new URL("../server/request-pipeline.js", import.meta.url), -).replace(/\\/g, "/"); -const requestContextShimPath = fileURLToPath( - new URL("../shims/request-context.js", import.meta.url), -).replace(/\\/g, "/"); -const normalizePathModulePath = fileURLToPath( - new URL("../server/normalize-path.js", import.meta.url), -).replace(/\\/g, "/"); -const appRouteHandlerRuntimePath = fileURLToPath( - new URL("../server/app-route-handler-runtime.js", import.meta.url), -).replace(/\\/g, "/"); -const appRouteHandlerPolicyPath = fileURLToPath( - new URL("../server/app-route-handler-policy.js", import.meta.url), -).replace(/\\/g, "/"); -const appRouteHandlerExecutionPath = fileURLToPath( - new URL("../server/app-route-handler-execution.js", import.meta.url), -).replace(/\\/g, "/"); -const appRouteHandlerCachePath = fileURLToPath( - new URL("../server/app-route-handler-cache.js", import.meta.url), -).replace(/\\/g, "/"); -const appPageCachePath = fileURLToPath( - new URL("../server/app-page-cache.js", import.meta.url), -).replace(/\\/g, "/"); -const appPageExecutionPath = fileURLToPath( - new URL("../server/app-page-execution.js", import.meta.url), -).replace(/\\/g, "/"); -const appPageBoundaryRenderPath = fileURLToPath( - new URL("../server/app-page-boundary-render.js", import.meta.url), -).replace(/\\/g, "/"); -const appPageRenderPath = fileURLToPath( - new URL("../server/app-page-render.js", import.meta.url), -).replace(/\\/g, "/"); -const appPageRequestPath = fileURLToPath( - new URL("../server/app-page-request.js", import.meta.url), -).replace(/\\/g, "/"); -const appRouteHandlerResponsePath = fileURLToPath( - new URL("../server/app-route-handler-response.js", import.meta.url), -).replace(/\\/g, "/"); -const routeTriePath = fileURLToPath(new URL("../routing/route-trie.js", import.meta.url)).replace( - /\\/g, - "/", +const configMatchersPath = resolveEntryPath("../config/config-matchers.js", import.meta.url); +const requestPipelinePath = resolveEntryPath("../server/request-pipeline.js", import.meta.url); +const requestContextShimPath = resolveEntryPath("../shims/request-context.js", import.meta.url); +const normalizePathModulePath = resolveEntryPath("../server/normalize-path.js", import.meta.url); +const appRouteHandlerRuntimePath = resolveEntryPath( + "../server/app-route-handler-runtime.js", + import.meta.url, ); +const appRouteHandlerPolicyPath = resolveEntryPath( + "../server/app-route-handler-policy.js", + import.meta.url, +); +const appRouteHandlerExecutionPath = resolveEntryPath( + "../server/app-route-handler-execution.js", + import.meta.url, +); +const appRouteHandlerCachePath = resolveEntryPath( + "../server/app-route-handler-cache.js", + import.meta.url, +); +const appPageCachePath = resolveEntryPath("../server/app-page-cache.js", import.meta.url); +const appPageExecutionPath = resolveEntryPath("../server/app-page-execution.js", import.meta.url); +const appPageBoundaryRenderPath = resolveEntryPath( + "../server/app-page-boundary-render.js", + import.meta.url, +); +const appPageRenderPath = resolveEntryPath("../server/app-page-render.js", import.meta.url); +const appPageRequestPath = resolveEntryPath("../server/app-page-request.js", import.meta.url); +const appRouteHandlerResponsePath = resolveEntryPath( + "../server/app-route-handler-response.js", + import.meta.url, +); +const routeTriePath = resolveEntryPath("../routing/route-trie.js", import.meta.url); +const metadataRoutesPath = resolveEntryPath("../server/metadata-routes.js", import.meta.url); /** * Resolved config options relevant to App Router request handling. @@ -358,7 +346,7 @@ import { LayoutSegmentProvider } from "vinext/layout-segment-context"; import { MetadataHead, mergeMetadata, resolveModuleMetadata, ViewportHead, mergeViewport, resolveModuleViewport } from "vinext/metadata"; ${middlewarePath ? `import * as middlewareModule from ${JSON.stringify(middlewarePath.replace(/\\/g, "/"))};` : ""} ${instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(instrumentationPath.replace(/\\/g, "/"))};` : ""} -${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(fileURLToPath(new URL("../server/metadata-routes.js", import.meta.url)).replace(/\\/g, "/"))};` : ""} +${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(metadataRoutesPath)};` : ""} import { requestContextFromRequest, normalizeHost, matchRedirect, matchRewrite, matchHeaders, isExternalUrl, proxyExternalRequest, sanitizeDestination } from ${JSON.stringify(configMatchersPath)}; import { decodePathParams as __decodePathParams } from ${JSON.stringify(normalizePathModulePath)}; import { validateCsrfOrigin, validateImageUrl, guardProtocolRelativeUrl, hasBasePath, stripBasePath, normalizeTrailingSlash, processMiddlewareHeaders } from ${JSON.stringify(requestPipelinePath)}; diff --git a/packages/vinext/src/entries/pages-server-entry.ts b/packages/vinext/src/entries/pages-server-entry.ts index 651086167..825da4bb6 100644 --- a/packages/vinext/src/entries/pages-server-entry.ts +++ b/packages/vinext/src/entries/pages-server-entry.ts @@ -7,8 +7,7 @@ * * Extracted from index.ts. */ -import path from "node:path"; -import { fileURLToPath } from "node:url"; +import { resolveEntryPath } from "./runtime-entry-module.js"; import { pagesRouter, apiRouter, type Route } from "../routing/pages-router.js"; import { createValidFileMatcher } from "../routing/file-matcher.js"; import { type ResolvedNextConfig } from "../config/next-config.js"; @@ -21,34 +20,17 @@ import { } from "../server/middleware-codegen.js"; import { findFileWithExts } from "./pages-entry-helpers.js"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const _requestContextShimPath = fileURLToPath( - new URL("../shims/request-context.js", import.meta.url), -).replace(/\\/g, "/"); -const _routeTriePath = fileURLToPath(new URL("../routing/route-trie.js", import.meta.url)).replace( - /\\/g, - "/", -); -const _pagesI18nPath = fileURLToPath(new URL("../server/pages-i18n.js", import.meta.url)).replace( - /\\/g, - "/", -); -const _pagesPageResponsePath = fileURLToPath( - new URL("../server/pages-page-response.js", import.meta.url), -).replace(/\\/g, "/"); -const _pagesPageDataPath = fileURLToPath( - new URL("../server/pages-page-data.js", import.meta.url), -).replace(/\\/g, "/"); -const _pagesNodeCompatPath = fileURLToPath( - new URL("../server/pages-node-compat.js", import.meta.url), -).replace(/\\/g, "/"); -const _pagesApiRoutePath = fileURLToPath( - new URL("../server/pages-api-route.js", import.meta.url), -).replace(/\\/g, "/"); -const _isrCachePath = fileURLToPath(new URL("../server/isr-cache.js", import.meta.url)).replace( - /\\/g, - "/", +const _requestContextShimPath = resolveEntryPath("../shims/request-context.js", import.meta.url); +const _routeTriePath = resolveEntryPath("../routing/route-trie.js", import.meta.url); +const _pagesI18nPath = resolveEntryPath("../server/pages-i18n.js", import.meta.url); +const _pagesPageResponsePath = resolveEntryPath( + "../server/pages-page-response.js", + import.meta.url, ); +const _pagesPageDataPath = resolveEntryPath("../server/pages-page-data.js", import.meta.url); +const _pagesNodeCompatPath = resolveEntryPath("../server/pages-node-compat.js", import.meta.url); +const _pagesApiRoutePath = resolveEntryPath("../server/pages-api-route.js", import.meta.url); +const _isrCachePath = resolveEntryPath("../server/isr-cache.js", import.meta.url); /** * Generate the virtual SSR server entry module. @@ -291,7 +273,7 @@ import { setI18nContext } from "vinext/i18n-context"; import { safeJsonStringify } from "vinext/html"; import { getSSRFontLinks as _getSSRFontLinks, getSSRFontStyles as _getSSRFontStylesGoogle, getSSRFontPreloads as _getSSRFontPreloadsGoogle } from "next/font/google"; import { getSSRFontStyles as _getSSRFontStylesLocal, getSSRFontPreloads as _getSSRFontPreloadsLocal } from "next/font/local"; -import { sanitizeDestination as sanitizeDestinationLocal } from ${JSON.stringify(path.resolve(__dirname, "../config/config-matchers.js").replace(/\\/g, "/"))}; +import { sanitizeDestination as sanitizeDestinationLocal } from ${JSON.stringify(resolveEntryPath("../config/config-matchers.js", import.meta.url))}; import { runWithExecutionContext as _runWithExecutionContext, getRequestExecutionContext as _getRequestExecutionContext } from ${JSON.stringify(_requestContextShimPath)}; import { buildRouteTrie as _buildRouteTrie, trieMatch as _trieMatch } from ${JSON.stringify(_routeTriePath)}; import { reportRequestError as _reportRequestError } from "vinext/instrumentation"; diff --git a/packages/vinext/src/entries/runtime-entry-module.ts b/packages/vinext/src/entries/runtime-entry-module.ts index f8603fab3..a84bf6f36 100644 --- a/packages/vinext/src/entries/runtime-entry-module.ts +++ b/packages/vinext/src/entries/runtime-entry-module.ts @@ -1,6 +1,21 @@ import fs from "node:fs"; import { fileURLToPath } from "node:url"; +/** + * Resolve a sibling module path relative to a caller's `import.meta.url`, + * returning a forward-slash path safe for embedding in generated code. + * + * This is the single place that owns the + * `fileURLToPath(new URL(rel, base)).replace(/\\/g, "/")` idiom so callers + * don't duplicate it. + * + * @param rel - Relative path to the target module (e.g. `"../server/foo.js"`) + * @param base - The caller's `import.meta.url` + */ +export function resolveEntryPath(rel: string, base: string): string { + return fileURLToPath(new URL(rel, base)).replace(/\\/g, "/"); +} + /** * Resolve a real runtime module for a virtual entry generator. * @@ -11,11 +26,11 @@ import { fileURLToPath } from "node:url"; */ export function resolveRuntimeEntryModule(name: string): string { for (const ext of [".ts", ".js", ".mts", ".mjs"]) { - const filePath = fileURLToPath(new URL(`../server/${name}${ext}`, import.meta.url)); + const filePath = resolveEntryPath(`../server/${name}${ext}`, import.meta.url); if (fs.existsSync(filePath)) { - return filePath.replace(/\\/g, "/"); + return filePath; } } - return fileURLToPath(new URL(`../server/${name}.js`, import.meta.url)).replace(/\\/g, "/"); + return resolveEntryPath(`../server/${name}.js`, import.meta.url); } diff --git a/packages/vinext/src/index.ts b/packages/vinext/src/index.ts index 781f51f17..7ac1db6a4 100644 --- a/packages/vinext/src/index.ts +++ b/packages/vinext/src/index.ts @@ -63,13 +63,13 @@ import tsconfigPaths from "vite-tsconfig-paths"; import type { Options as VitePluginReactOptions } from "@vitejs/plugin-react"; import MagicString from "magic-string"; import path from "node:path"; -import { fileURLToPath, pathToFileURL } from "node:url"; +import { pathToFileURL } from "node:url"; import { createRequire } from "node:module"; import fs from "node:fs"; import { randomBytes } from "node:crypto"; import commonjs from "vite-plugin-commonjs"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const __dirname = import.meta.dirname; type VitePluginReactModule = typeof import("@vitejs/plugin-react"); function resolveOptionalDependency(projectRoot: string, specifier: string): string | null {