From e775d83ccfab2693c9f7b5e86da58c2a79db4e37 Mon Sep 17 00:00:00 2001 From: Guz013 <43732358+Guz013@users.noreply.github.com> Date: Thu, 3 Aug 2023 18:33:58 -0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=E2=99=BB=EF=B8=8F=20repurpose=20Cl?= =?UTF-8?q?i=20class=20to=20Configs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed Cli to ConfigsProcessor, now the Cli class is purposed to just parsing the cli arguments and orchestrate other classes --- packages/cli/src/cli.js | 169 ++++++--------------------- packages/cli/src/configs.js | 2 +- packages/cli/src/configsProcessor.js | 138 ++++++++++++++++++++++ packages/cli/src/index.js | 10 +- packages/cli/src/types.d.ts | 7 ++ pnpm-lock.yaml | 11 +- 6 files changed, 193 insertions(+), 144 deletions(-) mode change 100755 => 100644 packages/cli/src/cli.js create mode 100755 packages/cli/src/configsProcessor.js diff --git a/packages/cli/src/cli.js b/packages/cli/src/cli.js old mode 100755 new mode 100644 index 2da197d..ef1b2f7 --- a/packages/cli/src/cli.js +++ b/packages/cli/src/cli.js @@ -1,152 +1,55 @@ -#!node -import path from 'node:path'; -import { createSpinner } from 'nanospinner'; -import glob from 'picomatch'; -import c from 'picocolors'; +import { Command } from 'commander'; +import ConfigsProcessor from './configsProcessor.js'; +import configs from './configs.js'; import Workspace from './workspace.js'; +import path from 'node:path'; export default class Cli { - /** @type {string} */ - dir = process.cwd(); - /** @type {import('./types').Config[]} */ - configs; + #program = new Command(); - /** @type {string[] | undefined} */ - #packagesPatterns; + /** @type {import('./types').CliArgs} */ + args = { + dir: process.cwd(), + }; - /** - * @param {{ - * configs: import('./types').Config[], - * packages?: string[], - * directory?: string, - * }} options - Cli options - */ - constructor(options) { - this.#packagesPatterns = options.packages; - this.configs = options?.configs; - this.dir = path.normalize(options.directory ?? this.dir); - } + setArgs() { + this.#program + .option('--packages ') + .option('--merge-to-root') + .option('--dir ', undefined); - /** - * @param {import('./types').Package} pkg - Package to detect from - * @param {import('./types').Config['options']} options - Options to be passed - * @param {boolean} single - Whether to only detect one option - * @param {import('nanospinner').Spinner} spinner - Spinner to update - * @returns {string[]} - The detected options - */ - detectOptions(pkg, options, single, spinner) { + this.#program.parse(); - /** @type {string[]} */ - const detectedOptions = []; + this.args = { + ...this.args, + ...this.#program.opts(), + }; - for (const option of options) { - - spinner.update({ - text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)}`)}`, - }); - - if (option.detect === true) { - detectedOptions.push(option.name); - spinner.update({ - text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.green('✓')}`)}`, - }); - continue; - } - else if (!option.detect) continue; - - const match = glob(option.detect); - - const files = pkg.files.filter(f => match ? match(f) : false); - const directories = pkg.directories.filter(f => match ? match(f) : false); - - if (files.length > 0 || directories.length > 0) { - detectedOptions.push(option.name); - spinner.update({ - text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.green('✔')}`)}`, - }); - if (single) break; - } - else { - spinner.update({ - text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.red('✖')}`)}`, - }); - } - } - - return detectedOptions; - } - - /** - * @param {import('./types').Package} pkg - The package to detect configs - * @returns {import('./types').Package['config']} - Detected configs record - */ - detectConfig(pkg) { - - const spinner = createSpinner(`Configuring ${c.bold(c.blue(pkg.name))}`); - spinner.start(); - - /** @type {import('./types').Package['config']} */ - const pkgConfig = {}; - - for (const config of this.configs) { - pkgConfig[config.name] = this.detectOptions( - pkg, - config.options, - config.type === 'single', - spinner, - ); - spinner.update({ text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: config ${config.name}`)}` }); - } - - spinner.success({ text: `Configuring ${c.bold(c.blue(pkg.name))}\n${c.dim(JSON.stringify(pkgConfig))}\n` }); - return pkgConfig; - } - - /** - * @param {import('./types').Package[]} packages Packages to generate the map from - * @returns {import('./types').PackagesConfigsMap} A map of what packages has some configuration - */ - generateConfigMap(packages) { - - /** @type {import('./types').PackagesConfigsMap} */ - const configMap = new Map(); - - for (const pkg of packages) { - - Object.entries(pkg.config ?? {}).forEach(([key, options]) => { - /** @type {Map} */ - const optionsMap = configMap.get(key) ?? new Map(); - - options.forEach(option => { - const paths = optionsMap.get(option) ?? []; - optionsMap.set(option, [pkg.path, ...paths]); - - if (paths.length >= packages.length - 2 || paths.includes(this.dir)) { - console.log('a', packages.length, paths.length); - optionsMap.set(option, [this.dir]); - } - }); - - configMap.set(key, optionsMap); - }); - } - - return configMap; + this.args.dir = !this.args.dir.startsWith('/') + ? path.join(process.cwd(), this.args.dir) + : this.args.dir; } async run() { - let packages = await new Workspace(this.dir, this.#packagesPatterns).getPackages(); + this.setArgs(); - packages = packages.map( - pkg => { - pkg.config = this.detectConfig(pkg); return pkg; - }, - ); + console.log(this.args.dir); - console.log(packages); + const processor = new ConfigsProcessor({ configs }); + const packages = (await new Workspace(this.args.dir, this.args?.packages ) + .getPackages()) + .map(pkg => { + pkg.config = processor.detectConfig(pkg); + return pkg; + }); + + const configsMaps = processor.generateConfigMap(packages); + + console.log(configsMaps); } + } diff --git a/packages/cli/src/configs.js b/packages/cli/src/configs.js index ad6aea4..896b094 100644 --- a/packages/cli/src/configs.js +++ b/packages/cli/src/configs.js @@ -3,7 +3,7 @@ export default [ { name: 'framework', - type: 'single', + type: 'multiple', options: [ { name: 'svelte', diff --git a/packages/cli/src/configsProcessor.js b/packages/cli/src/configsProcessor.js new file mode 100755 index 0000000..065ef08 --- /dev/null +++ b/packages/cli/src/configsProcessor.js @@ -0,0 +1,138 @@ +#!node +import path from 'node:path'; +import { createSpinner } from 'nanospinner'; +import glob from 'picomatch'; +import c from 'picocolors'; + +export default class ConfigsProcessor { + /** @type {string} */ + dir = process.cwd(); + + /** @type {import('./types.js').Config[]} */ + configs; + + /** @type {string[] | undefined} */ + #packagesPatterns; + + /** + * @param {{ + * configs: import('./types.js').Config[], + * packages?: string[], + * directory?: string, + * }} options - Cli options + */ + constructor(options) { + this.#packagesPatterns = options.packages; + this.configs = options?.configs; + this.dir = path.normalize(options.directory ?? this.dir); + } + + /** + * @param {import('./types.js').Package} pkg - Package to detect from + * @param {import('./types.js').Config['options']} options - Options to be passed + * @param {boolean} single - Whether to only detect one option + * @param {import('nanospinner').Spinner} spinner - Spinner to update + * @returns {string[]} - The detected options + */ + detectOptions(pkg, options, single, spinner) { + + /** @type {string[]} */ + const detectedOptions = []; + + for (const option of options) { + + spinner.update({ + text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)}`)}`, + }); + + if (option.detect === true) { + detectedOptions.push(option.name); + spinner.update({ + text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.green('✓')}`)}`, + }); + continue; + } + else if (!option.detect) continue; + + const match = glob(option.detect); + + const files = pkg.files.filter(f => match ? match(f) : false); + const directories = pkg.directories.filter(f => match ? match(f) : false); + + if (files.length > 0 || directories.length > 0) { + detectedOptions.push(option.name); + spinner.update({ + text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.green('✔')}`)}`, + }); + if (single) break; + } + else { + spinner.update({ + text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: option ${c.bold(option.name)} ${c.red('✖')}`)}`, + }); + } + } + + return detectedOptions; + } + + /** + * @param {import('./types.js').Package} pkg - The package to detect configs + * @returns {import('./types.js').Package['config']} - Detected configs record + */ + detectConfig(pkg) { + + const spinner = createSpinner(`Configuring ${c.bold(c.blue(pkg.name))}`); + spinner.start(); + + /** @type {import('./types.js').Package['config']} */ + const pkgConfig = {}; + + for (const config of this.configs) { + pkgConfig[config.name] = this.detectOptions( + pkg, + config.options, + config.type === 'single', + spinner, + ); + spinner.update({ text: `Configuring ${c.bold(c.blue(pkg.name))}${c.dim(`: config ${config.name}`)}` }); + } + + spinner.success({ text: `Configuring ${c.bold(c.blue(pkg.name))}\n${c.dim(JSON.stringify(pkgConfig))}\n` }); + return pkgConfig; + } + + /** + * @param {import('./types.js').Package[]} packages Packages to generate the map from + * @returns {import('./types.js').PackagesConfigsMap} A map of what packages has some configuration + */ + generateConfigMap(packages) { + + /** @type {import('./types.js').PackagesConfigsMap} */ + const configMap = new Map(); + + for (const pkg of packages) { + + Object.entries(pkg.config ?? {}).forEach(([key, options]) => { + /** @type {Map} */ + const optionsMap = configMap.get(key) ?? new Map(); + + options.forEach(option => { + const paths = optionsMap.get(option) ?? []; + optionsMap.set(option, [pkg.path, ...paths]); + + if (paths.length >= packages.length - 2 || paths.includes(this.dir)) { + console.log('a', packages.length, paths.length); + optionsMap.set(option, [this.dir]); + } + }); + + configMap.set(key, optionsMap); + }); + } + + return configMap; + + } +} + diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 454f4a8..20e2906 100755 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -1,12 +1,4 @@ import Cli from './cli.js'; -import configs from './configs.js'; -import { program } from 'commander'; -program.option('--packages '); - -program.parse(process.argv); -/** @type {{ packages?: string[] } & import('commander').OptionValues} */ -const options = program.opts(); - -const cli = new Cli({ configs, packages: options.packages }); +const cli = new Cli(); await cli.run(); diff --git a/packages/cli/src/types.d.ts b/packages/cli/src/types.d.ts index e9c9b21..91c2d7f 100644 --- a/packages/cli/src/types.d.ts +++ b/packages/cli/src/types.d.ts @@ -1,3 +1,10 @@ +import type { OptionValues } from 'commander'; + +export type CliArgs = { + packages?: string[] + mergeToRoot?: boolean + dir: string +} & OptionValues; export interface Config { name: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5edc700..0a81224 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,9 @@ importers: '@types/node': specifier: ^20.4.2 version: 20.4.2 + '@types/prompts': + specifier: ^2.4.4 + version: 2.4.4 packages/config: dependencies: @@ -891,6 +894,13 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/prompts@2.4.4: + resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==} + dependencies: + '@types/node': 20.4.2 + kleur: 3.0.3 + dev: true + /@types/pug@2.0.6: resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} dev: true @@ -2507,7 +2517,6 @@ packages: /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - dev: false /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}