Skip to content

Commit 97ded7d

Browse files
committed
refactor(module): edit nuxt.config modules with oxc instead of magicast
1 parent ea82130 commit 97ded7d

13 files changed

Lines changed: 1477 additions & 284 deletions

File tree

packages/nuxi/tsdown.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { purgePolyfills } from 'unplugin-purge-polyfills'
66

77
const isAnalysingSize = process.env.BUNDLE_SIZE === 'true'
88

9+
const PARSERS = ['rolldown', 'oxc-parser']
10+
911
export default defineConfig({
1012
entry: ['src/index.ts', 'src/cli.ts', 'src/dev/index.ts'],
1113
shims: true,
1214
fixedExtension: true,
13-
deps: { onlyBundle: false },
15+
deps: { onlyBundle: false, neverBundle: PARSERS },
1416
dts: !isAnalysingSize && {
1517
oxc: true,
1618
entry: ['src/index.ts'],

packages/nuxt-cli/package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
"peerDependencies": {
3636
"@nuxt/schema": "^4.4.6",
3737
"jiti": "^2.7.0",
38+
"oxc-parser": ">=0.56.3",
39+
"rolldown": "^1.0.0-rc.16 || ^1.0.0",
3840
"serve": "^14.0.0"
3941
},
4042
"peerDependenciesMeta": {
@@ -44,6 +46,12 @@
4446
"jiti": {
4547
"optional": true
4648
},
49+
"oxc-parser": {
50+
"optional": true
51+
},
52+
"rolldown": {
53+
"optional": true
54+
},
4755
"serve": {
4856
"optional": true
4957
}
@@ -60,7 +68,6 @@
6068
"fzf": "^0.5.2",
6169
"get-port-please": "^3.2.0",
6270
"giget": "^3.3.0",
63-
"magicast": "^0.5.3",
6471
"nypm": "^0.6.8",
6572
"obug": "^2.1.1",
6673
"ohash": "^2.0.11",
@@ -92,6 +99,7 @@
9299
"jiti": "^2.7.0",
93100
"nitro": "^3.0.1-alpha.2",
94101
"nitropack": "^2.13.4",
102+
"rolldown": "^1.0.0-rc.16",
95103
"rollup": "^4.62.2",
96104
"rollup-plugin-visualizer": "^7.0.1",
97105
"tsdown": "^0.22.7",

packages/nuxt-cli/src/commands/module/add.ts

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { PackageManager } from 'nypm'
22
import type { PackageJson } from 'pkg-types'
33

4+
import type { ConfigEntries } from '../../utils/config'
45
import type { RegistryMeta } from '../../utils/registry'
56
import type { NuxtModule } from './_utils'
67
import process from 'node:process'
@@ -15,7 +16,7 @@ import { joinURL } from 'ufo'
1516
import { satisfies } from 'verkit'
1617

1718
import { runCommandDef as runCommand } from '../../run-command'
18-
import { updateConfig } from '../../utils/config'
19+
import { addNuxtConfigEntries, createNuxtConfig, readNuxtConfig } from '../../utils/config'
1920
import { fetchJson } from '../../utils/fetch'
2021
import { createInstallLog, resolvePackageManagerDescriptor, runInstall, takeUnreportedIgnoredBuilds } from '../../utils/install'
2122
import { logger } from '../../utils/logger'
@@ -231,45 +232,33 @@ async function addModules(modules: ResolvedModule[], { skipInstall = false, skip
231232

232233
// Update nuxt.config.ts
233234
if (!skipConfig) {
234-
await updateConfig({
235-
cwd,
236-
configFile: 'nuxt.config',
237-
async onCreate() {
235+
try {
236+
let config = await readNuxtConfig(cwd)
237+
if (!config) {
238238
logger.info(`Creating ${colors.cyan('nuxt.config.ts')}`)
239+
config = await createNuxtConfig(cwd, getDefaultNuxtConfig())
240+
}
239241

240-
return getDefaultNuxtConfig()
241-
},
242-
async onUpdate(config) {
243-
if (!config.modules && modules.some(module => !module.isLayer)) {
244-
config.modules = []
245-
}
246-
247-
for (const resolved of modules) {
248-
const key = resolved.isLayer ? 'extends' : 'modules'
249-
if (resolved.isLayer && !Array.isArray(config.extends)) {
250-
// `extends` also accepts a single layer as a string
251-
config.extends = config.extends ? [config.extends] : []
252-
}
253-
254-
const target: unknown[] = resolved.isLayer ? config.extends : config.modules
242+
const toAdd: ConfigEntries = {}
243+
for (const resolved of modules) {
244+
const key = resolved.isLayer ? 'extends' : 'modules'
245+
if (config[key].includes(resolved.specifier)) {
246+
logger.info(`${colors.cyan(resolved.specifier)} is already in the ${colors.cyan(key)}`)
255247

256-
if (target.includes(resolved.specifier)) {
257-
logger.info(`${colors.cyan(resolved.specifier)} is already in the ${colors.cyan(key)}`)
248+
continue
249+
}
258250

259-
continue
260-
}
251+
logger.info(`Adding ${colors.cyan(resolved.specifier)} to the ${colors.cyan(key)}`)
261252

262-
logger.info(`Adding ${colors.cyan(resolved.specifier)} to the ${colors.cyan(key)}`)
253+
toAdd[key] = [...toAdd[key] ?? [], resolved.specifier]
254+
}
263255

264-
target.push(resolved.specifier)
265-
}
266-
},
267-
}).catch((error) => {
268-
logger.error(`Failed to update ${colors.cyan('nuxt.config')}: ${error.message}`)
256+
await addNuxtConfigEntries(config, toAdd)
257+
}
258+
catch (error) {
259+
logger.error(`Failed to update ${colors.cyan('nuxt.config')}: ${(error as Error).message}`)
269260
logger.error(`Please manually add ${colors.cyan(modules.map(module => module.specifier).join(', '))} to ${colors.cyan('nuxt.config.ts')}`)
270-
271-
return null
272-
})
261+
}
273262
}
274263

275264
return true

packages/nuxt-cli/src/commands/module/remove.ts

Lines changed: 40 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { PackageJson } from 'pkg-types'
22

3+
import type { ConfigEntries } from '../../utils/config'
34
import type { NuxtModule } from './_utils'
45

56
import process from 'node:process'
@@ -12,7 +13,8 @@ import colors from 'picocolors'
1213
import { readPackageJSON } from 'pkg-types'
1314

1415
import { runCommandDef as runCommand } from '../../run-command'
15-
import { updateConfig } from '../../utils/config'
16+
import { readNuxtConfig, removeNuxtConfigEntries } from '../../utils/config'
17+
import { CONFIG_KEYS } from '../../utils/config-parse'
1618
import { logger } from '../../utils/logger'
1719
import { logNetworkError } from '../../utils/network'
1820
import { relativeToProcess } from '../../utils/paths'
@@ -98,88 +100,55 @@ async function removeModules(modules: string[], { skipInstall = false, skipConfi
98100
const dependencies = getProjectDependencies(projectPkg)
99101

100102
if (!skipConfig) {
101-
let configMissing = false
102-
let cancelled = false
103-
104-
await updateConfig({
105-
cwd,
106-
configFile: 'nuxt.config',
107-
onCreate() {
108-
configMissing = true
109-
return false
110-
},
111-
async onUpdate(config) {
112-
const arrays = ([['modules', config.modules], ['extends', config.extends]] as const)
113-
.filter(([, value]) => Array.isArray(value)) as Array<[string, unknown[]]>
114-
115-
if (arrays.length === 0) {
116-
return
117-
}
103+
const config = await readNuxtConfig(cwd).catch((error) => {
104+
logger.error(`Failed to read ${colors.cyan('nuxt.config')}: ${(error as Error).message}`)
105+
return undefined
106+
})
118107

119-
const present: string[] = []
120-
for (const [, items] of arrays) {
121-
for (const item of items) {
122-
const name = readModuleName(item)
123-
if (name) {
124-
present.push(name)
125-
}
126-
}
127-
}
108+
const present = config ? CONFIG_KEYS.flatMap(key => config[key]) : []
128109

129-
let toRemove: Set<string>
130-
if (modules.length === 0) {
131-
if (present.length === 0) {
132-
return
133-
}
110+
let toRemove = new Set(modules)
111+
if (config && modules.length === 0 && present.length > 0) {
112+
const picked = await multiselect({
113+
message: 'Select modules to remove:',
114+
options: present.map(m => ({ value: m, label: m })),
115+
required: true,
116+
})
134117

135-
const picked = await multiselect({
136-
message: 'Select modules to remove:',
137-
options: present.map(m => ({ value: m, label: m })),
138-
required: true,
139-
})
118+
if (isCancel(picked)) {
119+
cancel('No modules selected.')
120+
return false
121+
}
140122

141-
if (isCancel(picked)) {
142-
cancelled = true
143-
return
144-
}
123+
toRemove = new Set(picked as string[])
124+
}
145125

146-
toRemove = new Set(picked as string[])
147-
}
148-
else {
149-
toRemove = new Set(modules)
126+
if (config) {
127+
const doomed: ConfigEntries = {}
128+
for (const key of CONFIG_KEYS) {
129+
// A package may be configured through a subpath (`maz-ui/nuxt`) while the
130+
// user asks to remove the package itself.
131+
const names = config[key].filter(name => toRemove.has(name) || toRemove.has(basePackageName(name)))
132+
if (!names.length) {
133+
continue
150134
}
151-
152-
for (const [key, items] of arrays) {
153-
for (let i = items.length - 1; i >= 0; i--) {
154-
const name = readModuleName(items[i])
155-
// A package may be configured through a subpath (`maz-ui/nuxt`) while the
156-
// user asks to remove the package itself.
157-
if (!name || !(toRemove.has(name) || toRemove.has(basePackageName(name)))) {
158-
continue
159-
}
160-
logger.info(`Removing ${colors.cyan(name)} from the ${colors.cyan(key)}`)
161-
items.splice(i, 1)
162-
removedFromConfig.push(name)
163-
}
135+
for (const name of names) {
136+
logger.info(`Removing ${colors.cyan(name)} from the ${colors.cyan(key)}`)
164137
}
165-
},
166-
}).catch((error) => {
167-
if (configMissing) {
168-
return
138+
doomed[key] = names
139+
removedFromConfig.push(...names)
169140
}
170-
logger.error(`Failed to update ${colors.cyan('nuxt.config')}: ${error.message}`)
171-
logger.error(`Please manually remove ${colors.cyan(modules.join(', ') || 'the relevant modules')} from ${colors.cyan('nuxt.config.ts')}`)
172-
})
173141

174-
if (cancelled) {
175-
cancel('No modules selected.')
176-
return false
142+
await removeNuxtConfigEntries(config, doomed).catch((error) => {
143+
logger.error(`Failed to update ${colors.cyan('nuxt.config')}: ${(error as Error).message}`)
144+
logger.error(`Please manually remove ${colors.cyan(modules.join(', ') || 'the relevant modules')} from ${colors.cyan('nuxt.config.ts')}`)
145+
})
177146
}
178147

179148
if (modules.length === 0 && removedFromConfig.length === 0) {
180-
cancel(configMissing
181-
? `No ${colors.cyan('nuxt.config')} found in ${colors.cyan(relativeToProcess(cwd))}.`
182-
: `No modules configured in ${colors.cyan('nuxt.config')}.`)
149+
cancel(config
150+
? `No modules configured in ${colors.cyan('nuxt.config')}.`
151+
: `No ${colors.cyan('nuxt.config')} found in ${colors.cyan(relativeToProcess(cwd))}.`)
183152
return false
184153
}
185154
}
@@ -263,16 +232,6 @@ async function removeModules(modules: string[], { skipInstall = false, skipConfi
263232
return true
264233
}
265234

266-
function readModuleName(item: unknown): string | null {
267-
if (typeof item === 'string') {
268-
return item
269-
}
270-
if (Array.isArray(item) && typeof item[0] === 'string') {
271-
return item[0]
272-
}
273-
return null
274-
}
275-
276235
function resolveModuleName(input: string, modulesDB: NuxtModule[], installed: Set<string>): string {
277236
if (installed.has(input)) {
278237
return input

0 commit comments

Comments
 (0)