⚡ Staged CLI arguments parser with TypeScript support
npm install @mirta/staged-args



> Утилита поэтапного разбора аргументов командной строки. Предназначена для создания сложных, многоуровневых CLI-инструментов с поддержкой глобальных флагов, безопасной обработки ошибок и гибких подсказок.
@mirta/staged-args — это минималистичный, типобезопасный инструмент на основе parseArgs из node:util для разбора аргументов в несколько этапов. Подходит для фреймворков, генераторов и оркестраторов, где требуется:
- Сначала обработать глобальные флаги (--config, --verbose),
- Затем — командные опции,
- При этом не прерывать выполнение при ошибках ввода,
- Иметь возможность локализации сообщений.
Пакет @mirta/staged-args предназначен исключительно для Node.js-инструментов (≥ 20.10.0), не используется в рантайме Duktape.
``sh`
pnpm add @mirta/staged-args
Создайте экземпляр парсера с аргументами командной строки:
`ts
import { createStagedArgs } from '@mirta/staged-args'
const staged = createStagedArgs(process.argv.slice(2))
`
Определите схему опций:
`ts`
const schema = {
config: { type: 'string', default: 'mirta.json' },
verbose: { type: 'boolean' },
} as const
Выполните поэтапный разбор:
`ts
const result = staged.parse(schema)
if (result.hasErrors) {
// Обработка ошибок
result.errors.forEach(error => {
console.error(Ошибка: ${error.type}, опция: ${error.option})
})
process.exit(1)
}
const { data: globals } = result
console.log('Глобальные флаги:', globals)
`
Позиционные аргументы (например, команда и её параметры) доступны через positionals:
`ts`
const { positionals } = result.data
// → ['deploy', 'staging']
> ⚠️ Неизвестные опции (например, --force) не попадают в positionals — они остаются опциями и могут быть отловлены как unknown-option при использовании parseFinal.
Для продолжения разбора используйте stagedArgs:
`ts`
const { stagedArgs } = result.data
const commandResult = stagedArgs.parseFinal(commandSchema)
@mirta/staged-args позволяет:
- Разделить разбор аргументов на этапы.
- Сначала обработать флаги, влияющие на конфигурацию.
- Позже — разобрать команду и её опции, основываясь на загруженных данных.
Это критично для инструментов вроде @mirta/cli, create-mirta, nx, где --config должен быть обработан до выбора команды.
> ⚠️ Парсер отслеживает, какие позиционные аргументы уже использовались как значения (например, --port 3000), и помечает их индексы, чтобы избежать повторного присваивания. --port
> Однако сами опции (например, ) не "исчезают" — они могут быть обработаны снова на следующих этапах, если указаны в схеме. deploy
> Это гарантирует, что значение не будет ошибочно использовано как значение для --port на втором этапе.
Функции parse и parseFinal возвращают:
`ts`
type Result
= | { hasErrors: false, data: TData }
| { hasErrors: true, errors: TError[] }
➡️ Пока вы не проверите hasErrors, поле data недоступно в типовой системе.
➡️ Это принуждает к явной обработке ошибок и предотвращает использование некорректных данных.
#### Почему это важно
`ts
if (result.hasErrors) {
// ❌ TypeScript не позволит обратиться к result.data
console.log(result.data.values) // → Ошибка компиляции
}
// ✅ Только после проверки
if (!result.hasErrors) {
console.log(result.data.values) // → OK
console.log(result.data.positionals) // → OK
console.log(result.data.stagedArgs) // → OK — можно продолжить разбор
}
`
Такой подход:
- Гарантирует, что вы не используете данные при наличии ошибок.
- Делает stagedArgs доступным только при успешном разборе.@mirta/i18n
- Совместим с системами локализации, такими как .
Можно передать функцию подсказки:
`ts`
const args = createStagedArgs(process.argv.slice(2), {
suggest: (unknown, known) => {
return known.includes('config') ? 'config' : undefined
}
})
Или использовать suggestClosest из @mirta/basics/fuzzy:
`ts
import { suggestClosest } from '@mirta/basics/fuzzy'
const staged = createStagedArgs(process.argv.slice(2), {
suggest: suggestClosest,
})
`
Это не обязательная зависимость — вы решаете, какую стратегию использовать.
- ParseError — возвращается в Result:`
ts`
{ type: 'unknown-option', option: '--confog', suggestion: 'config' }
Подлежит локализации, не прерывает выполнение.
- SchemaError — выбрасывается в виде исключения:
Например, при дублировании имён опций.
Это ошибка разработки, не может возникнуть у конечного пользователя.
Создаёт экземпляр парсера.
#### Параметры:
- args — массив строк (обычно process.argv.slice(2)).options.suggest
- — функция, возвращающая возможную коррекцию для неизвестной опции.
#### Возвращает:
Объект с методами parse, parseFinal.
---
Разбирает аргументы по схеме.
Сохраняет состояние: какие опции и значения уже обработаны.
#### Возвращает:
Result, где поле data содержит:values
- — распарсенные значения опций,positionals
- — необработанные позиционные аргументы,stagedArgs
- — новый этап парсинга, включающий текущую схему (можно продолжать разбор).
> ✅ Используйте parse для многоэтапного разбора.
---
Аналог parse, но считается финальным этапом:unknown-option
- Проверяет наличие неизвестных опций → ошибка .stagedArgs
- Не возвращает — дальнейший парсинг невозможен.
#### Возвращает:
Result, где поле data содержит:values
- — распарсенные значения,positionals
- — позиционные аргументы.
> ⚠️ Используйте parseFinal для команд или финальной валидации.
---
Возвращает необработанные аргументы для передачи следующему этапу.
---
Поддерживаемые типы ошибок:
`ts`
| { type: 'unknown-option', option: string, suggestion?: string }
| { type: 'missing-value', option: string }
`ts
const staged = createStagedArgs(process.argv.slice(2), { suggest: suggestClosest })
// Этап 1: глобальные флаги
const globalSchema = { config: { type: 'string' }, verbose: { type: 'boolean' } } as const
const globalResult = staged.parse(globalSchema)
if (globalResult.hasErrors) {
// Показываем локализованные сообщения
logErrors(globalResult.errors)
process.exit(1)
}
// ✅ data доступно — ошибок нет
const { positionals, stagedArgs } = globalResult.data
// Загружаем конфиг на основе --config
const config = loadConfig(globalResult.data.values.config)
// Этап 2: команда и её позиционные параметры
const command = positionals[0]
if (!command) {
console.error('Команда не указана')
process.exit(1)
}
const commandSchema = config.commands[command]
if (!commandSchema) {
console.error(Неизвестная команда: ${command})
process.exit(1)
}
// Продолжаем разбор: опции вроде --verbose остаются доступными
const commandResult = stagedArgs.parseFinal(commandSchema)
if (commandResult.hasErrors) {
logErrors(commandResult.errors)
process.exit(1)
}
// Выполняем
run(command, globalResult.data.values, commandResult.data.values)
`
- Модульность: каждый компонент — отдельный файл.
- Без зависимостей: только node:util.#src/*
- ESM-first: поддержка через imports.Values
- TypeScript: полная типизация, включая вывод