diff --git a/packages/runmate/src/cli.ts b/packages/runmate/src/cli.ts index 3cc2d73e..320989ed 100644 --- a/packages/runmate/src/cli.ts +++ b/packages/runmate/src/cli.ts @@ -7,7 +7,7 @@ import { align } from './commands/align'; import { executeInPackages } from './commands/executeInPackages'; import { list } from './commands/list'; import { peers } from './commands/peers'; -import { setJsonValue } from './commands/set-json-value'; +import { updateJsonValue } from './commands/update-json-value'; import { version } from './commands/version'; import { packageRunner, PackageRunnerUtils } from './packageRunner'; import { packageJSONDependencyKeys } from './packageVersion'; @@ -150,7 +150,7 @@ program list(program); version(program); align(program); -setJsonValue(program); +updateJsonValue(program); peers(program); program.version('> Runmate ' + require('../package.json').version); diff --git a/packages/runmate/src/commands/install.ts b/packages/runmate/src/commands/install.ts index d0766160..d67f29cd 100644 --- a/packages/runmate/src/commands/install.ts +++ b/packages/runmate/src/commands/install.ts @@ -35,26 +35,4 @@ export function install(program: Command) { hey.error(e); } }); - - program - .command( - 'version [pattern]' // - ) - .description( - [ - 'Update package versions.', - 'Examples: ', - '➜ version 1.0.0', - '➜ version minor', - '➜ version major ./packages/utils', - ].join('\n') - ) - .alias('v') - .action(async function run(releaseTypeOrVersion, pattern): Promise { - try { - await packageVersion(releaseTypeOrVersion, pattern); - } catch (e: any) { - hey.error(e); - } - }); } diff --git a/packages/runmate/src/commands/set-json-value.ts b/packages/runmate/src/commands/update-json-value.ts similarity index 68% rename from packages/runmate/src/commands/set-json-value.ts rename to packages/runmate/src/commands/update-json-value.ts index eabfcde9..7985413c 100644 --- a/packages/runmate/src/commands/set-json-value.ts +++ b/packages/runmate/src/commands/update-json-value.ts @@ -1,3 +1,5 @@ +import fs from 'fs'; + import { hey, jsonParse, setByPath } from '@powership/utils'; import { nodePath } from '@powership/utils/out/node'; import { Command } from 'commander'; @@ -6,7 +8,9 @@ import { writePackageJSON } from '../handleJSON'; import { packageRunner } from '../packageRunner'; import { packageVersion } from '../packageVersion'; -export function setJsonValue(program: Command) { +export function updateJsonValue(program: Command) {} + +function update(program: Command, op: 'get' | 'set') { program .command('set') .description('Set json value') @@ -44,41 +48,22 @@ export function setJsonValue(program: Command) { throw error; } + if (op === 'get') { + fs.writeSync(process.stdout.fd, value); + return process.exit(0); + } + const jsonValue = jsonParse(value)[1] || value; // @ts-ignore setByPath(json, objectPath, jsonValue); - if (dryRun) { - console.warn(nodePath.relative(util.cwd, jsonPath), '\n', json); - } else { - // @ts-ignore - writePackageJSON(jsonPath, json); - } + // @ts-ignore + writePackageJSON(jsonPath, json); + return; }); } catch (e: any) { hey.error(e); - } - }); - - program - .command( - 'version [pattern]' // - ) - .description( - [ - 'Update package versions.', - 'Examples: ', - '➜ version 1.0.0', - '➜ version minor', - '➜ version major ./packages/utils', - ].join('\n') - ) - .alias('v') - .action(async function run(releaseTypeOrVersion, pattern): Promise { - try { - await packageVersion(releaseTypeOrVersion, pattern); - } catch (e: any) { - hey.error(e); + process.exit(1); } }); } diff --git a/packages/utils/src/hey.spec.ts b/packages/utils/src/hey.spec.ts index ad1fe136..4cac8bcd 100644 --- a/packages/utils/src/hey.spec.ts +++ b/packages/utils/src/hey.spec.ts @@ -1,4 +1,4 @@ -import { hey } from './hey'; +import { hey, Ident } from './hey'; describe('hey', () => { // afterEach(); @@ -25,4 +25,62 @@ describe('hey', () => { test('error', () => { hey.error(new Error('foo')); }); + + test('Ident', () => { + let chain = new Ident('a') + .li('aa') + .li('ab') + .ul('ac') + .li('aca') + .li('acb') + .back() + .li('LI-after-back()') + .back(5) + // + .ul('UL-after-back(5)') + .li('UL-after-back5_child0'); + + const expHead = [ + 'a', + ' --> aa', + ' --> ab', + ' --> ac', + ' ----> aca', + ' ----> acb', + ' --> LI-after-back()', + ' --> UL-after-back(5)', + ' ----> UL-after-back5_child0', + ]; + + expect(chain.toString().split('\n')).toEqual(expHead); + + // expect(chain.head.state.toString()).toEqual([]); + + // expect(t.toString()).toBe('Block'); + // + // const childA = t.push('a'); + // + // expect(childA.toString()).toEqual('a'); + // expect(t.toString()).toEqual('Block\n a'); + // + // const b = t.push('b'); + // + // expect(b.print()).toEqual('Block\n a\n b'); + // + // const c = b.pop('I should be children of ') + + // expect(childA.printChild().split('\n')).toEqual([ + // '', + // ' a', // + // ' b', + // ]); + + // expect(childA.push('bread of').toString().split('\n')).toEqual([ + // 'Batata', + // ' potatoes', + // ]); + + // + // expect(child.write()).toEqual('potatoes'); + }); }); diff --git a/packages/utils/src/hey.ts b/packages/utils/src/hey.ts index 1890e197..8e7d81b2 100644 --- a/packages/utils/src/hey.ts +++ b/packages/utils/src/hey.ts @@ -1,11 +1,12 @@ // @only-server import fs from 'fs'; +import { isPlainObject } from './isObject'; import { tryCatch } from './tryCatch'; function _hey(strings: TemplateStringsArray | string, ...values: any[]) { const formatted = heyFormat(strings, ...values) + '\n'; - write(formatted); + writeToStdoutSync(formatted); return formatted; } @@ -27,9 +28,15 @@ export const styles = { _hey.styles = styles; -function heyFormat(input: string | TemplateStringsArray, ...values: any[]) { +function heyFormat( + input: string | TemplateStringsArray | string[], + ...values: any[] +) { let text = (() => { - if (typeof input === 'string') return input; + if (typeof input === 'string') { + input = [input]; + } + let result = input[0]; values.forEach((value, i) => { result += value + input[i + 1]; @@ -50,7 +57,7 @@ function heyFormat(input: string | TemplateStringsArray, ...values: any[]) { return trimTabs(text); } -function write(input: string) { +export function writeToStdoutSync(input: string) { if ( typeof process === 'undefined' || typeof process?.stdout?.write !== 'function' @@ -127,3 +134,197 @@ Object.keys(styles).forEach((key) => { }); export const hey = _hey as unknown as Hey; + +const IdentPrefixes = { + ol: (text: string, index: number) => { + return index > -1 ? `${index}) ${text}` : text; + }, + arrow: (text: string, _index: number) => `➜ ${text}`, + dot: (text: string, _index: number) => `• ${text}`, + ul: (text: string, _index: number) => `• ${text}`, + li: (text: string, _index: number) => `• ${text}`, +}; + +export type IdentStyle = keyof typeof IdentPrefixes; +export type IdentOptions = { + index?: number; + level?: number; + parent?: Ident | null; + style?: IdentStyle; + text?: string; + tabSize?: number; + children?: Ident[]; +}; + +export class Ident { + state: IdentState; + children = new Map(); + + get head(): Ident { + return this.state.parent?.head || this; + } + + constructor(text?: string, options?: IdentOptions); + constructor(options?: IdentOptions); + constructor(...args: any) { + this.state = new IdentState(...args); + } + + toString = () => { + return Ident.printElement(this.head); + }; + + get list() { + return [...this.children]; + } + get chain() { + const list = this.list; + return list.map.bind(list); + } + + /** + * @internal + * @param root + */ + static printElement = (root: Ident): string => { + const children = root.state.children.map((item) => { + const prefix = item.prefix; + const txt = Ident.printElement(item); + return prefix + txt; + }); + + return root.state.text + children.join(''); + }; + + li = (text: string, style: IdentStyle = 'li') => { + this.push(text, 1, style); + return this; + }; + + ul = (text: string, style: IdentStyle = 'ul') => { + return this.push(text, 2, style); + }; + + private push = ( + text: string, + distanceFromParent: number, + style: IdentStyle = 'arrow' + ) => { + const nextLevel = Math.max(distanceFromParent, 0) + this.state.level; + + const index = this.head.state.children.length; + + const child = new Ident(text, { + level: nextLevel, + style: style, + text, + tabSize: this.state.tabSize, + children: [], + parent: this, + index, + }); + + this.state.children.push(child); + + return child; + }; + + back = (times = 1): Ident => { + let current: Ident = this; + + while (times > 0) { + --times; + if (!current?.state.parent) { + return this.head; + } + current = current.state.parent; + } + + return current.state.parent || this.head; + }; + + private get prefix() { + const tab_size = Math.max(this.head.state.tabSize, 2); + const level = this.state.parent?.state.level || 1; + const times = level * tab_size; + + let txt = this.state.parent ? '\n ' : ' '; + txt += `-`.repeat(times); + txt += '> '; + return txt; + } + + static DEFAULT_STYLE: IdentStyle = 'arrow'; + static DEFAULT_TAB_SIZE = 2; +} + +export class IdentState { + index: number; + level: number; + parent: Ident | null; + style: IdentStyle; + text: string; + tabSize: number; + children: Ident[]; + + constructor( + ...input: + | [string | undefined, Partial | undefined] + | [Partial | undefined] + ) { + let { + level = 0, + index = 0, + parent = null, + style = Ident.DEFAULT_STYLE, + tabSize = Ident.DEFAULT_TAB_SIZE, + text = '', + children = [], + } = (input.find((el) => isPlainObject(el)) || {}) as IdentOptions; + + if (typeof input[0] === 'string') { + text = input[0]; + } + + this.text = text ?? `${text}`; + this.index = index; + this.parent = parent; + this.style = style; + this.level = level; + this.children = children; + this.tabSize = tabSize; + } + + toString = () => { + return this.print(new Map()); + }; + + private print(touched: Map) { + if (touched.has(this.index)) return touched.get(this.index)!; + + const { + // + parent, + index, + level, + style, + children, + tabSize, + text, + } = this; + + let txt = [ + ['index', index], + ['level', level], + ['style', style], + ['tabSize', tabSize], + ['text', text], + ['children', children.map((el) => el.state.index)], + ['parent', parent?.state.index], + ] + .map(([key, value]) => ` "${key}": ${JSON.stringify(value)}`) + .join(',\n'); + + return touched.set(index, `{\n${txt}\n}`).get(index)!; + } +}