Skip to content

Commit ced56f6

Browse files
authored
fix!: do not augment nuxt options inside module entry (#295)
1 parent 0429ee3 commit ced56f6

File tree

5 files changed

+61
-44
lines changed

5 files changed

+61
-44
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ package-lock.json
1010
.output
1111
.idea/
1212
example/playground/.nuxtrc
13+
.temp*

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
"pathe": "^1.1.2",
4141
"pkg-types": "^1.1.1",
4242
"tsconfck": "^3.1.0",
43-
"unbuild": "^2.0.0",
44-
"untyped": "^1.4.2"
43+
"unbuild": "^2.0.0"
4544
},
4645
"peerDependencies": {
4746
"@nuxt/kit": "^3.12.2",

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/build.ts

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ import type { TSConfig } from 'pkg-types'
88
import { defu } from 'defu'
99
import { anyOf, createRegExp } from 'magic-regexp'
1010
import { consola } from 'consola'
11-
import type { ModuleMeta, NuxtModule } from '@nuxt/schema'
11+
import type { NuxtModule } from '@nuxt/schema'
1212
import { findExports, resolvePath } from 'mlly'
13-
import { resolveSchema, generateTypes } from 'untyped'
14-
import type { InputObject } from 'untyped'
1513
import { defineCommand } from 'citty'
16-
import { loadNuxt } from '@nuxt/kit'
1714

1815
import { name, version } from '../../package.json'
1916

@@ -172,24 +169,14 @@ export default defineCommand({
172169
await fsp.writeFile(metaFile, JSON.stringify(moduleMeta, null, 2), 'utf8')
173170

174171
// Generate types
175-
await writeTypes(ctx.options.outDir, moduleMeta, async () => {
176-
const nuxt = await loadNuxt({
177-
cwd,
178-
overrides: {
179-
modules: [resolve(cwd, './src/module')],
180-
},
181-
})
182-
const moduleOptions = await moduleFn.getOptions?.({}, nuxt) || {}
183-
await nuxt.close()
184-
return moduleOptions
185-
})
172+
await writeTypes(ctx.options.outDir)
186173
},
187174
},
188175
})
189176
},
190177
})
191178

