Skip to content
Open
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
15 changes: 13 additions & 2 deletions packages/script/src/runtime/server/utils/cache-config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { pathToFileURL } from 'node:url'

/** Dedicated Nitro storage mount used by Nuxt Scripts' upstream caches. */
export const NUXT_SCRIPTS_CACHE_BASE = 'nuxt-scripts-cache'

Expand All @@ -11,6 +13,7 @@ export const NUXT_SCRIPTS_CACHE_MAX_ENTRY_SIZE = 8 * 1024 * 1024

interface NitroOptionsWithStorage {
storage?: Record<string, unknown>
alias?: Record<string, string>
kv?: Record<string, unknown>
}

Expand All @@ -25,8 +28,16 @@ export function ensureNuxtScriptsCacheStorage(nitroOptions: NitroOptionsWithStor
const mounts = nitroVersion >= 3
? (nitroOptions.kv ||= {})
: (nitroOptions.storage ||= {})
mounts[NUXT_SCRIPTS_CACHE_BASE] ||= {
driver,
if (mounts[NUXT_SCRIPTS_CACHE_BASE])
return

// Nitro imports custom drivers with the native ESM loader during setup.
const driverURL = pathToFileURL(driver).href
// Rollup needs a local path to bundle the driver into the server output.
nitroOptions.alias ||= {}
nitroOptions.alias[driverURL] = driver
mounts[NUXT_SCRIPTS_CACHE_BASE] = {
driver: driverURL,
max: NUXT_SCRIPTS_CACHE_MAX_ENTRIES,
maxSize: NUXT_SCRIPTS_CACHE_MAX_SIZE,
maxEntrySize: NUXT_SCRIPTS_CACHE_MAX_ENTRY_SIZE,
Expand Down
52 changes: 50 additions & 2 deletions test/unit/cached-upstream-lifecycle.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { execFileSync } from 'node:child_process'
import { mkdtemp, rm, symlink } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { resolveAlias } from '@nuxt/kit'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
ensureNuxtScriptsCacheStorage,
Expand All @@ -23,19 +29,61 @@ describe('upstream cache storage ownership', () => {
ensureNuxtScriptsCacheStorage(nitroOptions, '/nuxt-scripts/lru-cache-driver')

expect(nitroOptions.storage?.[NUXT_SCRIPTS_CACHE_BASE]).toEqual(expect.objectContaining({
driver: '/nuxt-scripts/lru-cache-driver',
driver: pathToFileURL('/nuxt-scripts/lru-cache-driver').href,
max: NUXT_SCRIPTS_CACHE_MAX_ENTRIES,
}))
})

it.each([2, 3])('loads the cache driver through the native ESM loader on Nitro v%i', async (nitroVersion) => {
const root = await mkdtemp(join(tmpdir(), 'nuxt-scripts-cache-'))
try {
const driverDir = join(root, 'space # percent % ü')
const sourceDir = fileURLToPath(new URL('../../packages/script/src/runtime/server/utils/', import.meta.url))
await symlink(sourceDir, driverDir, 'junction')
const nitroOptions: { storage?: Record<string, { driver: string }>, kv?: Record<string, { driver: string }> } = {}
ensureNuxtScriptsCacheStorage(nitroOptions, join(driverDir, 'lru-cache-driver.ts'), nitroVersion)
const mounts = nitroVersion >= 3 ? nitroOptions.kv! : nitroOptions.storage!

// Nitro imports custom storage drivers directly, outside the bundler.
expect(() => execFileSync(process.execPath, ['--input-type=module-typescript', '-e', `
import assert from 'node:assert/strict'
const { default: createDriver } = await import(process.argv[1])
const driver = createDriver({ max: 2 })
driver.setItem('key', 'value')
assert.equal(driver.getItem('key'), 'value')
`, mounts[NUXT_SCRIPTS_CACHE_BASE]!.driver], { stdio: 'pipe' })).not.toThrow()
}
finally {
await rm(root, { recursive: true, force: true })
}
})

it('preserves an application cache mount', () => {
const mount = { driver: 'redis', url: 'redis://localhost:6379' }
const nitroOptions = { storage: { [NUXT_SCRIPTS_CACHE_BASE]: mount } }

ensureNuxtScriptsCacheStorage(nitroOptions, '/nuxt-scripts/lru-cache-driver')

expect(nitroOptions.storage[NUXT_SCRIPTS_CACHE_BASE]).toBe(mount)
})

it.each([2, 3])('resolves the driver URL to a local file for server bundling on Nitro v%i', (nitroVersion) => {
const driver = fileURLToPath(new URL('../../packages/script/src/runtime/server/utils/lru-cache-driver.ts', import.meta.url))
const nitroOptions: { storage?: Record<string, unknown>, alias?: Record<string, string> } = {}

ensureNuxtScriptsCacheStorage(nitroOptions, driver, nitroVersion)

expect(resolveAlias(pathToFileURL(driver).href, nitroOptions.alias)).toBe(driver)
})

it('installs the cache mount under `kv` on Nitro v3', () => {
const nitroOptions: { storage?: Record<string, unknown>, kv?: Record<string, unknown> } = {}

ensureNuxtScriptsCacheStorage(nitroOptions, '/nuxt-scripts/lru-cache-driver', 3)

expect(nitroOptions.storage).toBeUndefined()
expect(nitroOptions.kv?.[NUXT_SCRIPTS_CACHE_BASE]).toEqual(expect.objectContaining({
driver: '/nuxt-scripts/lru-cache-driver',
driver: pathToFileURL('/nuxt-scripts/lru-cache-driver').href,
max: NUXT_SCRIPTS_CACHE_MAX_ENTRIES,
}))
})
Expand Down
Loading