diff --git a/.changeset/config.json b/.changeset/config.json index 5be92ba..80be20a 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -18,7 +18,8 @@ "ignore": [ "@examples/hackernews", "@examples/normal", - "@examples/normal", + "@examples/shadcn-cli", + "@examples/tailwindcss", "@examples/with-antd4" ] } diff --git a/.changeset/rotten-monkeys-trade.md b/.changeset/rotten-monkeys-trade.md new file mode 100644 index 0000000..375e248 --- /dev/null +++ b/.changeset/rotten-monkeys-trade.md @@ -0,0 +1,5 @@ +--- +'@umijs/tnf': patch +--- + +feat: add config command diff --git a/README.md b/README.md index 8e7a46a..0c31e93 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ $ pnpm preview - `tnf generate/g `: Generate a new page (or component and other types in the future). - `tnf preview`: Preview the product after building the project. - `tnf sync --mode=`: Sync the project to the temporary directory. +- `tnf config [ list | get | set | remove ] [name] [value]`: Quickly view and modify configurations through the command line. ## API diff --git a/package.json b/package.json index 4d43e25..7bd8a91 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "get-port-please": "^3.1.2", "http-proxy-middleware": "^3.0.3", "json5": "^2.2.3", + "magicast": "^0.3.5", "pathe": "^1.1.2", "picocolors": "^1.1.1", "random-color": "^1.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdebc5d..6b6bace 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: version: 0.9.4 c12: specifier: ^2.0.1 - version: 2.0.1 + version: 2.0.1(magicast@0.3.5) chokidar: specifier: ^4.0.1 version: 4.0.1 @@ -86,6 +86,9 @@ importers: json5: specifier: ^2.2.3 version: 2.2.3 + magicast: + specifier: ^0.3.5 + version: 0.3.5 pathe: specifier: ^1.1.2 version: 1.1.2 @@ -506,12 +509,10 @@ packages: /@babel/helper-string-parser@7.25.9: resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.25.9: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.25.9: resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} @@ -532,7 +533,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.26.0 - dev: true /@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0): resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} @@ -613,7 +613,6 @@ packages: dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - dev: true /@bloomberg/record-tuple-polyfill@0.0.4: resolution: {integrity: sha512-h0OYmPR3A5Dfbetra/GzxBAzQk8sH7LhRkRUTdagX6nrtlUgJGYCTv4bBK33jsTQw9HDd8PE2x1Ma+iRKEDUsw==} @@ -4301,7 +4300,7 @@ packages: engines: {node: '>= 0.8'} dev: false - /c12@2.0.1: + /c12@2.0.1(magicast@0.3.5): resolution: {integrity: sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==} peerDependencies: magicast: ^0.3.5 @@ -4315,6 +4314,7 @@ packages: dotenv: 16.4.5 giget: 1.2.3 jiti: 2.4.0 + magicast: 0.3.5 mlly: 1.7.2 ohash: 1.1.4 pathe: 1.1.2 @@ -7091,6 +7091,14 @@ packages: '@jridgewell/sourcemap-codec': 1.5.0 dev: true + /magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + source-map-js: 1.2.1 + dev: false + /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} diff --git a/src/cli.ts b/src/cli.ts index 6071463..5f964b1 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -59,6 +59,9 @@ async function run(cwd: string) { case 'sync': const { sync } = await import('./sync/sync.js'); return sync({ context }); + case 'config': + const { config } = await import('./config/config.js'); + return config({ context }); default: throw new Error(`Unknown command: ${cmd}`); } diff --git a/src/config/config.ts b/src/config/config.ts index f38fc33..16aa959 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -2,7 +2,10 @@ import { loadConfig as loadC12Config, watchConfig as watchC12Config, } from 'c12'; +import { updateConfig } from 'c12/update'; +import pc from 'picocolors'; import { CONFIG_FILE } from '../constants'; +import type { Context } from '../types'; import type { Config } from './types'; import { ConfigSchema } from './types'; @@ -48,3 +51,109 @@ function createLoadConfigOpts({ cwd, defaults, overrides }: ConfigOpts) { overrides, }; } + +export function list(config: Config, name?: string) { + const getValue = (value: any) => { + if (typeof value !== 'function') { + return value; + } + return pc.yellow('The value data type does not support the view'); + }; + const print = (key: string) => { + console.log( + ` - ${pc.blue(`[key: ${key}]`)}`, + getValue(config[key as keyof Config]), + ); + console.log(); + }; + console.log(); + console.log(` Configs:`); + console.log(); + if (name) { + if ( + !config[name as keyof Config] && + config[name as keyof Config] !== false + ) { + // current key not existed + throw new Error(`key '${name}' not found`); + } + print(name as string); + } else { + // list all + Object.keys(config).forEach((key) => { + print(key); + }); + } + + console.log(); +} + +export async function setConfig({ + cwd, + name, + value, + type = 'set', +}: { + cwd: string; + name: string | number | undefined; + value: any; + type?: string | 'set' | 'remove'; +}) { + const configOpts = createLoadConfigOpts({ cwd }); + await updateConfig({ + ...configOpts, + onUpdate: (_config) => { + if (!name) { + console.log(pc.yellow(`key '${name}' not found`)); + return; + } + const _value = _config[name]; + if (type === 'remove') { + if (_config.hasOwnProperty(name)) { + delete _config[name]; + } + } else { + if (value) { + let safeValue: string | number | boolean | undefined = value; + if (safeValue === 'true') { + safeValue = true; + } else if (safeValue === 'false') { + safeValue = false; + } + _config[name] = safeValue as string | number | undefined; + } + } + const result = ConfigSchema.safeParse(_config); + if (!result.success) { + console.log( + pc.yellow(`Invalid configuration: ${result.error.message}`), + ); + // Verification failed, recall modification + _config[name] = _value; + } else { + console.log( + pc.green(`${type} config:${name} on ${configOpts.configFile}`), + ); + } + }, + }); +} +export async function config({ context }: { context: Context }) { + const { _ } = context.argv; + const [, command, name, value] = _; + + switch (command) { + case 'list': + list(context.config); + break; + case 'get': + list(context.config, `${name}`); + break; + case 'set': + case 'remove': + setConfig({ cwd: context.cwd, name, value, type: command }); + break; + default: + throw new Error(`Unsupported sub command ${command} for tnf config.`); + } +}