Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using ESM pkg with custom incremental cache #59863

Merged
merged 11 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ import { formatManifest } from './manifests/formatter/format-manifest'
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'

interface ExperimentalBypassForInfo {
experimentalBypassFor?: RouteHas[]
Expand Down Expand Up @@ -1334,10 +1335,13 @@ export default async function build(
if (config.experimental.staticWorkerRequestDeduping) {
let CacheHandler
if (incrementalCacheHandlerPath) {
CacheHandler = require(path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath))
CacheHandler = CacheHandler.default || CacheHandler
CacheHandler = interopDefault(
await import(
path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath)
)
)
}

const cacheInitialization = await initializeIncrementalCache({
Expand Down
12 changes: 8 additions & 4 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { denormalizeAppPagePath } from '../shared/lib/page-path/denormalize-app-path'
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'

export type ROUTER_TYPE = 'pages' | 'app'
Expand Down Expand Up @@ -1308,10 +1309,13 @@ export async function buildAppStaticPaths({
let CacheHandler: any

if (incrementalCacheHandlerPath) {
CacheHandler = require(path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath))
CacheHandler = CacheHandler.default || CacheHandler
CacheHandler = interopDefault(
await import(
path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath)
)
)
}

const incrementalCache = new IncrementalCache({
Expand Down
16 changes: 11 additions & 5 deletions packages/next/src/export/helpers/create-incremental-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import path from 'path'
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'

export function createIncrementalCache({
export async function createIncrementalCache({
incrementalCacheHandlerPath,
isrMemoryCacheSize,
fetchCacheKeyPrefix,
Expand All @@ -27,10 +28,15 @@ export function createIncrementalCache({
// Custom cache handler overrides.
let CacheHandler: any
if (incrementalCacheHandlerPath) {
CacheHandler = require(path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath))
CacheHandler = CacheHandler.default || CacheHandler
CacheHandler = interopDefault(
(
await import(
path.isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: path.join(dir, incrementalCacheHandlerPath)
)
).default
)
}

const incrementalCache = new IncrementalCache({
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ async function exportPageImpl(
// cache instance for this page.
const incrementalCache =
isAppDir && fetchCache
? createIncrementalCache({
? await createIncrementalCache({
incrementalCacheHandlerPath,
isrMemoryCacheSize,
fetchCacheKeyPrefix,
Expand Down
8 changes: 4 additions & 4 deletions packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
protected abstract getIncrementalCache(options: {
requestHeaders: Record<string, undefined | string | string[]>
requestProtocol: 'http' | 'https'
}): import('./lib/incremental-cache').IncrementalCache
}): Promise<import('./lib/incremental-cache').IncrementalCache>

protected abstract getResponseCache(options: {
dev: boolean
Expand Down Expand Up @@ -1263,7 +1263,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
protocol = parsedFullUrl.protocol as 'https:' | 'http:'
} catch {}

const incrementalCache = this.getIncrementalCache({
const incrementalCache = await this.getIncrementalCache({
requestHeaders: Object.assign({}, req.headers),
requestProtocol: protocol.substring(0, protocol.length - 1) as
| 'http'
Expand Down Expand Up @@ -2127,12 +2127,12 @@ export default abstract class Server<ServerOptions extends Options = Options> {
// use existing incrementalCache instance if available
const incrementalCache =
(globalThis as any).__incrementalCache ||
this.getIncrementalCache({
(await this.getIncrementalCache({
requestHeaders: Object.assign({}, req.headers),
requestProtocol: protocol.substring(0, protocol.length - 1) as
| 'http'
| 'https',
})
}))

const { routeModule } = components

Expand Down
20 changes: 14 additions & 6 deletions packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,18 @@ import { RouteModuleLoader } from './future/helpers/module-loader/route-module-l
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'

export * from './base-server'

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

// For module that will be compiled to CJS, e.g. instrument
const dynamicRequire = process.env.NEXT_MINIMAL
? __non_webpack_require__
: require
Expand Down Expand Up @@ -289,7 +296,7 @@ export default class NextNodeServer extends BaseServer {
)
}

protected getIncrementalCache({
protected async getIncrementalCache({
requestHeaders,
requestProtocol,
}: {
Expand All @@ -301,12 +308,13 @@ export default class NextNodeServer extends BaseServer {
const { incrementalCacheHandlerPath } = this.nextConfig.experimental

if (incrementalCacheHandlerPath) {
CacheHandler = dynamicRequire(
isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: join(this.distDir, incrementalCacheHandlerPath)
CacheHandler = interopDefault(
await dynamicImportEsmDefault(
isAbsolute(incrementalCacheHandlerPath)
? incrementalCacheHandlerPath
: join(this.distDir, incrementalCacheHandlerPath)
)
)
CacheHandler = CacheHandler.default || CacheHandler
}

// incremental-cache is request specific
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/web-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
Object.assign(this.renderOpts, options.webServerConfig.extendRenderOpts)
}

protected getIncrementalCache({
protected async getIncrementalCache({
requestHeaders,
}: {
requestHeaders: IncrementalCache['requestHeaders']
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/app/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
12 changes: 12 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/app/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default async function Page() {
const data = await fetch(
'https://next-data-api-endpoint.vercel.app/api/random?page'
).then((res) => res.text())

return (
<>
<p id="page-data">{data}</p>
<p id="now">{Date.now()}</p>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Object.defineProperty(exports, '__esModule', { value: true })

const cache = new Map()

const CacheHandler = /** @class */ (function () {
function CacheHandler(options) {
this.options = options
this.cache = cache
console.log('initialized custom cache-handler')
console.log('cache handler - cjs default export')
}
CacheHandler.prototype.get = function (key) {
console.log('cache-handler get', key)
return Promise.resolve(this.cache.get(key))
}
CacheHandler.prototype.set = function (key, data) {
console.log('cache-handler set', key)
this.cache.set(key, {
value: data,
lastModified: Date.now(),
})
return Promise.resolve(undefined)
}
return CacheHandler
})()

exports.default = CacheHandler
27 changes: 27 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/cache-handler-esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const cache = new Map()

class CacheHandler {
constructor(options) {
this.options = options
this.cache = {}
console.log('initialized custom cache-handler')
console.log('cache handler - esm default export')
}

async get(key) {
console.log('key', key)
console.log('cache-handler get', key)
return cache.get(key)
}

async set(key, data) {
console.log('set key', key)
console.log('cache-handler set', key)
cache.set(key, {
value: data,
lastModified: Date.now(),
})
}
}

export default CacheHandler
27 changes: 27 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/cache-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const cache = new Map()

class CacheHandler {
constructor(options) {
this.options = options
this.cache = {}
console.log('initialized custom cache-handler')
console.log('cache handler - cjs module exports')
}

async get(key) {
console.log('key', key)
console.log('cache-handler get', key)
return cache.get(key)
}

async set(key, data) {
console.log('set key', key)
console.log('cache-handler set', key)
cache.set(key, {
value: data,
lastModified: Date.now(),
})
}
}

module.exports = CacheHandler
80 changes: 80 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { type NextInstance, createNextDescribe, FileRef } from 'e2e-utils'
import { check } from 'next-test-utils'
import fs from 'fs'

const originalNextConfig = fs.readFileSync(
__dirname + '/next.config.js',
'utf8'
)

function runTests(
exportType: string,
{ next, isNextDev }: { next: NextInstance; isNextDev: boolean }
) {
describe(exportType, () => {
it('should have logs from cache-handler', async () => {
if (isNextDev) {
await next.fetch('/')
}
await check(() => {
expect(next.cliOutput).toContain('cache handler - ' + exportType)
expect(next.cliOutput).toContain('initialized custom cache-handler')
expect(next.cliOutput).toContain('cache-handler get')
expect(next.cliOutput).toContain('cache-handler set')
return 'success'
}, 'success')
})
})
}

createNextDescribe(
'app-dir - custom-cache-handler - cjs',
{
files: __dirname,
skipDeployment: true,
env: {
CUSTOM_CACHE_HANDLER: 'cache-handler.js',
},
},
({ next, isNextDev }) => {
runTests('cjs module exports', { next, isNextDev })
}
)

createNextDescribe(
'app-dir - custom-cache-handler - cjs-default-export',
{
files: __dirname,
skipDeployment: true,
env: {
CUSTOM_CACHE_HANDLER: 'cache-handler-cjs-default-export.js',
},
},
({ next, isNextDev }) => {
runTests('cjs default export', { next, isNextDev })
}
)

createNextDescribe(
'app-dir - custom-cache-handler - esm',
{
files: {
app: new FileRef(__dirname + '/app'),
'cache-handler-esm.js': new FileRef(__dirname + '/cache-handler-esm.js'),
'next.config.js': originalNextConfig.replace(
'module.exports = ',
'export default '
),
},
skipDeployment: true,
packageJson: {
type: 'module',
},
env: {
CUSTOM_CACHE_HANDLER: 'cache-handler-esm.js',
},
},
({ next, isNextDev }) => {
runTests('esm default export', { next, isNextDev })
}
)
6 changes: 6 additions & 0 deletions test/e2e/app-dir/app-custom-cache-handler/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
experimental: {
incrementalCacheHandlerPath:
process.cwd() + '/' + process.env.CUSTOM_CACHE_HANDLER,
},
}
Loading