Skip to content

Commit 56b3d30

Browse files
committed
chore: progress
1 parent 22b312a commit 56b3d30

4 files changed

Lines changed: 46 additions & 8 deletions

File tree

docs/content/docs/1.guides/2.bundling.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,18 +244,31 @@ $script.add({
244244
})
245245
```
246246

247-
### Change Asset Behavior
247+
### Asset Configuration
248248

249-
Use the `assets` option in your configuration to customize how scripts are bundled, such as changing the output directory for the bundled scripts.
249+
Use the `assets` option in your configuration to customize how scripts are bundled and cached.
250250

251251
```ts [nuxt.config.ts]
252252
export default defineNuxtConfig({
253253
scripts: {
254254
assets: {
255255
prefix: '/_custom-script-path/',
256+
cacheMaxAge: 86400000, // 1 day in milliseconds
256257
}
257258
}
258259
})
259260
```
260261

261-
More configuration options will be available in future updates.
262+
#### Available Options
263+
264+
- **`prefix`** - Custom path where bundled scripts are served (default: `/_scripts/`)
265+
- **`cacheMaxAge`** - Cache duration for bundled scripts in milliseconds (default: 7 days)
266+
267+
#### Cache Behavior
268+
269+
The bundling system uses two different cache strategies:
270+
271+
- **Build-time cache**: Controlled by `cacheMaxAge` (default: 7 days). Scripts older than this are re-downloaded during builds to ensure freshness.
272+
- **Runtime cache**: Bundled scripts are served with 1-year cache headers since they are content-addressed by hash.
273+
274+
This dual approach ensures both build performance and reliable browser caching.

src/module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ export interface ModuleOptions {
6262
* Configure the fetch options used for downloading scripts.
6363
*/
6464
fetchOptions?: FetchOptions
65+
/**
66+
* Cache duration for bundled scripts in milliseconds.
67+
* Scripts older than this will be re-downloaded during builds.
68+
* @default 604800000 (7 days)
69+
*/
70+
cacheMaxAge?: number
6571
}
6672
/**
6773
* Whether the module is enabled.
@@ -234,6 +240,7 @@ export {}`
234240
assetsBaseURL: config.assets?.prefix,
235241
fallbackOnSrcOnBundleFail: config.assets?.fallbackOnSrcOnBundleFail,
236242
fetchOptions: config.assets?.fetchOptions,
243+
cacheMaxAge: config.assets?.cacheMaxAge,
237244
renderedScript,
238245
}))
239246

src/plugins/transform.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ import type { RegistryScript } from '#nuxt-scripts/types'
2020

2121
const SEVEN_DAYS_IN_MS = 7 * 24 * 60 * 60 * 1000
2222

23-
async function isCacheExpired(storage: any, filename: string): Promise<boolean> {
23+
async function isCacheExpired(storage: any, filename: string, cacheMaxAge: number = SEVEN_DAYS_IN_MS): Promise<boolean> {
2424
const metaKey = `bundle-meta:${filename}`
2525
const meta = await storage.getItem(metaKey)
2626
if (!meta || !meta.timestamp) {
2727
return true // No metadata means expired/invalid cache
2828
}
29-
return Date.now() - meta.timestamp > SEVEN_DAYS_IN_MS
29+
return Date.now() - meta.timestamp > cacheMaxAge
3030
}
3131

3232
export interface AssetBundlerTransformerOptions {
@@ -36,6 +36,7 @@ export interface AssetBundlerTransformerOptions {
3636
scripts?: Required<RegistryScript>[]
3737
fallbackOnSrcOnBundleFail?: boolean
3838
fetchOptions?: FetchOptions
39+
cacheMaxAge?: number
3940
renderedScript?: Map<string, {
4041
content: Buffer
4142
/**
@@ -68,7 +69,7 @@ async function downloadScript(opts: {
6869
url: string
6970
filename?: string
7071
forceDownload?: boolean
71-
}, renderedScript: NonNullable<AssetBundlerTransformerOptions['renderedScript']>, fetchOptions?: FetchOptions) {
72+
}, renderedScript: NonNullable<AssetBundlerTransformerOptions['renderedScript']>, fetchOptions?: FetchOptions, cacheMaxAge?: number) {
7273
const { src, url, filename, forceDownload } = opts
7374
if (src === url || !filename) {
7475
return
@@ -79,7 +80,7 @@ async function downloadScript(opts: {
7980
if (!res) {
8081
// Use storage to cache the font data between builds
8182
const cacheKey = `bundle:${filename}`
82-
const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !(await isCacheExpired(storage, filename))
83+
const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !(await isCacheExpired(storage, filename, cacheMaxAge))
8384

8485
if (shouldUseCache) {
8586
const res = await storage.getItemRaw<Buffer>(cacheKey)
@@ -312,7 +313,7 @@ export function NuxtScriptBundleTransformer(options: AssetBundlerTransformerOpti
312313
const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL)
313314
let url = _url
314315
try {
315-
await downloadScript({ src, url, filename, forceDownload }, renderedScript, options.fetchOptions)
316+
await downloadScript({ src, url, filename, forceDownload }, renderedScript, options.fetchOptions, options.cacheMaxAge)
316317
}
317318
catch (e) {
318319
if (options.fallbackOnSrcOnBundleFail) {

test/unit/transform.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,23 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({
497497
`)
498498
})
499499

500+
it('custom cache max age is passed through', async () => {
501+
vi.mocked(hash).mockImplementationOnce(() => 'beacon.min')
502+
const customCacheMaxAge = 3600000 // 1 hour
503+
504+
const code = await transform(
505+
`const instance = useScript('https://static.cloudflareinsights.com/beacon.min.js', {
506+
bundle: true,
507+
})`,
508+
{
509+
cacheMaxAge: customCacheMaxAge,
510+
},
511+
)
512+
513+
// Verify transformation still works with custom cache duration
514+
expect(code).toMatchInlineSnapshot(`"const instance = useScript('/_scripts/beacon.min.js', )"`)
515+
})
516+
500517
describe.todo('fallbackOnSrcOnBundleFail', () => {
501518
beforeEach(() => {
502519
vi.mocked($fetch).mockImplementationOnce(() => Promise.reject(new Error('fetch error')))

0 commit comments

Comments
 (0)