From 27f3c2be9f8fff5628b06eacfc3ecfcca077eaea Mon Sep 17 00:00:00 2001 From: Stephen Zhou Date: Mon, 27 Nov 2023 16:47:09 +0800 Subject: [PATCH] fix: respect config file when no explicit cli flags (#88) --- src/cli.ts | 41 +++++++++++++++-------------------------- src/config.ts | 12 +++++++----- src/constants.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/io/packages.ts | 2 +- src/log.ts | 9 +++++---- src/types.ts | 16 +++++++++------- src/utils/sort.ts | 8 ++++++++ 7 files changed, 84 insertions(+), 43 deletions(-) create mode 100644 src/constants.ts diff --git a/src/cli.ts b/src/cli.ts index 9af3a5c..ee55323 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,24 +6,23 @@ import c from 'picocolors' import { version } from '../package.json' import { check } from './commands/check' import { usage } from './commands/usage' +import { resolveConfig } from './config' +import { LOG_LEVELS, MODE_CHOICES } from './constants' import type { CommonOptions } from './types' -import { LOGLEVELS, resolveConfig } from './config' -import type { SortOption } from './utils/sort' +import { SORT_CHOICES } from './utils/sort' import { checkGlobal } from './commands/check/checkGlobal' function commonOptions(args: Argv): Argv { return args .option('cwd', { alias: 'C', - default: '', type: 'string', describe: 'specify the current working directory', }) .option('loglevel', { - default: 'info', type: 'string', describe: 'log level', - choices: LOGLEVELS, + choices: LOG_LEVELS, }) .option('failOnOutdated', { type: 'boolean', @@ -31,7 +30,6 @@ function commonOptions(args: Argv): Argv { }) .option('silent', { alias: 's', - default: false, type: 'boolean', describe: 'complete silent', }) @@ -45,12 +43,6 @@ function commonOptions(args: Argv): Argv { type: 'boolean', describe: 'force fetching from server, bypass cache', }) - .option('sort', { - type: 'string', - default: 'diff-asc' as SortOption, - choices: ['time-asc', 'time-desc', 'diff-asc', 'diff-desc', 'name-asc', 'name-desc', 'time', 'diff', 'name'], - describe: 'sort by most outdated absolute or relative to dependency', - }) .option('ignore-paths', { type: 'string', describe: 'ignore paths for search package.json', @@ -77,11 +69,6 @@ function commonOptions(args: Argv): Argv { describe: 'update only for dependencies', conflicts: ['dev'], }) - .option('include-locked', { - alias: 'l', - type: 'boolean', - describe: 'include locked dependencies & devDependencies', - }) } // eslint-disable-next-line no-unused-expressions @@ -96,7 +83,6 @@ yargs(hideBin(process.argv)) .option('detail', { alias: 'a', type: 'boolean', - default: false, describe: 'show more info', }) .help() @@ -110,47 +96,50 @@ yargs(hideBin(process.argv)) (args) => { return commonOptions(args) .positional('mode', { - default: 'default', type: 'string', describe: 'the mode how version range resolves, can be "default", "major", "minor", "latest" or "newest"', - choices: ['default', 'major', 'minor', 'patch', 'latest', 'newest'], + choices: MODE_CHOICES, }) .option('write', { alias: 'w', - default: false, type: 'boolean', describe: 'write to package.json', }) .option('global', { alias: 'g', - default: false, type: 'boolean', describe: 'update global packages', }) .option('interactive', { alias: 'I', - default: false, // TODO: enable by default: !process.env.CI && process.stdout.isTTY, type: 'boolean', describe: 'interactive mode', }) .option('install', { alias: 'i', - default: false, type: 'boolean', describe: 'install directly after bumping', }) .option('update', { alias: 'u', - default: false, type: 'boolean', describe: 'update directly after bumping', }) .option('all', { alias: 'a', - default: false, type: 'boolean', describe: 'show all packages up to date info', }) + .option('sort', { + type: 'string', + choices: SORT_CHOICES, + describe: 'sort by most outdated absolute or relative to dependency', + }) + .option('includeLocked', { + alias: 'l', + type: 'boolean', + describe: 'include locked dependencies & devDependencies', + }) .help() }, async (args) => { diff --git a/src/config.ts b/src/config.ts index 2aed91f..c3b2b6d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,11 +4,10 @@ import _debug from 'debug' import { createConfigLoader } from 'unconfig' import type { CommonOptions } from './types' import { toArray } from './utils/toArray' +import { DEFAULT_CHECK_OPTIONS, DEFAULT_USAGE_OPTIONS } from './constants' const debug = _debug('taze:config') -export const LOGLEVELS = ['debug', 'info', 'warn', 'error', 'silent'] - function normalizeConfig(options: T) { options.ignorePaths = toArray(options.ignorePaths) options.exclude = toArray(options.exclude) @@ -20,7 +19,10 @@ function normalizeConfig(options: T) { return options } -export async function resolveConfig(options: T): Promise { +export async function resolveConfig( + options: T & { _?: (string | number)[] }, +): Promise { + const defaults = options?._?.[0] === 'usage' ? DEFAULT_USAGE_OPTIONS : DEFAULT_CHECK_OPTIONS options = normalizeConfig(options) const loader = createConfigLoader({ @@ -44,10 +46,10 @@ export async function resolveConfig(options: T): Promis const config = await loader.load() if (!config.sources.length) - return options + return deepmerge(defaults, options as T) as T debug(`config file found ${config.sources[0]}`) const configOptions = normalizeConfig(config.config) - return deepmerge(configOptions, options) as T + return deepmerge(deepmerge(defaults, configOptions), options as T) as T } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..d719aef --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,39 @@ +import type { CheckOptions, CommonOptions, UsageOptions } from './types' + +export const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'silent'] as const + +export const MODE_CHOICES = ['default', 'major', 'minor', 'patch', 'latest', 'newest'] as const + +export const DEFAULT_COMMON_OPTIONS: CommonOptions = { + cwd: '', + loglevel: 'info', + failOnOutdated: false, + silent: false, + recursive: false, + force: false, + ignorePaths: '', + include: '', + exclude: '', + dev: false, + prod: false, +} + +export const DEFAULT_USAGE_OPTIONS: UsageOptions = { + ...DEFAULT_COMMON_OPTIONS, + detail: false, + recursive: true, +} + +export const DEFAULT_CHECK_OPTIONS: CheckOptions = { + ...DEFAULT_COMMON_OPTIONS, + mode: 'default', + write: false, + global: false, + // TODO: enable by default: !process.env.CI && process.stdout.isTTY, + interactive: false, + install: false, + update: false, + all: false, + sort: 'diff-asc', + includeLocked: false, +} diff --git a/src/io/packages.ts b/src/io/packages.ts index 44ad4e4..ca75ac3 100644 --- a/src/io/packages.ts +++ b/src/io/packages.ts @@ -48,7 +48,7 @@ export async function writePackage(pkg: PackageMeta, options: CommonOptions) { } export async function loadPackage(relative: string, options: CommonOptions, shouldUpdate: (name: string) => boolean): Promise { - const filepath = path.resolve(options.cwd, relative) + const filepath = path.resolve(options.cwd ?? '', relative) const raw = await readJSON(filepath) let deps: RawDep[] = [] diff --git a/src/log.ts b/src/log.ts index a471a21..14b2d3b 100644 --- a/src/log.ts +++ b/src/log.ts @@ -1,18 +1,19 @@ import process from 'node:process' import c from 'picocolors' import { MultiBar, Presets } from 'cli-progress' -import { LOGLEVELS } from './config' +import { LOG_LEVELS } from './constants' import { visualLength, visualPadEnd, visualPadStart } from './render' +import type { LogLevel } from './types' interface Options { columns: number pending: number align: string - loglevel: string + loglevel: LogLevel } -export function shouldLog(level: string, messageLevel: string) { - return LOGLEVELS.indexOf(level) <= LOGLEVELS.indexOf(messageLevel) +export function shouldLog(level: LogLevel, messageLevel: LogLevel) { + return LOG_LEVELS.indexOf(level) <= LOG_LEVELS.indexOf(messageLevel) } export class TableLogger { diff --git a/src/types.ts b/src/types.ts index c5ef359..ef7fc3b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,15 +41,17 @@ export interface ResolvedDepChange extends RawDep { aliasName?: string } +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent' + export interface CommonOptions { - cwd: string + cwd?: string recursive?: boolean ignorePaths?: string | string[] include?: string | string[] exclude?: string | string[] prod?: boolean dev?: boolean - loglevel?: string + loglevel?: LogLevel failOnOutdated?: boolean silent?: boolean force?: boolean @@ -57,14 +59,14 @@ export interface CommonOptions { } export interface UsageOptions extends CommonOptions { - detail: boolean - recursive: true + detail?: boolean + recursive?: true } export interface CheckOptions extends CommonOptions { - mode: string - write: boolean - all: boolean + mode?: RangeMode + write?: boolean + all?: boolean sort?: SortOption interactive?: boolean install?: boolean diff --git a/src/utils/sort.ts b/src/utils/sort.ts index 18eba08..001c67e 100644 --- a/src/utils/sort.ts +++ b/src/utils/sort.ts @@ -6,6 +6,14 @@ export type SortKey = 'time' | 'diff' | 'name' export type SortOrder = 'asc' | 'desc' export type SortOption = `${SortKey}-${SortOrder}` +export const SORT_CHOICES = [ + 'time-asc', + 'time-desc', + 'diff-asc', + 'diff-desc', + 'name-asc', + 'name-desc', +] as const export function parseSortOption(option: SortOption) { return option.split('-') as [SortKey, SortOrder]