192-
async function writeTypes(distDir: string, meta: ModuleMeta, getOptions: () => Promise<Record<string, unknown>>) {
179+
async function writeTypes(distDir: string) {
193180
const dtsFile = resolve(distDir, 'types.d.ts')
194181
const dtsFileMts = resolve(distDir, 'types.d.mts')
195182
if (existsSync(dtsFile)) {
@@ -211,21 +198,17 @@ async function writeTypes(distDir: string, meta: ModuleMeta, getOptions: () => P
211198
const appShims: string[] = []
212199
const schemaShims: string[] = []
213200
const moduleImports: string[] = []
214-
const moduleDeclarations: string[] = []
201+
const schemaImports: string[] = []
202+
const moduleExports: string[] = []
215203

216204
const hasTypeExport = (name: string) => isStub || typeExports.find(exp => exp.names.includes(name))
217205

218-
if (meta.configKey) {
219-
schemaShims.push(` interface NuxtConfig { ['${meta.configKey}']?: Partial<ModuleOptions> }`)
220-
schemaShims.push(` interface NuxtOptions { ['${meta.configKey}']?: ModuleOptions }`)
221-
222-
if (hasTypeExport('ModuleOptions')) {
223-
moduleImports.push('ModuleOptions')
224-
}
225-
else {
226-
moduleDeclarations.push(` ${generateTypes(await resolveSchema(await getOptions() as InputObject), { interfaceName: 'ModuleOptions' })}`)
227-
}
206+
if (!hasTypeExport('ModuleOptions')) {
207+
schemaImports.push('NuxtModule')
208+
moduleImports.push('default as Module')
209+
moduleExports.push(`export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>`)
228210
}
211+
229212
if (hasTypeExport('ModuleHooks')) {
230213
moduleImports.push('ModuleHooks')
231214
schemaShims.push(' interface NuxtHooks extends ModuleHooks {}')
@@ -252,14 +235,16 @@ async function writeTypes(distDir: string, meta: ModuleMeta, getOptions: () => P
252235
}
253236

254237
const dtsContents = `
238+
${schemaImports.length ? `import type { ${schemaImports.join(', ')} } from '@nuxt/schema'` : ''}
239+
255240
import type { ${moduleImports.join(', ')} } from './module'
256241
257-
${moduleDeclarations.join('\n')}
258242
${appShims.length ? `declare module '#app' {\n${appShims.join('\n')}\n}\n` : ''}
259243
${schemaShims.length ? `declare module '@nuxt/schema' {\n${schemaShims.join('\n')}\n}\n` : ''}
260244
${schemaShims.length ? `declare module 'nuxt/schema' {\n${schemaShims.join('\n')}\n}\n` : ''}
245+
${moduleExports.length ? `\n${moduleExports.join('\n')}` : ''}
261246
${typeExports[0] ? `\nexport type { ${typeExports[0].names.join(', ')} } from './module'` : ''}
262-
`
247+
`.trim().replace(/[\n\r]{3,}/g, '\n\n') + '\n'
263248

264249
await fsp.writeFile(dtsFile, dtsContents, 'utf8')
265250
if (!existsSync(dtsFileMts)) {

test/build.spec.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
import { fileURLToPath } from 'node:url'
2-
import { readFile, readdir } from 'node:fs/promises'
2+
import { cp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
33
import { beforeAll, describe, it, expect } from 'vitest'
44
import { execaCommand } from 'execa'
55
import { readPackageJSON } from 'pkg-types'
6-
import { join } from 'pathe'
6+
import { dirname, join } from 'pathe'
77
import { findStaticImports } from 'mlly'
88
import { version } from '../package.json'
99

1010
describe('module builder', () => {
1111
const rootDir = fileURLToPath(new URL('../example', import.meta.url))
12+
const secondRootDir = rootDir.replace('example', '.temp-example-without-options')
1213
const distDir = join(rootDir, 'dist')
14+
const secondDistDir = join(secondRootDir, 'dist')
1315
const runtimeDir = join(distDir, 'runtime')
1416

1517
beforeAll(async () => {
16-
await execaCommand('pnpm dev:prepare', { cwd: rootDir })
17-
await execaCommand('pnpm prepack', { cwd: rootDir })
18+
// Prepare second root directory without type export
19+
await mkdir(dirname(secondRootDir), { recursive: true })
20+
await rm(secondRootDir, { force: true, recursive: true })
21+
await cp(rootDir, secondRootDir, { recursive: true })
22+
const moduleSrc = join(secondRootDir, 'src/module.ts')
23+
const contents = await readFile(moduleSrc, 'utf-8').then(r => r.replace('export interface ModuleOptions', 'interface ModuleOptions'))
24+
await writeFile(moduleSrc, contents)
25+
26+
await Promise.all([
27+
execaCommand('pnpm dev:prepare', { cwd: rootDir }).then(() => execaCommand('pnpm prepack', { cwd: rootDir })),
28+
execaCommand('pnpm dev:prepare', { cwd: secondRootDir }).then(() => execaCommand('pnpm prepack', { cwd: secondRootDir })),
29+
])
1830
}, 120 * 1000)
1931

2032
it('should generate all files', async () => {
@@ -47,32 +59,55 @@ describe('module builder', () => {
4759
it('should write types to output directory', async () => {
4860
const types = await readFile(join(distDir, 'types.d.ts'), 'utf-8')
4961
expect(types).toMatchInlineSnapshot(`
62+
"import type { ModuleHooks, ModuleRuntimeHooks, ModuleRuntimeConfig, ModulePublicRuntimeConfig } from './module'
63+
64+
declare module '#app' {
65+
interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}
66+
}
67+
68+
declare module '@nuxt/schema' {
69+
interface NuxtHooks extends ModuleHooks {}
70+
interface RuntimeConfig extends ModuleRuntimeConfig {}
71+
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
72+
}
73+
74+
declare module 'nuxt/schema' {
75+
interface NuxtHooks extends ModuleHooks {}
76+
interface RuntimeConfig extends ModuleRuntimeConfig {}
77+
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
78+
}
79+
80+
export type { ModuleHooks, ModuleOptions, ModulePublicRuntimeConfig, ModuleRuntimeConfig, ModuleRuntimeHooks, default } from './module'
5081
"
51-
import type { ModuleOptions, ModuleHooks, ModuleRuntimeHooks, ModuleRuntimeConfig, ModulePublicRuntimeConfig } from './module'
82+
`)
83+
})
5284

85+
it('should generate types when no ModuleOptions are exported', async () => {
86+
const types = await readFile(join(secondDistDir, 'types.d.ts'), 'utf-8')
87+
expect(types).toMatchInlineSnapshot(`
88+
"import type { NuxtModule } from '@nuxt/schema'
89+
90+
import type { default as Module, ModuleHooks, ModuleRuntimeHooks, ModuleRuntimeConfig, ModulePublicRuntimeConfig } from './module'
5391
5492
declare module '#app' {
5593
interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}
5694
}
5795
5896
declare module '@nuxt/schema' {
59-
interface NuxtConfig { ['myModule']?: Partial<ModuleOptions> }
60-
interface NuxtOptions { ['myModule']?: ModuleOptions }
6197
interface NuxtHooks extends ModuleHooks {}
6298
interface RuntimeConfig extends ModuleRuntimeConfig {}
6399
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
64100
}
65101
66102
declare module 'nuxt/schema' {
67-
interface NuxtConfig { ['myModule']?: Partial<ModuleOptions> }
68-
interface NuxtOptions { ['myModule']?: ModuleOptions }
69103
interface NuxtHooks extends ModuleHooks {}
70104
interface RuntimeConfig extends ModuleRuntimeConfig {}
71105
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
72106
}
73107
108+
export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
74109
75-
export type { ModuleHooks, ModuleOptions, ModulePublicRuntimeConfig, ModuleRuntimeConfig, ModuleRuntimeHooks, default } from './module'
110+
export type { ModuleHooks, ModulePublicRuntimeConfig, ModuleRuntimeConfig, ModuleRuntimeHooks, default } from './module'
76111
"
77112
`)
78113
})

0 commit comments

Comments
 (0)