Fix custom cache handler importing on windows (#60312)
### Fixing a bug ### What? Custom cache handler doesn't work on Windows ### Why? It broke in a recent fix, when adding ESM support - #59863. The problem is not new - dynamic imports consider an absolute path in Windows as a protocol: `ERR! Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'C:'` ### How? As a solution, it is necessary to explicitly indicate that it is indeed an absolute path, for example by adding a / at the beginning, but the most reliable way is to use pathToFileURL. Since the logic is repeated in 4 places - I created a common function. Fixes #58509 --------- Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
parent
46370d816b
commit
51bda321f0
5 changed files with 35 additions and 21 deletions
|
@ -163,6 +163,7 @@ import { getStartServerInfo, logStartInfo } from '../server/lib/app-info-log'
|
|||
import type { NextEnabledDirectories } from '../server/base-server'
|
||||
import { hasCustomExportOutput } from '../export/utils'
|
||||
import { interopDefault } from '../lib/interop-default'
|
||||
import { formatDynamicImportPath } from '../lib/format-dynamic-import-path'
|
||||
|
||||
interface ExperimentalBypassForInfo {
|
||||
experimentalBypassFor?: RouteHas[]
|
||||
|
@ -1537,10 +1538,8 @@ export default async function build(
|
|||
if (incrementalCacheHandlerPath) {
|
||||
CacheHandler = interopDefault(
|
||||
await import(
|
||||
path.isAbsolute(incrementalCacheHandlerPath)
|
||||
? incrementalCacheHandlerPath
|
||||
: path.join(dir, incrementalCacheHandlerPath)
|
||||
)
|
||||
formatDynamicImportPath(dir, incrementalCacheHandlerPath)
|
||||
).then((mod) => mod.default || mod)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ import { RouteKind } from '../server/future/route-kind'
|
|||
import { isAppRouteRouteModule } from '../server/future/route-modules/checks'
|
||||
import { interopDefault } from '../lib/interop-default'
|
||||
import type { PageExtensions } from './page-extensions-type'
|
||||
import { formatDynamicImportPath } from '../lib/format-dynamic-import-path'
|
||||
|
||||
export type ROUTER_TYPE = 'pages' | 'app'
|
||||
|
||||
|
@ -1314,10 +1315,8 @@ export async function buildAppStaticPaths({
|
|||
if (incrementalCacheHandlerPath) {
|
||||
CacheHandler = interopDefault(
|
||||
await import(
|
||||
path.isAbsolute(incrementalCacheHandlerPath)
|
||||
? incrementalCacheHandlerPath
|
||||
: path.join(dir, incrementalCacheHandlerPath)
|
||||
)
|
||||
formatDynamicImportPath(dir, incrementalCacheHandlerPath)
|
||||
).then((mod) => mod.default || mod)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import { IncrementalCache } from '../../server/lib/incremental-cache'
|
|||
import { hasNextSupport } from '../../telemetry/ci-info'
|
||||
import { nodeFs } from '../../server/lib/node-fs-methods'
|
||||
import { interopDefault } from '../../lib/interop-default'
|
||||
import { formatDynamicImportPath } from '../../lib/format-dynamic-import-path'
|
||||
|
||||
export async function createIncrementalCache({
|
||||
incrementalCacheHandlerPath,
|
||||
|
@ -29,13 +30,9 @@ export async function createIncrementalCache({
|
|||
let CacheHandler: any
|
||||
if (incrementalCacheHandlerPath) {
|
||||
CacheHandler = interopDefault(
|
||||
(
|
||||
await import(
|
||||
path.isAbsolute(incrementalCacheHandlerPath)
|
||||
? incrementalCacheHandlerPath
|
||||
: path.join(dir, incrementalCacheHandlerPath)
|
||||
)
|
||||
).default
|
||||
await import(
|
||||
formatDynamicImportPath(dir, incrementalCacheHandlerPath)
|
||||
).then((mod) => mod.default || mod)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
19
packages/next/src/lib/format-dynamic-import-path.ts
Normal file
19
packages/next/src/lib/format-dynamic-import-path.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import path from 'path'
|
||||
import { pathToFileURL } from 'url'
|
||||
|
||||
/**
|
||||
* The path for a dynamic route must be URLs with a valid scheme.
|
||||
*
|
||||
* When an absolute Windows path is passed to it, it interprets the beginning of the path as a protocol (`C:`).
|
||||
* Therefore, it is important to always construct a complete path.
|
||||
* @param dir File directory
|
||||
* @param filePath Absolute or relative path
|
||||
*/
|
||||
export const formatDynamicImportPath = (dir: string, filePath: string) => {
|
||||
const absoluteFilePath = path.isAbsolute(filePath)
|
||||
? filePath
|
||||
: path.join(dir, filePath)
|
||||
const formattedFilePath = pathToFileURL(absoluteFilePath).toString()
|
||||
|
||||
return formattedFilePath
|
||||
}
|
|
@ -26,7 +26,7 @@ import type { ParsedUrl } from '../shared/lib/router/utils/parse-url'
|
|||
import type { Revalidate } from './lib/revalidate'
|
||||
|
||||
import fs from 'fs'
|
||||
import { join, resolve, isAbsolute } from 'path'
|
||||
import { join, resolve } from 'path'
|
||||
import { getRouteMatcher } from '../shared/lib/router/utils/route-matcher'
|
||||
import { addRequestMeta, getRequestMeta } from './request-meta'
|
||||
import {
|
||||
|
@ -101,6 +101,7 @@ import { loadManifest } from './load-manifest'
|
|||
import { lazyRenderAppPage } from './future/route-modules/app-page/module.render'
|
||||
import { lazyRenderPagesPage } from './future/route-modules/pages/module.render'
|
||||
import { interopDefault } from '../lib/interop-default'
|
||||
import { formatDynamicImportPath } from '../lib/format-dynamic-import-path'
|
||||
|
||||
export * from './base-server'
|
||||
|
||||
|
@ -108,8 +109,9 @@ declare const __non_webpack_require__: NodeRequire
|
|||
|
||||
// For module that can be both CJS or ESM
|
||||
const dynamicImportEsmDefault = process.env.NEXT_MINIMAL
|
||||
? __non_webpack_require__
|
||||
: async (mod: string) => (await import(mod)).default
|
||||
? (id: string) =>
|
||||
import(/* webpackIgnore: true */ id).then((mod) => mod.default || mod)
|
||||
: (id: string) => import(id).then((mod) => mod.default || mod)
|
||||
|
||||
// For module that will be compiled to CJS, e.g. instrument
|
||||
const dynamicRequire = process.env.NEXT_MINIMAL
|
||||
|
@ -310,9 +312,7 @@ export default class NextNodeServer extends BaseServer {
|
|||
if (incrementalCacheHandlerPath) {
|
||||
CacheHandler = interopDefault(
|
||||
await dynamicImportEsmDefault(
|
||||
isAbsolute(incrementalCacheHandlerPath)
|
||||
? incrementalCacheHandlerPath
|
||||
: join(this.distDir, incrementalCacheHandlerPath)
|
||||
formatDynamicImportPath(this.distDir, incrementalCacheHandlerPath)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue