From c6d6573b5bf31b21bf460b31bc6eb8318055fa21 Mon Sep 17 00:00:00 2001 From: Ezira Ashenafi Date: Wed, 17 Sep 2025 20:58:21 +0300 Subject: [PATCH 1/5] refactor(nuxi): accept command definition on `runCommand` --- packages/nuxi/src/commands/index.ts | 26 ++++++++++++++++++++++++ packages/nuxi/src/commands/init.ts | 4 ++-- packages/nuxi/src/commands/module/add.ts | 3 ++- packages/nuxi/src/run.ts | 20 ++++++++++-------- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/packages/nuxi/src/commands/index.ts b/packages/nuxi/src/commands/index.ts index 416196fe..388afd21 100644 --- a/packages/nuxi/src/commands/index.ts +++ b/packages/nuxi/src/commands/index.ts @@ -21,3 +21,29 @@ export const commands = { typecheck: () => import('./typecheck').then(_rDefault), upgrade: () => import('./upgrade').then(_rDefault), } as const + + +// Inlined to avoid importing commands as it has sideeffects +export const nuxiCommands = [ + 'add', + 'analyze', + 'build', + 'cleanup', + '_dev', + 'dev', + 'devtools', + 'generate', + 'info', + 'init', + 'module', + 'prepare', + 'preview', + 'start', + 'test', + 'typecheck', + 'upgrade', +] as const satisfies (keyof typeof commands)[] + +export function isNuxiCommand(command: string) { + return (nuxiCommands as string[]).includes(command) +} \ No newline at end of file diff --git a/packages/nuxi/src/commands/init.ts b/packages/nuxi/src/commands/init.ts index 57008fbe..eb51f32d 100644 --- a/packages/nuxi/src/commands/init.ts +++ b/packages/nuxi/src/commands/init.ts @@ -16,6 +16,7 @@ import { hasTTY } from 'std-env' import { x } from 'tinyexec' import { runCommand } from '../run' +import addModuleCommand from './module/add' import { nuxtIcon, themeColor } from '../utils/ascii' import { logger } from '../utils/logger' import { cwdArgs, logLevelArgs } from './_shared' @@ -435,14 +436,13 @@ export default defineCommand({ // Add modules if (modulesToAdd.length > 0) { const args: string[] = [ - 'add', ...modulesToAdd, `--cwd=${templateDownloadPath}`, ctx.args.install ? '' : '--skipInstall', ctx.args.logLevel ? `--logLevel=${ctx.args.logLevel}` : '', ].filter(Boolean) - await runCommand('module', args) + await runCommand(addModuleCommand, args) } // Display next steps diff --git a/packages/nuxi/src/commands/module/add.ts b/packages/nuxi/src/commands/module/add.ts index b40723a3..22d9fa55 100644 --- a/packages/nuxi/src/commands/module/add.ts +++ b/packages/nuxi/src/commands/module/add.ts @@ -22,6 +22,7 @@ import { logger } from '../../utils/logger' import { getNuxtVersion } from '../../utils/versions' import { cwdArgs, logLevelArgs } from '../_shared' import { checkNuxtCompatibility, fetchModules, getRegistryFromContent } from './_utils' +import prepareCommand from '../prepare' interface RegistryMeta { registry: string @@ -95,7 +96,7 @@ export default defineCommand({ if (!ctx.args.skipInstall) { const args = Object.entries(ctx.args).filter(([k]) => k in cwdArgs || k in logLevelArgs).map(([k, v]) => `--${k}=${v}`) - await runCommand('prepare', args) + await runCommand(prepareCommand, args) } }, }) diff --git a/packages/nuxi/src/run.ts b/packages/nuxi/src/run.ts index 6d3d2cc6..f3451274 100644 --- a/packages/nuxi/src/run.ts +++ b/packages/nuxi/src/run.ts @@ -1,9 +1,9 @@ import process from 'node:process' import { fileURLToPath } from 'node:url' -import { runCommand as _runCommand, runMain as _runMain } from 'citty' +import { runCommand as _runCommand, runMain as _runMain, ArgsDef, CommandDef } from 'citty' -import { commands } from './commands' +import { isNuxiCommand } from './commands' import { main } from './main' globalThis.__nuxt_cli__ = globalThis.__nuxt_cli__ || { @@ -20,18 +20,22 @@ globalThis.__nuxt_cli__ = globalThis.__nuxt_cli__ || { export const runMain = () => _runMain(main) // To provide subcommands call it as `runCommand(, [, ...])` -export async function runCommand( - name: string, +export async function runCommand( + command: CommandDef, argv: string[] = process.argv.slice(2), data: { overrides?: Record } = {}, ) { argv.push('--no-clear') // Dev - - if (!(name in commands)) { - throw new Error(`Invalid command ${name}`) + if (command.meta && "name" in command.meta && typeof command.meta.name === 'string') { + const name = command.meta.name + if (!(isNuxiCommand(name))) { + throw new Error(`Invalid command ${name}`) + } + } else { + throw new Error(`Invalid command, must be named`) } - return await _runCommand(await commands[name as keyof typeof commands](), { + return await _runCommand(command, { rawArgs: argv, data: { overrides: data.overrides || {}, From c1594b23de3f26a377fc038a310cbd070a253680 Mon Sep 17 00:00:00 2001 From: Ezira Ashenafi Date: Wed, 17 Sep 2025 21:44:58 +0300 Subject: [PATCH 2/5] refactor(nuxi): collocate exported `runMain` with main --- packages/nuxi/src/index.ts | 4 ++-- packages/nuxi/src/main.ts | 4 +++- packages/nuxi/src/run.ts | 4 +--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nuxi/src/index.ts b/packages/nuxi/src/index.ts index 580bb27c..7de3cd13 100644 --- a/packages/nuxi/src/index.ts +++ b/packages/nuxi/src/index.ts @@ -1,2 +1,2 @@ -export { main } from './main' -export { runCommand, runMain } from './run' +export { main, runMain} from './main' +export { runCommand } from './run' diff --git a/packages/nuxi/src/main.ts b/packages/nuxi/src/main.ts index 7b26ae95..99211d8c 100644 --- a/packages/nuxi/src/main.ts +++ b/packages/nuxi/src/main.ts @@ -2,7 +2,7 @@ import nodeCrypto from 'node:crypto' import { resolve } from 'node:path' import process from 'node:process' -import { defineCommand } from 'citty' +import { defineCommand, runMain as _runMain } from 'citty' import { provider } from 'std-env' import { description, name, version } from '../package.json' @@ -71,3 +71,5 @@ export const main = defineCommand({ } }, }) + +export const runMain = () => _runMain(main) diff --git a/packages/nuxi/src/run.ts b/packages/nuxi/src/run.ts index f3451274..5eb28dd9 100644 --- a/packages/nuxi/src/run.ts +++ b/packages/nuxi/src/run.ts @@ -1,7 +1,7 @@ import process from 'node:process' import { fileURLToPath } from 'node:url' -import { runCommand as _runCommand, runMain as _runMain, ArgsDef, CommandDef } from 'citty' +import { runCommand as _runCommand, ArgsDef, CommandDef } from 'citty' import { isNuxiCommand } from './commands' import { main } from './main' @@ -17,8 +17,6 @@ globalThis.__nuxt_cli__ = globalThis.__nuxt_cli__ || { ), } -export const runMain = () => _runMain(main) - // To provide subcommands call it as `runCommand(, [, ...])` export async function runCommand( command: CommandDef, From fb09b10007eb9e0fcb9d2fb63b975599a5051f47 Mon Sep 17 00:00:00 2001 From: Ezira Ashenafi Date: Wed, 17 Sep 2025 21:45:43 +0300 Subject: [PATCH 3/5] refactor(nuxi): extract command utils file --- packages/nuxi/src/commands/_utils.ts | 26 ++++++++++++++++++++++++++ packages/nuxi/src/commands/index.ts | 24 ------------------------ packages/nuxi/src/run.ts | 3 +-- 3 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 packages/nuxi/src/commands/_utils.ts diff --git a/packages/nuxi/src/commands/_utils.ts b/packages/nuxi/src/commands/_utils.ts new file mode 100644 index 00000000..edabbc20 --- /dev/null +++ b/packages/nuxi/src/commands/_utils.ts @@ -0,0 +1,26 @@ +import { type commands } from '.'; + +// Inlined list of nuxi commands to avoid including `commands` in bundle if possible +export const nuxiCommands = [ + 'add', + 'analyze', + 'build', + 'cleanup', + '_dev', + 'dev', + 'devtools', + 'generate', + 'info', + 'init', + 'module', + 'prepare', + 'preview', + 'start', + 'test', + 'typecheck', + 'upgrade', +] as const satisfies (keyof typeof commands)[]; + +export function isNuxiCommand(command: string) { + return (nuxiCommands as string[]).includes(command); +} diff --git a/packages/nuxi/src/commands/index.ts b/packages/nuxi/src/commands/index.ts index 388afd21..be47b624 100644 --- a/packages/nuxi/src/commands/index.ts +++ b/packages/nuxi/src/commands/index.ts @@ -23,27 +23,3 @@ export const commands = { } as const -// Inlined to avoid importing commands as it has sideeffects -export const nuxiCommands = [ - 'add', - 'analyze', - 'build', - 'cleanup', - '_dev', - 'dev', - 'devtools', - 'generate', - 'info', - 'init', - 'module', - 'prepare', - 'preview', - 'start', - 'test', - 'typecheck', - 'upgrade', -] as const satisfies (keyof typeof commands)[] - -export function isNuxiCommand(command: string) { - return (nuxiCommands as string[]).includes(command) -} \ No newline at end of file diff --git a/packages/nuxi/src/run.ts b/packages/nuxi/src/run.ts index 5eb28dd9..d64c3c4d 100644 --- a/packages/nuxi/src/run.ts +++ b/packages/nuxi/src/run.ts @@ -3,8 +3,7 @@ import { fileURLToPath } from 'node:url' import { runCommand as _runCommand, ArgsDef, CommandDef } from 'citty' -import { isNuxiCommand } from './commands' -import { main } from './main' +import { isNuxiCommand } from './commands/_utils' globalThis.__nuxt_cli__ = globalThis.__nuxt_cli__ || { // Programmatic usage fallback From af5b074caf31ccb89b8d26d331a50119eb949b7c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 19:52:50 +0000 Subject: [PATCH 4/5] [autofix.ci] apply automated fixes --- packages/nuxi/src/commands/_utils.ts | 6 +++--- packages/nuxi/src/commands/index.ts | 2 -- packages/nuxi/src/commands/init.ts | 2 +- packages/nuxi/src/commands/module/add.ts | 2 +- packages/nuxi/src/index.ts | 2 +- packages/nuxi/src/main.ts | 2 +- packages/nuxi/src/run.ts | 10 ++++++---- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/nuxi/src/commands/_utils.ts b/packages/nuxi/src/commands/_utils.ts index edabbc20..024ce837 100644 --- a/packages/nuxi/src/commands/_utils.ts +++ b/packages/nuxi/src/commands/_utils.ts @@ -1,4 +1,4 @@ -import { type commands } from '.'; +import type { commands } from '.' // Inlined list of nuxi commands to avoid including `commands` in bundle if possible export const nuxiCommands = [ @@ -19,8 +19,8 @@ export const nuxiCommands = [ 'test', 'typecheck', 'upgrade', -] as const satisfies (keyof typeof commands)[]; +] as const satisfies (keyof typeof commands)[] export function isNuxiCommand(command: string) { - return (nuxiCommands as string[]).includes(command); + return (nuxiCommands as string[]).includes(command) } diff --git a/packages/nuxi/src/commands/index.ts b/packages/nuxi/src/commands/index.ts index be47b624..416196fe 100644 --- a/packages/nuxi/src/commands/index.ts +++ b/packages/nuxi/src/commands/index.ts @@ -21,5 +21,3 @@ export const commands = { typecheck: () => import('./typecheck').then(_rDefault), upgrade: () => import('./upgrade').then(_rDefault), } as const - - diff --git a/packages/nuxi/src/commands/init.ts b/packages/nuxi/src/commands/init.ts index eb51f32d..1a46d3f5 100644 --- a/packages/nuxi/src/commands/init.ts +++ b/packages/nuxi/src/commands/init.ts @@ -16,10 +16,10 @@ import { hasTTY } from 'std-env' import { x } from 'tinyexec' import { runCommand } from '../run' -import addModuleCommand from './module/add' import { nuxtIcon, themeColor } from '../utils/ascii' import { logger } from '../utils/logger' import { cwdArgs, logLevelArgs } from './_shared' +import addModuleCommand from './module/add' const DEFAULT_REGISTRY = 'https://raw.githubusercontent.com/nuxt/starter/templates/templates' const DEFAULT_TEMPLATE_NAME = 'v4' diff --git a/packages/nuxi/src/commands/module/add.ts b/packages/nuxi/src/commands/module/add.ts index 22d9fa55..8a5da677 100644 --- a/packages/nuxi/src/commands/module/add.ts +++ b/packages/nuxi/src/commands/module/add.ts @@ -21,8 +21,8 @@ import { runCommand } from '../../run' import { logger } from '../../utils/logger' import { getNuxtVersion } from '../../utils/versions' import { cwdArgs, logLevelArgs } from '../_shared' -import { checkNuxtCompatibility, fetchModules, getRegistryFromContent } from './_utils' import prepareCommand from '../prepare' +import { checkNuxtCompatibility, fetchModules, getRegistryFromContent } from './_utils' interface RegistryMeta { registry: string diff --git a/packages/nuxi/src/index.ts b/packages/nuxi/src/index.ts index 7de3cd13..70f311c8 100644 --- a/packages/nuxi/src/index.ts +++ b/packages/nuxi/src/index.ts @@ -1,2 +1,2 @@ -export { main, runMain} from './main' +export { main, runMain } from './main' export { runCommand } from './run' diff --git a/packages/nuxi/src/main.ts b/packages/nuxi/src/main.ts index 99211d8c..8f0457c4 100644 --- a/packages/nuxi/src/main.ts +++ b/packages/nuxi/src/main.ts @@ -2,7 +2,7 @@ import nodeCrypto from 'node:crypto' import { resolve } from 'node:path' import process from 'node:process' -import { defineCommand, runMain as _runMain } from 'citty' +import { runMain as _runMain, defineCommand } from 'citty' import { provider } from 'std-env' import { description, name, version } from '../package.json' diff --git a/packages/nuxi/src/run.ts b/packages/nuxi/src/run.ts index d64c3c4d..3975cd6b 100644 --- a/packages/nuxi/src/run.ts +++ b/packages/nuxi/src/run.ts @@ -1,7 +1,8 @@ +import type { ArgsDef, CommandDef } from 'citty' import process from 'node:process' -import { fileURLToPath } from 'node:url' -import { runCommand as _runCommand, ArgsDef, CommandDef } from 'citty' +import { fileURLToPath } from 'node:url' +import { runCommand as _runCommand } from 'citty' import { isNuxiCommand } from './commands/_utils' @@ -23,12 +24,13 @@ export async function runCommand( data: { overrides?: Record } = {}, ) { argv.push('--no-clear') // Dev - if (command.meta && "name" in command.meta && typeof command.meta.name === 'string') { + if (command.meta && 'name' in command.meta && typeof command.meta.name === 'string') { const name = command.meta.name if (!(isNuxiCommand(name))) { throw new Error(`Invalid command ${name}`) } - } else { + } + else { throw new Error(`Invalid command, must be named`) } From 30eb0647d67e2dbdfadd888c32134909a32f07ff Mon Sep 17 00:00:00 2001 From: Ezira Ashenafi Date: Thu, 18 Sep 2025 10:33:48 +0300 Subject: [PATCH 5/5] test(nuxi) add unit test for `isNuxiCommand` --- .../nuxi/test/unit/commands/_utils.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/nuxi/test/unit/commands/_utils.test.ts diff --git a/packages/nuxi/test/unit/commands/_utils.test.ts b/packages/nuxi/test/unit/commands/_utils.test.ts new file mode 100644 index 00000000..e61ea96d --- /dev/null +++ b/packages/nuxi/test/unit/commands/_utils.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest' +import { isNuxiCommand, nuxiCommands } from '../../../src/commands/_utils' + +describe('isNuxiCommand', () => { + it('should return true for valid nuxi commands', () => { + nuxiCommands.forEach((command) => { + expect(isNuxiCommand(command)).toBe(true) + }) + }) + + it('should return false for invalid nuxi commands', () => { + const invalidCommands = [ + '', + ' ', + 'devv', + 'Dev', + 'BuilD', + 'random', + 'nuxi', + 'install', + undefined, + null, + ] + + invalidCommands.forEach((command) => { + expect(isNuxiCommand(command as string)).toBe(false) + }) + }) +})