Skip to content

Commit 40bedf0

Browse files
committed
fix(nuxt): absolutely resolve defu in app config template
Ported from a006fd4. The `nitro/runtime-config` half does not apply: 3.x uses `#internal/nitro` (a nitropack v2 virtual) for `useRuntimeConfig`.
1 parent 77187ee commit 40bedf0

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

packages/nuxt/src/core/templates.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { join, relative, resolve } from 'pathe'
44
import type { JSValue } from 'untyped'
55
import { generateTypes, resolveSchema } from 'untyped'
66
import escapeRE from 'escape-string-regexp'
7+
import { resolveModulePath } from 'exsolve'
78
import { hash } from 'ohash'
89
import { camelCase } from 'scule'
910
import { filename, reverseResolveAlias } from 'pathe/utils'
@@ -13,6 +14,8 @@ import { annotatePlugins, checkForCircularDependencies } from './app.ts'
1314
import { EXTENSION_RE } from './utils/index.ts'
1415
import type { NuxtOptions, NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema'
1516

17+
const defuPath = resolveModulePath('defu', { try: true, from: import.meta.url }) ?? 'defu'
18+
1619
export const vueShim: NuxtTemplate = {
1720
filename: 'types/vue-shim.d.ts',
1821
getContents: ({ nuxt }) => {
@@ -429,7 +432,7 @@ export const appConfigTemplate: NuxtTemplate = {
429432
getContents ({ app, nuxt }) {
430433
return `
431434
import { _replaceAppConfig } from '#app/config'
432-
import { defuFn } from 'defu'
435+
import { defuFn } from ${JSON.stringify(defuPath)}
433436
434437
const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
435438
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { existsSync } from 'node:fs'
2+
import { describe, expect, it } from 'vitest'
3+
import { resolve } from 'pathe'
4+
5+
// `templates.ts` re-imports `./app.ts`, which reads `defaultTemplates.*` at
6+
// module init. Pulling `app.ts` in first lets that cycle resolve the same way
7+
// it does in production; otherwise the test would see partially-initialised
8+
// exports and crash before any assertions run.
9+
import '../src/core/app.ts'
10+
import { appConfigTemplate } from '../src/core/templates.ts'
11+
12+
import type { Nuxt, NuxtApp } from 'nuxt/schema'
13+
14+
function makeNuxt (overrides: Partial<Nuxt['options']> = {}): Nuxt {
15+
return {
16+
options: {
17+
dev: false,
18+
appConfig: {},
19+
app: { baseURL: '/', buildAssetsDir: '/_nuxt/', cdnURL: '' },
20+
...overrides,
21+
},
22+
} as unknown as Nuxt
23+
}
24+
25+
function makeApp (configs: string[] = []): NuxtApp {
26+
return { configs } as unknown as NuxtApp
27+
}
28+
29+
describe('appConfigTemplate', () => {
30+
it('emits an absolute path for the `defu` import so Nitro can resolve it under strict pnpm hoist', async () => {
31+
const contents = await appConfigTemplate.getContents!({ nuxt: makeNuxt(), app: makeApp(), options: {} })
32+
33+
expect(contents).not.toMatch(/from ['"]defu['"]/)
34+
const match = contents.match(/import \{ defuFn \} from ["']([^"']+)["']/)
35+
expect(match, 'expected resolved `defuFn` import').toBeTruthy()
36+
const resolved = match![1]!
37+
expect(resolve(resolved)).toBe(resolved)
38+
expect(existsSync(resolved)).toBe(true)
39+
})
40+
})

0 commit comments

Comments
 (0)