From b262fadc2c3abb6f8ecaef912afcaed3b7892158 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sun, 16 Jul 2023 16:44:19 -0400 Subject: [PATCH 01/17] refactor(commands): type safe available builders list --- commands/start.command.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commands/start.command.ts b/commands/start.command.ts index dd907e2d1..1d4f256b2 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -3,6 +3,7 @@ import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; import { Input } from './command.input'; +import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -79,7 +80,7 @@ export class StartCommand extends AbstractCommand { !isWebpackEnabled, }); - const availableBuilders = ['tsc', 'webpack', 'swc']; + const availableBuilders: BuilderVariant[] = ['tsc', 'webpack', 'swc']; if (command.builder && !availableBuilders.includes(command.builder)) { console.error( ERROR_PREFIX + From fcda32b764b6590ce05edd359b6d129eb6867400 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sun, 16 Jul 2023 17:38:28 -0400 Subject: [PATCH 02/17] refactor: replace command inputs by dictionary-like data abstraction --- actions/abstract.action.ts | 4 +- actions/add.action.ts | 14 +++--- actions/build.action.ts | 36 +++++---------- actions/generate.action.ts | 6 +-- actions/new.action.ts | 46 ++++++++----------- actions/start.action.ts | 25 +++------- commands/add.command.ts | 13 +++--- commands/build.command.ts | 22 ++++----- commands/command.input.ts | 40 +++++++++++++++- commands/generate.command.ts | 21 +++++---- commands/new.command.ts | 25 +++++----- commands/start.command.ts | 34 +++++++------- lib/compiler/helpers/get-builder.ts | 4 +- lib/compiler/helpers/get-tsc-config.path.ts | 4 +- lib/compiler/helpers/get-value-or-default.ts | 12 +++-- .../helpers/get-webpack-config-path.ts | 4 +- lib/compiler/webpack-compiler.ts | 4 +- lib/utils/project-utils.ts | 10 ++-- 18 files changed, 165 insertions(+), 159 deletions(-) diff --git a/actions/abstract.action.ts b/actions/abstract.action.ts index 70355adf8..ca8bd5825 100644 --- a/actions/abstract.action.ts +++ b/actions/abstract.action.ts @@ -1,9 +1,9 @@ -import { Input } from '../commands'; +import { Input, CommandInputsContainer } from '../commands'; export abstract class AbstractAction { public abstract handle( inputs?: Input[], - options?: Input[], + options?: CommandInputsContainer, extraFlags?: string[], ): Promise; } diff --git a/actions/add.action.ts b/actions/add.action.ts index 5c5d1d09e..08c7924e8 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -1,5 +1,5 @@ import * as chalk from 'chalk'; -import { Input } from '../commands'; +import { CommandInputsContainer, Input } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractPackageManager, @@ -23,7 +23,7 @@ import { AbstractAction } from './abstract.action'; const schematicName = 'nest-add'; export class AddAction extends AbstractAction { - public async handle(inputs: Input[], options: Input[], extraFlags: string[]) { + public async handle(inputs: Input[], options: CommandInputsContainer, extraFlags: string[]) { const libraryName = this.getLibraryName(inputs); const packageName = this.getPackageName(libraryName); const collectionName = this.getCollectionName(libraryName, packageName); @@ -33,9 +33,11 @@ export class AddAction extends AbstractAction { skipInstall || (await this.installPackage(collectionName, tagName)); if (packageInstallSuccess) { const sourceRootOption: Input = await this.getSourceRoot( - inputs.concat(options), + inputs.concat(options.toArray()), ); - options.push(sourceRootOption); + if (sourceRootOption) { + options.addInput(sourceRootOption) + } await this.addLibrary(collectionName, options, extraFlags); } else { @@ -117,7 +119,7 @@ export class AddAction extends AbstractAction { private async addLibrary( collectionName: string, - options: Input[], + options: CommandInputsContainer, extraFlags: string[], ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); @@ -125,7 +127,7 @@ export class AddAction extends AbstractAction { schematicOptions.push( new SchematicOption( 'sourceRoot', - options.find((option) => option.name === 'sourceRoot')!.value as string, + options.resolveInput('sourceRoot', true).value, ), ); const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; diff --git a/actions/build.action.ts b/actions/build.action.ts index 7048aa086..e736569d5 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -1,7 +1,7 @@ import * as chalk from 'chalk'; import { join } from 'path'; import * as ts from 'typescript'; -import { Input } from '../commands'; +import { Input, CommandInputsContainer } from '../commands'; import { AssetsManager } from '../lib/compiler/assets-manager'; import { Compiler } from '../lib/compiler/compiler'; import { getBuilder } from '../lib/compiler/helpers/get-builder'; @@ -40,19 +40,10 @@ export class BuildAction extends AbstractAction { protected readonly assetsManager = new AssetsManager(); protected readonly workspaceUtils = new WorkspaceUtils(); - public async handle(commandInputs: Input[], commandOptions: Input[]) { + public async handle(commandInputs: Input[], commandOptions: CommandInputsContainer) { try { - const watchModeOption = commandOptions.find( - (option) => option.name === 'watch', - ); - const watchMode = !!(watchModeOption && watchModeOption.value); - - const watchAssetsModeOption = commandOptions.find( - (option) => option.name === 'watchAssets', - ); - const watchAssetsMode = !!( - watchAssetsModeOption && watchAssetsModeOption.value - ); + const watchMode = !!commandOptions.resolveInput('watch')?.value; + const watchAssetsMode = !!commandOptions.resolveInput('watchAssets')?.value; await this.runBuild( commandInputs, @@ -72,15 +63,13 @@ export class BuildAction extends AbstractAction { public async runBuild( commandInputs: Input[], - commandOptions: Input[], + commandOptions: CommandInputsContainer, watchMode: boolean, watchAssetsMode: boolean, isDebugEnabled = false, onSuccess?: () => void, ) { - const configFileName = commandOptions.find( - (option) => option.name === 'config', - )!.value as string; + const configFileName = commandOptions.resolveInput('config', true).value const configuration = await this.loader.load(configFileName); const appName = commandInputs.find((input) => input.name === 'app')! .value as string; @@ -155,7 +144,7 @@ export class BuildAction extends AbstractAction { appName: string, pathToTsconfig: string, watchMode: boolean, - options: Input[], + options: CommandInputsContainer, tsOptions: ts.CompilerOptions, onSuccess: (() => void) | undefined, ) { @@ -183,7 +172,7 @@ export class BuildAction extends AbstractAction { private runWebpack( configuration: Required, appName: string, - commandOptions: Input[], + commandOptions: CommandInputsContainer, pathToTsconfig: string, debug: boolean, watchMode: boolean, @@ -216,7 +205,7 @@ export class BuildAction extends AbstractAction { private runTsc( watchMode: boolean, - options: Input[], + options: CommandInputsContainer, configuration: Required, pathToTsconfig: string, appName: string, @@ -228,15 +217,12 @@ export class BuildAction extends AbstractAction { this.tsConfigProvider, this.tsLoader, ); - const isPreserveWatchOutputEnabled = options.find( - (option) => - option.name === 'preserveWatchOutput' && option.value === true, - ); + const isPreserveWatchOutputEnabled = options.resolveInput('preserveWatchOutput')?.value || false watchCompiler.run( configuration, pathToTsconfig, appName, - { preserveWatchOutput: !!isPreserveWatchOutputEnabled }, + { preserveWatchOutput: isPreserveWatchOutputEnabled }, onSuccess, ); } else { diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 22c3ff012..c412737df 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk'; import { Answers } from 'inquirer'; -import { Input } from '../commands'; +import { Input, CommandInputsContainer } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractCollection, @@ -21,8 +21,8 @@ import { import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { - public async handle(inputs: Input[], options: Input[]) { - await generateFiles(inputs.concat(options)); + public async handle(inputs: Input[], options: CommandInputsContainer) { + await generateFiles(inputs.concat(options.toArray())); } } diff --git a/actions/new.action.ts b/actions/new.action.ts index 99c3fef50..6e78fbbf9 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; import { join } from 'path'; -import { Input } from '../commands'; +import { CommandInputsContainer, Input } from '../commands'; import { defaultGitIgnore } from '../lib/configuration/defaults'; import { AbstractPackageManager, @@ -24,22 +24,16 @@ import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { - public async handle(inputs: Input[], options: Input[]) { - const directoryOption = options.find( - (option) => option.name === 'directory', - ); - const dryRunOption = options.find((option) => option.name === 'dry-run'); - const isDryRunEnabled = dryRunOption && dryRunOption.value; + public async handle(inputs: Input[], options: CommandInputsContainer) { + const directoryOption = options.resolveInput('directory'); + const dryRunOption = options.resolveInput('dry-run'); + const isDryRunEnabled = !!dryRunOption?.value; await askForMissingInformation(inputs, options); await generateApplicationFiles(inputs, options).catch(exit); - const shouldSkipInstall = options.some( - (option) => option.name === 'skip-install' && option.value === true, - ); - const shouldSkipGit = options.some( - (option) => option.name === 'skip-git' && option.value === true, - ); + const shouldSkipInstall = options.resolveInput('skip-install')?.value + const shouldSkipGit = options.resolveInput('skip-git')?.value; const projectDirectory = getProjectDirectory( getApplicationNameInput(inputs)!, directoryOption, @@ -67,9 +61,6 @@ export class NewAction extends AbstractAction { const getApplicationNameInput = (inputs: Input[]) => inputs.find((input) => input.name === 'name'); -const getPackageManagerInput = (inputs: Input[]) => - inputs.find((options) => options.name === 'packageManager'); - const getProjectDirectory = ( applicationName: Input, directoryOption?: Input, @@ -80,7 +71,7 @@ const getProjectDirectory = ( ); }; -const askForMissingInformation = async (inputs: Input[], options: Input[]) => { +const askForMissingInformation = async (inputs: Input[], options: CommandInputsContainer) => { console.info(MESSAGES.PROJECT_INFORMATION_START); console.info(); @@ -94,17 +85,20 @@ const askForMissingInformation = async (inputs: Input[], options: Input[]) => { replaceInputMissingInformation(inputs, answers); } - const packageManagerInput = getPackageManagerInput(options); - if (!packageManagerInput!.value) { + const packageManagerInput = options.resolveInput('packageManager') + if (!packageManagerInput?.value) { const answers = await askForPackageManager(); replaceInputMissingInformation(options, answers); } }; const replaceInputMissingInformation = ( - inputs: Input[], + inputs: Input[] | CommandInputsContainer, answers: Answers, ): Input[] => { + if (!Array.isArray(inputs)) { + inputs = inputs.toArray() + } return inputs.map( (input) => (input.value = @@ -112,15 +106,13 @@ const replaceInputMissingInformation = ( ); }; -const generateApplicationFiles = async (args: Input[], options: Input[]) => { - const collectionName = options.find( - (option) => option.name === 'collection' && option.value != null, - )!.value; +const generateApplicationFiles = async (args: Input[], options: CommandInputsContainer) => { + const collectionName = options.resolveInput('collection', true)?.value const collection: AbstractCollection = CollectionFactory.create( (collectionName as Collection) || Collection.NESTJS, ); const schematicOptions: SchematicOption[] = mapSchematicOptions( - args.concat(options), + args.concat(options.toArray()), ); await collection.execute('application', schematicOptions); console.info(); @@ -139,11 +131,11 @@ const mapSchematicOptions = (options: Input[]): SchematicOption[] => { }; const installPackages = async ( - options: Input[], + options: CommandInputsContainer, dryRunMode: boolean, installDirectory: string, ) => { - const inputPackageManager = getPackageManagerInput(options)!.value as string; + const inputPackageManager = options.resolveInput('packageManager', true).value; let packageManager: AbstractPackageManager; if (dryRunMode) { diff --git a/actions/start.action.ts b/actions/start.action.ts index a87599a13..a56824385 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process'; import * as fs from 'fs'; import { join } from 'path'; import * as killProcess from 'tree-kill'; -import { Input } from '../commands'; +import { Input, CommandInputsContainer } from '../commands'; import { getTscConfigPath } from '../lib/compiler/helpers/get-tsc-config.path'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { @@ -15,11 +15,9 @@ import { treeKillSync as killProcessSync } from '../lib/utils/tree-kill'; import { BuildAction } from './build.action'; export class StartAction extends BuildAction { - public async handle(commandInputs: Input[], commandOptions: Input[]) { + public async handle(commandInputs: Input[], commandOptions: CommandInputsContainer) { try { - const configFileName = commandOptions.find( - (option) => option.name === 'config', - )!.value as string; + const configFileName = commandOptions.resolveInput('config', true).value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.find((input) => input.name === 'app')! .value as string; @@ -30,20 +28,9 @@ export class StartAction extends BuildAction { appName, ); - const debugModeOption = commandOptions.find( - (option) => option.name === 'debug', - ); - const watchModeOption = commandOptions.find( - (option) => option.name === 'watch', - ); - const isWatchEnabled = !!(watchModeOption && watchModeOption.value); - const watchAssetsModeOption = commandOptions.find( - (option) => option.name === 'watchAssets', - ); - const isWatchAssetsEnabled = !!( - watchAssetsModeOption && watchAssetsModeOption.value - ); - const debugFlag = debugModeOption && debugModeOption.value; + const isWatchEnabled = !!commandOptions.resolveInput('watch')?.value; + const isWatchAssetsEnabled = !!commandOptions.resolveInput('watchAssets')?.value; + const debugFlag = commandOptions.resolveInput('debug')?.value; const binaryToRun = getValueOrDefault( configuration, 'exec', diff --git a/commands/add.command.ts b/commands/add.command.ts index a2006dbef..792a3a52b 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input } from './command.input'; +import { Input, CommandInputsContainer } from './command.input'; export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -17,10 +17,11 @@ export class AddCommand extends AbstractCommand { .option('-p, --project [project]', 'Project in which to generate files.') .usage(' [options] [library-specific-options]') .action(async (library: string, command: Command) => { - const options: Input[] = []; - options.push({ name: 'dry-run', value: !!command.dryRun }); - options.push({ name: 'skip-install', value: command.skipInstall }); - options.push({ + const commandOptions = new CommandInputsContainer(); + + commandOptions.addInput({ name: 'dry-run', value: !!command.dryRun }); + commandOptions.addInput({ name: 'skip-install', value: command.skipInstall }); + commandOptions.addInput({ name: 'project', value: command.project, }); @@ -30,7 +31,7 @@ export class AddCommand extends AbstractCommand { const flags = getRemainingFlags(program); try { - await this.action.handle(inputs, options, flags); + await this.action.handle(inputs, commandOptions, flags); } catch (err) { process.exit(1); } diff --git a/commands/build.command.ts b/commands/build.command.ts index cd40aab75..42bde0c0a 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { Input } from './command.input'; +import { Input, CommandInputsContainer } from './command.input'; export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -21,22 +21,22 @@ export class BuildCommand extends AbstractCommand { .option('--tsc', 'Use tsc for compilation.') .description('Build Nest application.') .action(async (app: string, command: Command) => { - const options: Input[] = []; + const commandOptions = new CommandInputsContainer() - options.push({ + commandOptions.addInput({ name: 'config', value: command.config, }); const isWebpackEnabled = command.tsc ? false : command.webpack; - options.push({ name: 'webpack', value: isWebpackEnabled }); - options.push({ name: 'watch', value: !!command.watch }); - options.push({ name: 'watchAssets', value: !!command.watchAssets }); - options.push({ + commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); + commandOptions.addInput({ name: 'watch', value: !!command.watch }); + commandOptions.addInput({ name: 'watchAssets', value: !!command.watchAssets }); + commandOptions.addInput({ name: 'path', value: command.path, }); - options.push({ + commandOptions.addInput({ name: 'webpackPath', value: command.webpackPath, }); @@ -51,7 +51,7 @@ export class BuildCommand extends AbstractCommand { ); return; } - options.push({ + commandOptions.addInput({ name: 'builder', value: command.builder, }); @@ -62,14 +62,14 @@ export class BuildCommand extends AbstractCommand { ` "typeCheck" will not have any effect when "builder" is not "swc".`, ); } - options.push({ + commandOptions.addInput({ name: 'typeCheck', value: command.typeCheck, }); const inputs: Input[] = []; inputs.push({ name: 'app', value: app }); - await this.action.handle(inputs, options); + await this.action.handle(inputs, commandOptions); }); } } diff --git a/commands/command.input.ts b/commands/command.input.ts index 3d490827f..79f82998f 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -1,5 +1,41 @@ -export interface Input { +export interface Input { name: string; - value: boolean | string; + value: TValue; options?: any; } + +export class CommandInputsContainer { + private readonly inputsByName = new Map(); + + toArray(): Input[] { + return Array.from(this.inputsByName.values()) + } + + addInput(input: Input) { + this.inputsByName.set(input.name, input); + } + + resolveInput( + inputName: Input['name'], + ): Input | undefined; + resolveInput( + inputName: Input['name'], + errorOnMissing: false, + ): Input | undefined; + resolveInput( + inputName: Input['name'], + errorOnMissing: true, + ): Input; + resolveInput( + inputName: Input['name'], + errorOnMissing = false + ): Input | undefined { + const input = this.inputsByName.get(inputName) as Input | undefined; + if (errorOnMissing) { + if (!input) { + throw new Error(`The input ${inputName} is missing!`); + } + } + return input + } +} diff --git a/commands/generate.command.ts b/commands/generate.command.ts index d6613afe5..a5e638462 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -5,7 +5,7 @@ import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; -import { Input } from './command.input'; +import { Input, CommandInputsContainer } from './command.input'; export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { @@ -55,14 +55,15 @@ export class GenerateCommand extends AbstractCommand { path: string, command: Command, ) => { - const options: Input[] = []; - options.push({ name: 'dry-run', value: !!command.dryRun }); + const commandOptions = new CommandInputsContainer(); + + commandOptions.addInput({ name: 'dry-run', value: !!command.dryRun }); if (command.flat !== undefined) { - options.push({ name: 'flat', value: command.flat }); + commandOptions.addInput({ name: 'flat', value: command.flat }); } - options.push({ + commandOptions.addInput({ name: 'spec', value: typeof command.spec === 'boolean' @@ -75,20 +76,20 @@ export class GenerateCommand extends AbstractCommand { : command.spec.passedAsInput, }, }); - options.push({ + commandOptions.addInput({ name: 'specFileSuffix', value: command.specFileSuffix, }); - options.push({ + commandOptions.addInput({ name: 'collection', value: command.collection, }); - options.push({ + commandOptions.addInput({ name: 'project', value: command.project, }); - options.push({ + commandOptions.addInput({ name: 'skipImport', value: command.skipImport, }); @@ -98,7 +99,7 @@ export class GenerateCommand extends AbstractCommand { inputs.push({ name: 'name', value: name }); inputs.push({ name: 'path', value: path }); - await this.action.handle(inputs, options); + await this.action.handle(inputs, commandOptions); }, ); } diff --git a/commands/new.command.ts b/commands/new.command.ts index 28b595e1e..f549282a1 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; -import { Input } from './command.input'; +import { Input, CommandInputsContainer } from './command.input'; export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { @@ -33,19 +33,20 @@ export class NewCommand extends AbstractCommand { ) .option('--strict', 'Enables strict mode in TypeScript.', false) .action(async (name: string, command: Command) => { - const options: Input[] = []; - const availableLanguages = ['js', 'ts', 'javascript', 'typescript']; - options.push({ name: 'directory', value: command.directory }); - options.push({ name: 'dry-run', value: command.dryRun }); - options.push({ name: 'skip-git', value: command.skipGit }); - options.push({ name: 'skip-install', value: command.skipInstall }); - options.push({ name: 'strict', value: command.strict }); - options.push({ + const commandOptions = new CommandInputsContainer(); + + commandOptions.addInput({ name: 'directory', value: command.directory }); + commandOptions.addInput({ name: 'dry-run', value: command.dryRun }); + commandOptions.addInput({ name: 'skip-git', value: command.skipGit }); + commandOptions.addInput({ name: 'skip-install', value: command.skipInstall }); + commandOptions.addInput({ name: 'strict', value: command.strict }); + commandOptions.addInput({ name: 'packageManager', value: command.packageManager, }); - options.push({ name: 'collection', value: command.collection }); + commandOptions.addInput({ name: 'collection', value: command.collection }); + const availableLanguages = ['js', 'ts', 'javascript', 'typescript']; if (!!command.language) { const lowercasedLanguage = command.language.toLowerCase(); const langMatch = availableLanguages.includes(lowercasedLanguage); @@ -66,7 +67,7 @@ export class NewCommand extends AbstractCommand { break; } } - options.push({ + commandOptions.addInput({ name: 'language', value: command.language, }); @@ -74,7 +75,7 @@ export class NewCommand extends AbstractCommand { const inputs: Input[] = []; inputs.push({ name: 'name', value: name }); - await this.action.handle(inputs, options); + await this.action.handle(inputs, commandOptions); }); } } diff --git a/commands/start.command.ts b/commands/start.command.ts index 1d4f256b2..f95e9265d 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input } from './command.input'; +import { Input, CommandInputsContainer } from './command.input'; import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { @@ -40,39 +40,39 @@ export class StartCommand extends AbstractCommand { ) .description('Run Nest application.') .action(async (app: string, command: Command) => { - const options: Input[] = []; + const commandOptions = new CommandInputsContainer(); - options.push({ + commandOptions.addInput({ name: 'config', value: command.config, - }); + }) const isWebpackEnabled = command.tsc ? false : command.webpack; - options.push({ name: 'webpack', value: isWebpackEnabled }); - options.push({ name: 'debug', value: command.debug }); - options.push({ name: 'watch', value: !!command.watch }); - options.push({ name: 'watchAssets', value: !!command.watchAssets }); - options.push({ + commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); + commandOptions.addInput({ name: 'debug', value: command.debug }); + commandOptions.addInput({ name: 'watch', value: !!command.watch }); + commandOptions.addInput({ name: 'watchAssets', value: !!command.watchAssets }); + commandOptions.addInput({ name: 'path', value: command.path, }); - options.push({ + commandOptions.addInput({ name: 'webpackPath', value: command.webpackPath, }); - options.push({ + commandOptions.addInput({ name: 'exec', value: command.exec, }); - options.push({ + commandOptions.addInput({ name: 'sourceRoot', value: command.sourceRoot, }); - options.push({ + commandOptions.addInput({ name: 'entryFile', value: command.entryFile, }); - options.push({ + commandOptions.addInput({ name: 'preserveWatchOutput', value: !!command.preserveWatchOutput && @@ -90,7 +90,7 @@ export class StartCommand extends AbstractCommand { ); return; } - options.push({ + commandOptions.addInput({ name: 'builder', value: command.builder, }); @@ -101,7 +101,7 @@ export class StartCommand extends AbstractCommand { ` "typeCheck" will not have any effect when "builder" is not "swc".`, ); } - options.push({ + commandOptions.addInput({ name: 'typeCheck', value: command.typeCheck, }); @@ -111,7 +111,7 @@ export class StartCommand extends AbstractCommand { const flags = getRemainingFlags(program); try { - await this.action.handle(inputs, options, flags); + await this.action.handle(inputs, commandOptions, flags); } catch (err) { process.exit(1); } diff --git a/lib/compiler/helpers/get-builder.ts b/lib/compiler/helpers/get-builder.ts index 316513136..bd1250525 100644 --- a/lib/compiler/helpers/get-builder.ts +++ b/lib/compiler/helpers/get-builder.ts @@ -1,4 +1,4 @@ -import { Input } from '../../../commands'; +import { CommandInputsContainer } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getBuilder( configuration: Required, - cmdOptions: Input[], + cmdOptions: CommandInputsContainer, appName: string, ) { const builderValue = getValueOrDefault( diff --git a/lib/compiler/helpers/get-tsc-config.path.ts b/lib/compiler/helpers/get-tsc-config.path.ts index 04dee58f6..a31583713 100644 --- a/lib/compiler/helpers/get-tsc-config.path.ts +++ b/lib/compiler/helpers/get-tsc-config.path.ts @@ -1,4 +1,4 @@ -import { Input } from '../../../commands'; +import { CommandInputsContainer } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getDefaultTsconfigPath } from '../../utils/get-default-tsconfig-path'; import { getValueOrDefault } from './get-value-or-default'; @@ -12,7 +12,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getTscConfigPath( configuration: Required, - cmdOptions: Input[], + cmdOptions: CommandInputsContainer, appName: string, ) { let tsconfigPath = getValueOrDefault( diff --git a/lib/compiler/helpers/get-value-or-default.ts b/lib/compiler/helpers/get-value-or-default.ts index 9b0b71c14..157373f01 100644 --- a/lib/compiler/helpers/get-value-or-default.ts +++ b/lib/compiler/helpers/get-value-or-default.ts @@ -1,4 +1,4 @@ -import { Input } from '../../../commands'; +import { Input, CommandInputsContainer } from '../../../commands'; import { Configuration } from '../../configuration'; export function getValueOrDefault( @@ -14,13 +14,15 @@ export function getValueOrDefault( | 'exec' | 'builder' | 'typeCheck', - options: Input[] = [], + options: Input[] | CommandInputsContainer = [], defaultValue?: T, ): T { - const item = options.find((option) => option.name === key); - const origValue = item && (item.value as unknown as T); + const item = Array.isArray(options) + ? options.find((option) => option.name === key) + : key && options.resolveInput(key) + const origValue = item?.value as T | undefined; if (origValue !== undefined && origValue !== null) { - return origValue as T; + return origValue; } if (configuration.projects && configuration.projects[appName as string]) { // Wrap the application name in double-quotes to prevent splitting it diff --git a/lib/compiler/helpers/get-webpack-config-path.ts b/lib/compiler/helpers/get-webpack-config-path.ts index 08e8a58ca..bc4f3be90 100644 --- a/lib/compiler/helpers/get-webpack-config-path.ts +++ b/lib/compiler/helpers/get-webpack-config-path.ts @@ -1,4 +1,4 @@ -import { Input } from '../../../commands'; +import { CommandInputsContainer } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getWebpackConfigPath( configuration: Required, - cmdOptions: Input[], + cmdOptions: CommandInputsContainer, appName: string, ) { let webpackPath = getValueOrDefault( diff --git a/lib/compiler/webpack-compiler.ts b/lib/compiler/webpack-compiler.ts index db125e182..5072503ce 100644 --- a/lib/compiler/webpack-compiler.ts +++ b/lib/compiler/webpack-compiler.ts @@ -1,6 +1,6 @@ import { existsSync } from 'fs'; import { join } from 'path'; -import { Input } from '../../commands'; +import { CommandInputsContainer } from '../../commands'; import { Configuration } from '../configuration'; import { INFO_PREFIX } from '../ui'; import { AssetsManager } from './assets-manager'; @@ -20,7 +20,7 @@ type WebpackConfigFactoryOrConfig = | webpack.Configuration; type WebpackCompilerExtras = { - inputs: Input[]; + inputs: CommandInputsContainer; assetsManager: AssetsManager; webpackConfigFactoryOrConfig: | WebpackConfigFactoryOrConfig diff --git a/lib/utils/project-utils.ts b/lib/utils/project-utils.ts index 5c4c6fbf9..dd8f4252a 100644 --- a/lib/utils/project-utils.ts +++ b/lib/utils/project-utils.ts @@ -1,6 +1,6 @@ import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; -import { Input } from '../../commands'; +import { CommandInputsContainer } from '../../commands'; import { getValueOrDefault } from '../compiler/helpers/get-value-or-default'; import { Configuration, ProjectConfiguration } from '../configuration'; import { generateSelect } from '../questions/questions'; @@ -142,11 +142,9 @@ export function moveDefaultProjectToStart( export function hasValidOptionFlag( queriedOptionName: string, - options: Input[], + options: CommandInputsContainer, queriedValue: string | number | boolean = true, ): boolean { - return options.some( - (option: Input) => - option.name === queriedOptionName && option.value === queriedValue, - ); + const option = options.resolveInput(queriedOptionName); + return option?.value === queriedValue; } From 4aa56a5197de874d6a6c09e5f5d9fdc1e8c7f8bf Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sun, 16 Jul 2023 22:33:34 -0400 Subject: [PATCH 03/17] style: fix formatting --- actions/add.action.ts | 8 +++++-- actions/build.action.ts | 16 +++++++++---- actions/new.action.ts | 25 ++++++++++++++------ actions/start.action.ts | 16 +++++++++---- commands/add.command.ts | 5 +++- commands/build.command.ts | 7 ++++-- commands/command.input.ts | 6 ++--- commands/new.command.ts | 15 +++++++++--- commands/start.command.ts | 7 ++++-- lib/compiler/helpers/get-value-or-default.ts | 2 +- 10 files changed, 78 insertions(+), 29 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 08c7924e8..ea06f7565 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -23,7 +23,11 @@ import { AbstractAction } from './abstract.action'; const schematicName = 'nest-add'; export class AddAction extends AbstractAction { - public async handle(inputs: Input[], options: CommandInputsContainer, extraFlags: string[]) { + public async handle( + inputs: Input[], + options: CommandInputsContainer, + extraFlags: string[], + ) { const libraryName = this.getLibraryName(inputs); const packageName = this.getPackageName(libraryName); const collectionName = this.getCollectionName(libraryName, packageName); @@ -36,7 +40,7 @@ export class AddAction extends AbstractAction { inputs.concat(options.toArray()), ); if (sourceRootOption) { - options.addInput(sourceRootOption) + options.addInput(sourceRootOption); } await this.addLibrary(collectionName, options, extraFlags); diff --git a/actions/build.action.ts b/actions/build.action.ts index e736569d5..2165b645b 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -40,10 +40,14 @@ export class BuildAction extends AbstractAction { protected readonly assetsManager = new AssetsManager(); protected readonly workspaceUtils = new WorkspaceUtils(); - public async handle(commandInputs: Input[], commandOptions: CommandInputsContainer) { + public async handle( + commandInputs: Input[], + commandOptions: CommandInputsContainer, + ) { try { const watchMode = !!commandOptions.resolveInput('watch')?.value; - const watchAssetsMode = !!commandOptions.resolveInput('watchAssets')?.value; + const watchAssetsMode = + !!commandOptions.resolveInput('watchAssets')?.value; await this.runBuild( commandInputs, @@ -69,7 +73,10 @@ export class BuildAction extends AbstractAction { isDebugEnabled = false, onSuccess?: () => void, ) { - const configFileName = commandOptions.resolveInput('config', true).value + const configFileName = commandOptions.resolveInput( + 'config', + true, + ).value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.find((input) => input.name === 'app')! .value as string; @@ -217,7 +224,8 @@ export class BuildAction extends AbstractAction { this.tsConfigProvider, this.tsLoader, ); - const isPreserveWatchOutputEnabled = options.resolveInput('preserveWatchOutput')?.value || false + const isPreserveWatchOutputEnabled = + options.resolveInput('preserveWatchOutput')?.value || false; watchCompiler.run( configuration, pathToTsconfig, diff --git a/actions/new.action.ts b/actions/new.action.ts index 6e78fbbf9..604cf0f4b 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -32,7 +32,8 @@ export class NewAction extends AbstractAction { await askForMissingInformation(inputs, options); await generateApplicationFiles(inputs, options).catch(exit); - const shouldSkipInstall = options.resolveInput('skip-install')?.value + const shouldSkipInstall = + options.resolveInput('skip-install')?.value; const shouldSkipGit = options.resolveInput('skip-git')?.value; const projectDirectory = getProjectDirectory( getApplicationNameInput(inputs)!, @@ -71,7 +72,10 @@ const getProjectDirectory = ( ); }; -const askForMissingInformation = async (inputs: Input[], options: CommandInputsContainer) => { +const askForMissingInformation = async ( + inputs: Input[], + options: CommandInputsContainer, +) => { console.info(MESSAGES.PROJECT_INFORMATION_START); console.info(); @@ -85,7 +89,7 @@ const askForMissingInformation = async (inputs: Input[], options: CommandInputsC replaceInputMissingInformation(inputs, answers); } - const packageManagerInput = options.resolveInput('packageManager') + const packageManagerInput = options.resolveInput('packageManager'); if (!packageManagerInput?.value) { const answers = await askForPackageManager(); replaceInputMissingInformation(options, answers); @@ -97,7 +101,7 @@ const replaceInputMissingInformation = ( answers: Answers, ): Input[] => { if (!Array.isArray(inputs)) { - inputs = inputs.toArray() + inputs = inputs.toArray(); } return inputs.map( (input) => @@ -106,8 +110,12 @@ const replaceInputMissingInformation = ( ); }; -const generateApplicationFiles = async (args: Input[], options: CommandInputsContainer) => { - const collectionName = options.resolveInput('collection', true)?.value +const generateApplicationFiles = async ( + args: Input[], + options: CommandInputsContainer, +) => { + const collectionName = options.resolveInput('collection', true) + ?.value; const collection: AbstractCollection = CollectionFactory.create( (collectionName as Collection) || Collection.NESTJS, ); @@ -135,7 +143,10 @@ const installPackages = async ( dryRunMode: boolean, installDirectory: string, ) => { - const inputPackageManager = options.resolveInput('packageManager', true).value; + const inputPackageManager = options.resolveInput( + 'packageManager', + true, + ).value; let packageManager: AbstractPackageManager; if (dryRunMode) { diff --git a/actions/start.action.ts b/actions/start.action.ts index a56824385..6e4f89e32 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -15,9 +15,15 @@ import { treeKillSync as killProcessSync } from '../lib/utils/tree-kill'; import { BuildAction } from './build.action'; export class StartAction extends BuildAction { - public async handle(commandInputs: Input[], commandOptions: CommandInputsContainer) { + public async handle( + commandInputs: Input[], + commandOptions: CommandInputsContainer, + ) { try { - const configFileName = commandOptions.resolveInput('config', true).value; + const configFileName = commandOptions.resolveInput( + 'config', + true, + ).value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.find((input) => input.name === 'app')! .value as string; @@ -28,8 +34,10 @@ export class StartAction extends BuildAction { appName, ); - const isWatchEnabled = !!commandOptions.resolveInput('watch')?.value; - const isWatchAssetsEnabled = !!commandOptions.resolveInput('watchAssets')?.value; + const isWatchEnabled = + !!commandOptions.resolveInput('watch')?.value; + const isWatchAssetsEnabled = + !!commandOptions.resolveInput('watchAssets')?.value; const debugFlag = commandOptions.resolveInput('debug')?.value; const binaryToRun = getValueOrDefault( configuration, diff --git a/commands/add.command.ts b/commands/add.command.ts index 792a3a52b..9736e68cc 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -20,7 +20,10 @@ export class AddCommand extends AbstractCommand { const commandOptions = new CommandInputsContainer(); commandOptions.addInput({ name: 'dry-run', value: !!command.dryRun }); - commandOptions.addInput({ name: 'skip-install', value: command.skipInstall }); + commandOptions.addInput({ + name: 'skip-install', + value: command.skipInstall, + }); commandOptions.addInput({ name: 'project', value: command.project, diff --git a/commands/build.command.ts b/commands/build.command.ts index 42bde0c0a..3b045aff5 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -21,7 +21,7 @@ export class BuildCommand extends AbstractCommand { .option('--tsc', 'Use tsc for compilation.') .description('Build Nest application.') .action(async (app: string, command: Command) => { - const commandOptions = new CommandInputsContainer() + const commandOptions = new CommandInputsContainer(); commandOptions.addInput({ name: 'config', @@ -31,7 +31,10 @@ export class BuildCommand extends AbstractCommand { const isWebpackEnabled = command.tsc ? false : command.webpack; commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); commandOptions.addInput({ name: 'watch', value: !!command.watch }); - commandOptions.addInput({ name: 'watchAssets', value: !!command.watchAssets }); + commandOptions.addInput({ + name: 'watchAssets', + value: !!command.watchAssets, + }); commandOptions.addInput({ name: 'path', value: command.path, diff --git a/commands/command.input.ts b/commands/command.input.ts index 79f82998f..832858b8c 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -8,7 +8,7 @@ export class CommandInputsContainer { private readonly inputsByName = new Map(); toArray(): Input[] { - return Array.from(this.inputsByName.values()) + return Array.from(this.inputsByName.values()); } addInput(input: Input) { @@ -28,7 +28,7 @@ export class CommandInputsContainer { ): Input; resolveInput( inputName: Input['name'], - errorOnMissing = false + errorOnMissing = false, ): Input | undefined { const input = this.inputsByName.get(inputName) as Input | undefined; if (errorOnMissing) { @@ -36,6 +36,6 @@ export class CommandInputsContainer { throw new Error(`The input ${inputName} is missing!`); } } - return input + return input; } } diff --git a/commands/new.command.ts b/commands/new.command.ts index f549282a1..923dc029a 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -35,16 +35,25 @@ export class NewCommand extends AbstractCommand { .action(async (name: string, command: Command) => { const commandOptions = new CommandInputsContainer(); - commandOptions.addInput({ name: 'directory', value: command.directory }); + commandOptions.addInput({ + name: 'directory', + value: command.directory, + }); commandOptions.addInput({ name: 'dry-run', value: command.dryRun }); commandOptions.addInput({ name: 'skip-git', value: command.skipGit }); - commandOptions.addInput({ name: 'skip-install', value: command.skipInstall }); + commandOptions.addInput({ + name: 'skip-install', + value: command.skipInstall, + }); commandOptions.addInput({ name: 'strict', value: command.strict }); commandOptions.addInput({ name: 'packageManager', value: command.packageManager, }); - commandOptions.addInput({ name: 'collection', value: command.collection }); + commandOptions.addInput({ + name: 'collection', + value: command.collection, + }); const availableLanguages = ['js', 'ts', 'javascript', 'typescript']; if (!!command.language) { diff --git a/commands/start.command.ts b/commands/start.command.ts index f95e9265d..150ea7378 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -45,13 +45,16 @@ export class StartCommand extends AbstractCommand { commandOptions.addInput({ name: 'config', value: command.config, - }) + }); const isWebpackEnabled = command.tsc ? false : command.webpack; commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); commandOptions.addInput({ name: 'debug', value: command.debug }); commandOptions.addInput({ name: 'watch', value: !!command.watch }); - commandOptions.addInput({ name: 'watchAssets', value: !!command.watchAssets }); + commandOptions.addInput({ + name: 'watchAssets', + value: !!command.watchAssets, + }); commandOptions.addInput({ name: 'path', value: command.path, diff --git a/lib/compiler/helpers/get-value-or-default.ts b/lib/compiler/helpers/get-value-or-default.ts index 157373f01..c7e77d672 100644 --- a/lib/compiler/helpers/get-value-or-default.ts +++ b/lib/compiler/helpers/get-value-or-default.ts @@ -19,7 +19,7 @@ export function getValueOrDefault( ): T { const item = Array.isArray(options) ? options.find((option) => option.name === key) - : key && options.resolveInput(key) + : key && options.resolveInput(key); const origValue = item?.value as T | undefined; if (origValue !== undefined && origValue !== null) { return origValue; From ffb3c1930c4788079667315a6778a9645cad9f7d Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 11:26:33 -0400 Subject: [PATCH 04/17] refactor: rename input container --- actions/abstract.action.ts | 4 +-- actions/add.action.ts | 10 +++---- actions/build.action.ts | 20 ++++++------- actions/generate.action.ts | 4 +-- actions/new.action.ts | 26 ++++++++-------- actions/start.action.ts | 12 ++++---- commands/add.command.ts | 10 +++---- commands/build.command.ts | 20 ++++++------- commands/command.input.ts | 12 ++++---- commands/generate.command.ts | 18 +++++------ commands/new.command.ts | 20 ++++++------- commands/start.command.ts | 30 +++++++++---------- lib/compiler/helpers/get-builder.ts | 4 +-- lib/compiler/helpers/get-tsc-config.path.ts | 4 +-- lib/compiler/helpers/get-value-or-default.ts | 6 ++-- .../helpers/get-webpack-config-path.ts | 4 +-- lib/compiler/webpack-compiler.ts | 4 +-- lib/utils/project-utils.ts | 6 ++-- 18 files changed, 107 insertions(+), 107 deletions(-) diff --git a/actions/abstract.action.ts b/actions/abstract.action.ts index ca8bd5825..32d49b797 100644 --- a/actions/abstract.action.ts +++ b/actions/abstract.action.ts @@ -1,9 +1,9 @@ -import { Input, CommandInputsContainer } from '../commands'; +import { Input, CommandStorage } from '../commands'; export abstract class AbstractAction { public abstract handle( inputs?: Input[], - options?: CommandInputsContainer, + options?: CommandStorage, extraFlags?: string[], ): Promise; } diff --git a/actions/add.action.ts b/actions/add.action.ts index ea06f7565..d46852301 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -1,5 +1,5 @@ import * as chalk from 'chalk'; -import { CommandInputsContainer, Input } from '../commands'; +import { CommandStorage, Input } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractPackageManager, @@ -25,7 +25,7 @@ const schematicName = 'nest-add'; export class AddAction extends AbstractAction { public async handle( inputs: Input[], - options: CommandInputsContainer, + options: CommandStorage, extraFlags: string[], ) { const libraryName = this.getLibraryName(inputs); @@ -40,7 +40,7 @@ export class AddAction extends AbstractAction { inputs.concat(options.toArray()), ); if (sourceRootOption) { - options.addInput(sourceRootOption); + options.add(sourceRootOption); } await this.addLibrary(collectionName, options, extraFlags); @@ -123,7 +123,7 @@ export class AddAction extends AbstractAction { private async addLibrary( collectionName: string, - options: CommandInputsContainer, + options: CommandStorage, extraFlags: string[], ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); @@ -131,7 +131,7 @@ export class AddAction extends AbstractAction { schematicOptions.push( new SchematicOption( 'sourceRoot', - options.resolveInput('sourceRoot', true).value, + options.get('sourceRoot', true).value, ), ); const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; diff --git a/actions/build.action.ts b/actions/build.action.ts index 2165b645b..99344f57d 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -1,7 +1,7 @@ import * as chalk from 'chalk'; import { join } from 'path'; import * as ts from 'typescript'; -import { Input, CommandInputsContainer } from '../commands'; +import { Input, CommandStorage } from '../commands'; import { AssetsManager } from '../lib/compiler/assets-manager'; import { Compiler } from '../lib/compiler/compiler'; import { getBuilder } from '../lib/compiler/helpers/get-builder'; @@ -42,12 +42,12 @@ export class BuildAction extends AbstractAction { public async handle( commandInputs: Input[], - commandOptions: CommandInputsContainer, + commandOptions: CommandStorage, ) { try { - const watchMode = !!commandOptions.resolveInput('watch')?.value; + const watchMode = !!commandOptions.get('watch')?.value; const watchAssetsMode = - !!commandOptions.resolveInput('watchAssets')?.value; + !!commandOptions.get('watchAssets')?.value; await this.runBuild( commandInputs, @@ -67,13 +67,13 @@ export class BuildAction extends AbstractAction { public async runBuild( commandInputs: Input[], - commandOptions: CommandInputsContainer, + commandOptions: CommandStorage, watchMode: boolean, watchAssetsMode: boolean, isDebugEnabled = false, onSuccess?: () => void, ) { - const configFileName = commandOptions.resolveInput( + const configFileName = commandOptions.get( 'config', true, ).value; @@ -151,7 +151,7 @@ export class BuildAction extends AbstractAction { appName: string, pathToTsconfig: string, watchMode: boolean, - options: CommandInputsContainer, + options: CommandStorage, tsOptions: ts.CompilerOptions, onSuccess: (() => void) | undefined, ) { @@ -179,7 +179,7 @@ export class BuildAction extends AbstractAction { private runWebpack( configuration: Required, appName: string, - commandOptions: CommandInputsContainer, + commandOptions: CommandStorage, pathToTsconfig: string, debug: boolean, watchMode: boolean, @@ -212,7 +212,7 @@ export class BuildAction extends AbstractAction { private runTsc( watchMode: boolean, - options: CommandInputsContainer, + options: CommandStorage, configuration: Required, pathToTsconfig: string, appName: string, @@ -225,7 +225,7 @@ export class BuildAction extends AbstractAction { this.tsLoader, ); const isPreserveWatchOutputEnabled = - options.resolveInput('preserveWatchOutput')?.value || false; + options.get('preserveWatchOutput')?.value || false; watchCompiler.run( configuration, pathToTsconfig, diff --git a/actions/generate.action.ts b/actions/generate.action.ts index c412737df..07c4ed7e3 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk'; import { Answers } from 'inquirer'; -import { Input, CommandInputsContainer } from '../commands'; +import { Input, CommandStorage } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractCollection, @@ -21,7 +21,7 @@ import { import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { - public async handle(inputs: Input[], options: CommandInputsContainer) { + public async handle(inputs: Input[], options: CommandStorage) { await generateFiles(inputs.concat(options.toArray())); } } diff --git a/actions/new.action.ts b/actions/new.action.ts index 604cf0f4b..23ee720ec 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; import { join } from 'path'; -import { CommandInputsContainer, Input } from '../commands'; +import { CommandStorage, Input } from '../commands'; import { defaultGitIgnore } from '../lib/configuration/defaults'; import { AbstractPackageManager, @@ -24,17 +24,17 @@ import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { - public async handle(inputs: Input[], options: CommandInputsContainer) { - const directoryOption = options.resolveInput('directory'); - const dryRunOption = options.resolveInput('dry-run'); + public async handle(inputs: Input[], options: CommandStorage) { + const directoryOption = options.get('directory'); + const dryRunOption = options.get('dry-run'); const isDryRunEnabled = !!dryRunOption?.value; await askForMissingInformation(inputs, options); await generateApplicationFiles(inputs, options).catch(exit); const shouldSkipInstall = - options.resolveInput('skip-install')?.value; - const shouldSkipGit = options.resolveInput('skip-git')?.value; + options.get('skip-install')?.value; + const shouldSkipGit = options.get('skip-git')?.value; const projectDirectory = getProjectDirectory( getApplicationNameInput(inputs)!, directoryOption, @@ -74,7 +74,7 @@ const getProjectDirectory = ( const askForMissingInformation = async ( inputs: Input[], - options: CommandInputsContainer, + options: CommandStorage, ) => { console.info(MESSAGES.PROJECT_INFORMATION_START); console.info(); @@ -89,7 +89,7 @@ const askForMissingInformation = async ( replaceInputMissingInformation(inputs, answers); } - const packageManagerInput = options.resolveInput('packageManager'); + const packageManagerInput = options.get('packageManager'); if (!packageManagerInput?.value) { const answers = await askForPackageManager(); replaceInputMissingInformation(options, answers); @@ -97,7 +97,7 @@ const askForMissingInformation = async ( }; const replaceInputMissingInformation = ( - inputs: Input[] | CommandInputsContainer, + inputs: Input[] | CommandStorage, answers: Answers, ): Input[] => { if (!Array.isArray(inputs)) { @@ -112,9 +112,9 @@ const replaceInputMissingInformation = ( const generateApplicationFiles = async ( args: Input[], - options: CommandInputsContainer, + options: CommandStorage, ) => { - const collectionName = options.resolveInput('collection', true) + const collectionName = options.get('collection', true) ?.value; const collection: AbstractCollection = CollectionFactory.create( (collectionName as Collection) || Collection.NESTJS, @@ -139,11 +139,11 @@ const mapSchematicOptions = (options: Input[]): SchematicOption[] => { }; const installPackages = async ( - options: CommandInputsContainer, + options: CommandStorage, dryRunMode: boolean, installDirectory: string, ) => { - const inputPackageManager = options.resolveInput( + const inputPackageManager = options.get( 'packageManager', true, ).value; diff --git a/actions/start.action.ts b/actions/start.action.ts index 6e4f89e32..58f967a74 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process'; import * as fs from 'fs'; import { join } from 'path'; import * as killProcess from 'tree-kill'; -import { Input, CommandInputsContainer } from '../commands'; +import { Input, CommandStorage } from '../commands'; import { getTscConfigPath } from '../lib/compiler/helpers/get-tsc-config.path'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { @@ -17,10 +17,10 @@ import { BuildAction } from './build.action'; export class StartAction extends BuildAction { public async handle( commandInputs: Input[], - commandOptions: CommandInputsContainer, + commandOptions: CommandStorage, ) { try { - const configFileName = commandOptions.resolveInput( + const configFileName = commandOptions.get( 'config', true, ).value; @@ -35,10 +35,10 @@ export class StartAction extends BuildAction { ); const isWatchEnabled = - !!commandOptions.resolveInput('watch')?.value; + !!commandOptions.get('watch')?.value; const isWatchAssetsEnabled = - !!commandOptions.resolveInput('watchAssets')?.value; - const debugFlag = commandOptions.resolveInput('debug')?.value; + !!commandOptions.get('watchAssets')?.value; + const debugFlag = commandOptions.get('debug')?.value; const binaryToRun = getValueOrDefault( configuration, 'exec', diff --git a/commands/add.command.ts b/commands/add.command.ts index 9736e68cc..27f82b8f4 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandInputsContainer } from './command.input'; +import { Input, CommandStorage } from './command.input'; export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -17,14 +17,14 @@ export class AddCommand extends AbstractCommand { .option('-p, --project [project]', 'Project in which to generate files.') .usage(' [options] [library-specific-options]') .action(async (library: string, command: Command) => { - const commandOptions = new CommandInputsContainer(); + const commandOptions = new CommandStorage(); - commandOptions.addInput({ name: 'dry-run', value: !!command.dryRun }); - commandOptions.addInput({ + commandOptions.add({ name: 'dry-run', value: !!command.dryRun }); + commandOptions.add({ name: 'skip-install', value: command.skipInstall, }); - commandOptions.addInput({ + commandOptions.add({ name: 'project', value: command.project, }); diff --git a/commands/build.command.ts b/commands/build.command.ts index 3b045aff5..3f2f3ae45 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandInputsContainer } from './command.input'; +import { Input, CommandStorage } from './command.input'; export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -21,25 +21,25 @@ export class BuildCommand extends AbstractCommand { .option('--tsc', 'Use tsc for compilation.') .description('Build Nest application.') .action(async (app: string, command: Command) => { - const commandOptions = new CommandInputsContainer(); + const commandOptions = new CommandStorage(); - commandOptions.addInput({ + commandOptions.add({ name: 'config', value: command.config, }); const isWebpackEnabled = command.tsc ? false : command.webpack; - commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); - commandOptions.addInput({ name: 'watch', value: !!command.watch }); - commandOptions.addInput({ + commandOptions.add({ name: 'webpack', value: isWebpackEnabled }); + commandOptions.add({ name: 'watch', value: !!command.watch }); + commandOptions.add({ name: 'watchAssets', value: !!command.watchAssets, }); - commandOptions.addInput({ + commandOptions.add({ name: 'path', value: command.path, }); - commandOptions.addInput({ + commandOptions.add({ name: 'webpackPath', value: command.webpackPath, }); @@ -54,7 +54,7 @@ export class BuildCommand extends AbstractCommand { ); return; } - commandOptions.addInput({ + commandOptions.add({ name: 'builder', value: command.builder, }); @@ -65,7 +65,7 @@ export class BuildCommand extends AbstractCommand { ` "typeCheck" will not have any effect when "builder" is not "swc".`, ); } - commandOptions.addInput({ + commandOptions.add({ name: 'typeCheck', value: command.typeCheck, }); diff --git a/commands/command.input.ts b/commands/command.input.ts index 832858b8c..733eac116 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -4,29 +4,29 @@ export interface Input { options?: any; } -export class CommandInputsContainer { +export class CommandStorage { private readonly inputsByName = new Map(); toArray(): Input[] { return Array.from(this.inputsByName.values()); } - addInput(input: Input) { + add(input: Input) { this.inputsByName.set(input.name, input); } - resolveInput( + get( inputName: Input['name'], ): Input | undefined; - resolveInput( + get( inputName: Input['name'], errorOnMissing: false, ): Input | undefined; - resolveInput( + get( inputName: Input['name'], errorOnMissing: true, ): Input; - resolveInput( + get( inputName: Input['name'], errorOnMissing = false, ): Input | undefined { diff --git a/commands/generate.command.ts b/commands/generate.command.ts index a5e638462..2f0e637b3 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -5,7 +5,7 @@ import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandInputsContainer } from './command.input'; +import { Input, CommandStorage } from './command.input'; export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { @@ -55,15 +55,15 @@ export class GenerateCommand extends AbstractCommand { path: string, command: Command, ) => { - const commandOptions = new CommandInputsContainer(); + const commandOptions = new CommandStorage(); - commandOptions.addInput({ name: 'dry-run', value: !!command.dryRun }); + commandOptions.add({ name: 'dry-run', value: !!command.dryRun }); if (command.flat !== undefined) { - commandOptions.addInput({ name: 'flat', value: command.flat }); + commandOptions.add({ name: 'flat', value: command.flat }); } - commandOptions.addInput({ + commandOptions.add({ name: 'spec', value: typeof command.spec === 'boolean' @@ -76,20 +76,20 @@ export class GenerateCommand extends AbstractCommand { : command.spec.passedAsInput, }, }); - commandOptions.addInput({ + commandOptions.add({ name: 'specFileSuffix', value: command.specFileSuffix, }); - commandOptions.addInput({ + commandOptions.add({ name: 'collection', value: command.collection, }); - commandOptions.addInput({ + commandOptions.add({ name: 'project', value: command.project, }); - commandOptions.addInput({ + commandOptions.add({ name: 'skipImport', value: command.skipImport, }); diff --git a/commands/new.command.ts b/commands/new.command.ts index 923dc029a..550c5eb2d 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandInputsContainer } from './command.input'; +import { Input, CommandStorage } from './command.input'; export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { @@ -33,24 +33,24 @@ export class NewCommand extends AbstractCommand { ) .option('--strict', 'Enables strict mode in TypeScript.', false) .action(async (name: string, command: Command) => { - const commandOptions = new CommandInputsContainer(); + const commandOptions = new CommandStorage(); - commandOptions.addInput({ + commandOptions.add({ name: 'directory', value: command.directory, }); - commandOptions.addInput({ name: 'dry-run', value: command.dryRun }); - commandOptions.addInput({ name: 'skip-git', value: command.skipGit }); - commandOptions.addInput({ + commandOptions.add({ name: 'dry-run', value: command.dryRun }); + commandOptions.add({ name: 'skip-git', value: command.skipGit }); + commandOptions.add({ name: 'skip-install', value: command.skipInstall, }); - commandOptions.addInput({ name: 'strict', value: command.strict }); - commandOptions.addInput({ + commandOptions.add({ name: 'strict', value: command.strict }); + commandOptions.add({ name: 'packageManager', value: command.packageManager, }); - commandOptions.addInput({ + commandOptions.add({ name: 'collection', value: command.collection, }); @@ -76,7 +76,7 @@ export class NewCommand extends AbstractCommand { break; } } - commandOptions.addInput({ + commandOptions.add({ name: 'language', value: command.language, }); diff --git a/commands/start.command.ts b/commands/start.command.ts index 150ea7378..78edfdaee 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandInputsContainer } from './command.input'; +import { Input, CommandStorage } from './command.input'; import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { @@ -40,42 +40,42 @@ export class StartCommand extends AbstractCommand { ) .description('Run Nest application.') .action(async (app: string, command: Command) => { - const commandOptions = new CommandInputsContainer(); + const commandOptions = new CommandStorage(); - commandOptions.addInput({ + commandOptions.add({ name: 'config', value: command.config, }); const isWebpackEnabled = command.tsc ? false : command.webpack; - commandOptions.addInput({ name: 'webpack', value: isWebpackEnabled }); - commandOptions.addInput({ name: 'debug', value: command.debug }); - commandOptions.addInput({ name: 'watch', value: !!command.watch }); - commandOptions.addInput({ + commandOptions.add({ name: 'webpack', value: isWebpackEnabled }); + commandOptions.add({ name: 'debug', value: command.debug }); + commandOptions.add({ name: 'watch', value: !!command.watch }); + commandOptions.add({ name: 'watchAssets', value: !!command.watchAssets, }); - commandOptions.addInput({ + commandOptions.add({ name: 'path', value: command.path, }); - commandOptions.addInput({ + commandOptions.add({ name: 'webpackPath', value: command.webpackPath, }); - commandOptions.addInput({ + commandOptions.add({ name: 'exec', value: command.exec, }); - commandOptions.addInput({ + commandOptions.add({ name: 'sourceRoot', value: command.sourceRoot, }); - commandOptions.addInput({ + commandOptions.add({ name: 'entryFile', value: command.entryFile, }); - commandOptions.addInput({ + commandOptions.add({ name: 'preserveWatchOutput', value: !!command.preserveWatchOutput && @@ -93,7 +93,7 @@ export class StartCommand extends AbstractCommand { ); return; } - commandOptions.addInput({ + commandOptions.add({ name: 'builder', value: command.builder, }); @@ -104,7 +104,7 @@ export class StartCommand extends AbstractCommand { ` "typeCheck" will not have any effect when "builder" is not "swc".`, ); } - commandOptions.addInput({ + commandOptions.add({ name: 'typeCheck', value: command.typeCheck, }); diff --git a/lib/compiler/helpers/get-builder.ts b/lib/compiler/helpers/get-builder.ts index bd1250525..3a43ce90f 100644 --- a/lib/compiler/helpers/get-builder.ts +++ b/lib/compiler/helpers/get-builder.ts @@ -1,4 +1,4 @@ -import { CommandInputsContainer } from '../../../commands'; +import { CommandStorage } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getBuilder( configuration: Required, - cmdOptions: CommandInputsContainer, + cmdOptions: CommandStorage, appName: string, ) { const builderValue = getValueOrDefault( diff --git a/lib/compiler/helpers/get-tsc-config.path.ts b/lib/compiler/helpers/get-tsc-config.path.ts index a31583713..320f50550 100644 --- a/lib/compiler/helpers/get-tsc-config.path.ts +++ b/lib/compiler/helpers/get-tsc-config.path.ts @@ -1,4 +1,4 @@ -import { CommandInputsContainer } from '../../../commands'; +import { CommandStorage } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getDefaultTsconfigPath } from '../../utils/get-default-tsconfig-path'; import { getValueOrDefault } from './get-value-or-default'; @@ -12,7 +12,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getTscConfigPath( configuration: Required, - cmdOptions: CommandInputsContainer, + cmdOptions: CommandStorage, appName: string, ) { let tsconfigPath = getValueOrDefault( diff --git a/lib/compiler/helpers/get-value-or-default.ts b/lib/compiler/helpers/get-value-or-default.ts index c7e77d672..49e403cec 100644 --- a/lib/compiler/helpers/get-value-or-default.ts +++ b/lib/compiler/helpers/get-value-or-default.ts @@ -1,4 +1,4 @@ -import { Input, CommandInputsContainer } from '../../../commands'; +import { Input, CommandStorage } from '../../../commands'; import { Configuration } from '../../configuration'; export function getValueOrDefault( @@ -14,12 +14,12 @@ export function getValueOrDefault( | 'exec' | 'builder' | 'typeCheck', - options: Input[] | CommandInputsContainer = [], + options: Input[] | CommandStorage = [], defaultValue?: T, ): T { const item = Array.isArray(options) ? options.find((option) => option.name === key) - : key && options.resolveInput(key); + : key && options.get(key); const origValue = item?.value as T | undefined; if (origValue !== undefined && origValue !== null) { return origValue; diff --git a/lib/compiler/helpers/get-webpack-config-path.ts b/lib/compiler/helpers/get-webpack-config-path.ts index bc4f3be90..f53836216 100644 --- a/lib/compiler/helpers/get-webpack-config-path.ts +++ b/lib/compiler/helpers/get-webpack-config-path.ts @@ -1,4 +1,4 @@ -import { CommandInputsContainer } from '../../../commands'; +import { CommandStorage } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getWebpackConfigPath( configuration: Required, - cmdOptions: CommandInputsContainer, + cmdOptions: CommandStorage, appName: string, ) { let webpackPath = getValueOrDefault( diff --git a/lib/compiler/webpack-compiler.ts b/lib/compiler/webpack-compiler.ts index 5072503ce..56eb83d83 100644 --- a/lib/compiler/webpack-compiler.ts +++ b/lib/compiler/webpack-compiler.ts @@ -1,6 +1,6 @@ import { existsSync } from 'fs'; import { join } from 'path'; -import { CommandInputsContainer } from '../../commands'; +import { CommandStorage } from '../../commands'; import { Configuration } from '../configuration'; import { INFO_PREFIX } from '../ui'; import { AssetsManager } from './assets-manager'; @@ -20,7 +20,7 @@ type WebpackConfigFactoryOrConfig = | webpack.Configuration; type WebpackCompilerExtras = { - inputs: CommandInputsContainer; + inputs: CommandStorage; assetsManager: AssetsManager; webpackConfigFactoryOrConfig: | WebpackConfigFactoryOrConfig diff --git a/lib/utils/project-utils.ts b/lib/utils/project-utils.ts index dd8f4252a..0cc9867bc 100644 --- a/lib/utils/project-utils.ts +++ b/lib/utils/project-utils.ts @@ -1,6 +1,6 @@ import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; -import { CommandInputsContainer } from '../../commands'; +import { CommandStorage } from '../../commands'; import { getValueOrDefault } from '../compiler/helpers/get-value-or-default'; import { Configuration, ProjectConfiguration } from '../configuration'; import { generateSelect } from '../questions/questions'; @@ -142,9 +142,9 @@ export function moveDefaultProjectToStart( export function hasValidOptionFlag( queriedOptionName: string, - options: CommandInputsContainer, + options: CommandStorage, queriedValue: string | number | boolean = true, ): boolean { - const option = options.resolveInput(queriedOptionName); + const option = options.get(queriedOptionName); return option?.value === queriedValue; } From 62ec68c38487fc61be22cda83f4b2b63c74758bd Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 12:04:30 -0400 Subject: [PATCH 05/17] refactor: drop input in favor of command storage abstraction --- actions/abstract.action.ts | 4 +- actions/add.action.ts | 31 +++++++------ actions/build.action.ts | 9 ++-- actions/generate.action.ts | 44 +++++++++---------- actions/new.action.ts | 34 +++++++-------- actions/start.action.ts | 7 ++- commands/add.command.ts | 6 +-- commands/build.command.ts | 6 +-- commands/command.input.ts | 46 ++++++++++++++------ commands/generate.command.ts | 10 ++--- commands/new.command.ts | 6 +-- commands/start.command.ts | 6 +-- lib/compiler/helpers/get-value-or-default.ts | 4 +- lib/utils/project-utils.ts | 2 +- test/lib/questions/questions.spec.ts | 4 +- 15 files changed, 119 insertions(+), 100 deletions(-) diff --git a/actions/abstract.action.ts b/actions/abstract.action.ts index 32d49b797..a2b049aa8 100644 --- a/actions/abstract.action.ts +++ b/actions/abstract.action.ts @@ -1,8 +1,8 @@ -import { Input, CommandStorage } from '../commands'; +import { CommandStorage } from '../commands'; export abstract class AbstractAction { public abstract handle( - inputs?: Input[], + inputs?: CommandStorage, options?: CommandStorage, extraFlags?: string[], ): Promise; diff --git a/actions/add.action.ts b/actions/add.action.ts index d46852301..a88e64471 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -1,5 +1,5 @@ import * as chalk from 'chalk'; -import { CommandStorage, Input } from '../commands'; +import { CommandStorage, CommandStorageEntry } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractPackageManager, @@ -24,7 +24,7 @@ const schematicName = 'nest-add'; export class AddAction extends AbstractAction { public async handle( - inputs: Input[], + inputs: CommandStorage, options: CommandStorage, extraFlags: string[], ) { @@ -36,9 +36,10 @@ export class AddAction extends AbstractAction { const packageInstallSuccess = skipInstall || (await this.installPackage(collectionName, tagName)); if (packageInstallSuccess) { - const sourceRootOption: Input = await this.getSourceRoot( - inputs.concat(options.toArray()), - ); + const sourceRootOption: CommandStorageEntry = await this.getSourceRoot([ + inputs, + options, + ]); if (sourceRootOption) { options.add(sourceRootOption); } @@ -56,12 +57,18 @@ export class AddAction extends AbstractAction { } } - private async getSourceRoot(inputs: Input[]): Promise { + private async getSourceRoot(storages: CommandStorage[]): Promise { const configuration = await loadConfiguration(); const configurationProjects = configuration.projects; - const appName = inputs.find((option) => option.name === 'project')! - .value as string; + let appName: string | undefined; + for (const storage of storages) { + const maybeProject = storage.get('project'); + if (maybeProject) { + appName = maybeProject.value; + break; + } + } let sourceRoot = appName ? getValueOrDefault(configuration, 'sourceRoot', appName) @@ -152,15 +159,13 @@ export class AddAction extends AbstractAction { } } - private getLibraryName(inputs: Input[]): string { - const libraryInput: Input = inputs.find( - (input) => input.name === 'library', - ) as Input; + private getLibraryName(inputs: CommandStorage): string { + const libraryInput = inputs.get('library') if (!libraryInput) { throw new Error('No library found in command input'); } - return libraryInput.value as string; + return libraryInput.value; } private getPackageName(library: string): string { diff --git a/actions/build.action.ts b/actions/build.action.ts index 99344f57d..c3c3f0937 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -1,7 +1,7 @@ import * as chalk from 'chalk'; import { join } from 'path'; import * as ts from 'typescript'; -import { Input, CommandStorage } from '../commands'; +import { CommandStorage } from '../commands'; import { AssetsManager } from '../lib/compiler/assets-manager'; import { Compiler } from '../lib/compiler/compiler'; import { getBuilder } from '../lib/compiler/helpers/get-builder'; @@ -41,7 +41,7 @@ export class BuildAction extends AbstractAction { protected readonly workspaceUtils = new WorkspaceUtils(); public async handle( - commandInputs: Input[], + commandInputs: CommandStorage, commandOptions: CommandStorage, ) { try { @@ -66,7 +66,7 @@ export class BuildAction extends AbstractAction { } public async runBuild( - commandInputs: Input[], + commandInputs: CommandStorage, commandOptions: CommandStorage, watchMode: boolean, watchAssetsMode: boolean, @@ -78,8 +78,7 @@ export class BuildAction extends AbstractAction { true, ).value; const configuration = await this.loader.load(configFileName); - const appName = commandInputs.find((input) => input.name === 'app')! - .value as string; + const appName = commandInputs.get('app', true).value; const pathToTsconfig = getTscConfigPath( configuration, diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 07c4ed7e3..22029ff0a 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk'; import { Answers } from 'inquirer'; -import { Input, CommandStorage } from '../commands'; +import { CommandStorage } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractCollection, @@ -21,30 +21,28 @@ import { import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { - public async handle(inputs: Input[], options: CommandStorage) { - await generateFiles(inputs.concat(options.toArray())); + public async handle(inputs: CommandStorage, options: CommandStorage) { + inputs.mergeWith(options) + await generateFiles( + inputs + ); } } -const generateFiles = async (inputs: Input[]) => { +const generateFiles = async (storage: CommandStorage) => { const configuration = await loadConfiguration(); - const collectionOption = inputs.find( - (option) => option.name === 'collection', - )!.value as string; - const schematic = inputs.find((option) => option.name === 'schematic')! - .value as string; - const appName = inputs.find((option) => option.name === 'project')! - .value as string; - const spec = inputs.find((option) => option.name === 'spec'); - const flat = inputs.find((option) => option.name === 'flat'); - const specFileSuffix = inputs.find( - (option) => option.name === 'specFileSuffix', - ); + + const collectionOption = storage.get('collection', true).value + const schematic = storage.get('schematic', true).value + const appName = storage.get('project', true).value + const spec = storage.get('spec', true) + const flat = storage.get('flat', true) + const specFileSuffix = storage.get('specFileSuffix', true) const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, ); - const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); + const schematicOptions: SchematicOption[] = mapSchematicOptions(storage); schematicOptions.push( new SchematicOption('language', configuration.language), ); @@ -54,10 +52,10 @@ const generateFiles = async (inputs: Input[]) => { ? getValueOrDefault(configuration, 'sourceRoot', appName) : configuration.sourceRoot; - const specValue = spec!.value as boolean; + const specValue = spec.value const flatValue = !!flat?.value; - const specFileSuffixValue = specFileSuffix!.value as string; - const specOptions = spec!.options as any; + const specFileSuffixValue = specFileSuffix.value + const specOptions = spec.options let generateSpec = shouldGenerateSpec( configuration, schematic, @@ -132,7 +130,7 @@ const generateFiles = async (inputs: Input[]) => { new SchematicOption('specFileSuffix', generateSpecFileSuffix), ); try { - const schematicInput = inputs.find((input) => input.name === 'schematic'); + const schematicInput = storage.get('schematic') if (!schematicInput) { throw new Error('Unable to find a schematic for this configuration'); } @@ -144,10 +142,10 @@ const generateFiles = async (inputs: Input[]) => { } }; -const mapSchematicOptions = (inputs: Input[]): SchematicOption[] => { +const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; - inputs.forEach((input) => { + storage.toArray().forEach((input) => { if (!excludedInputNames.includes(input.name) && input.value !== undefined) { options.push(new SchematicOption(input.name, input.value)); } diff --git a/actions/new.action.ts b/actions/new.action.ts index 23ee720ec..bb6925611 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; import { join } from 'path'; -import { CommandStorage, Input } from '../commands'; +import { CommandStorage, CommandStorageEntry } from '../commands'; import { defaultGitIgnore } from '../lib/configuration/defaults'; import { AbstractPackageManager, @@ -24,7 +24,7 @@ import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { - public async handle(inputs: Input[], options: CommandStorage) { + public async handle(inputs: CommandStorage, options: CommandStorage) { const directoryOption = options.get('directory'); const dryRunOption = options.get('dry-run'); const isDryRunEnabled = !!dryRunOption?.value; @@ -34,9 +34,9 @@ export class NewAction extends AbstractAction { const shouldSkipInstall = options.get('skip-install')?.value; - const shouldSkipGit = options.get('skip-git')?.value; + const shouldSkipGit = !!options.get('skip-git')?.value; const projectDirectory = getProjectDirectory( - getApplicationNameInput(inputs)!, + getApplicationNameInput(inputs), directoryOption, ); @@ -59,21 +59,21 @@ export class NewAction extends AbstractAction { } } -const getApplicationNameInput = (inputs: Input[]) => - inputs.find((input) => input.name === 'name'); +const getApplicationNameInput = (inputs: CommandStorage) => + inputs.get('name', true); const getProjectDirectory = ( - applicationName: Input, - directoryOption?: Input, + applicationName: CommandStorageEntry, + directoryOption: CommandStorageEntry | undefined, ): string => { return ( - (directoryOption && (directoryOption.value as string)) || - normalizeToKebabOrSnakeCase(applicationName.value as string) + (directoryOption?.value) || + normalizeToKebabOrSnakeCase(applicationName.value) ); }; const askForMissingInformation = async ( - inputs: Input[], + inputs: CommandStorage, options: CommandStorage, ) => { console.info(MESSAGES.PROJECT_INFORMATION_START); @@ -97,9 +97,9 @@ const askForMissingInformation = async ( }; const replaceInputMissingInformation = ( - inputs: Input[] | CommandStorage, + inputs: CommandStorageEntry[] | CommandStorage, answers: Answers, -): Input[] => { +): CommandStorageEntry[] => { if (!Array.isArray(inputs)) { inputs = inputs.toArray(); } @@ -111,7 +111,7 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async ( - args: Input[], + args: CommandStorage, options: CommandStorage, ) => { const collectionName = options.get('collection', true) @@ -120,15 +120,15 @@ const generateApplicationFiles = async ( (collectionName as Collection) || Collection.NESTJS, ); const schematicOptions: SchematicOption[] = mapSchematicOptions( - args.concat(options.toArray()), + args.toArray().concat(options.toArray()), ); await collection.execute('application', schematicOptions); console.info(); }; -const mapSchematicOptions = (options: Input[]): SchematicOption[] => { +const mapSchematicOptions = (options: CommandStorageEntry[]): SchematicOption[] => { return options.reduce( - (schematicOptions: SchematicOption[], option: Input) => { + (schematicOptions: SchematicOption[], option: CommandStorageEntry) => { if (option.name !== 'skip-install') { schematicOptions.push(new SchematicOption(option.name, option.value)); } diff --git a/actions/start.action.ts b/actions/start.action.ts index 58f967a74..58bff5079 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process'; import * as fs from 'fs'; import { join } from 'path'; import * as killProcess from 'tree-kill'; -import { Input, CommandStorage } from '../commands'; +import { CommandStorage } from '../commands'; import { getTscConfigPath } from '../lib/compiler/helpers/get-tsc-config.path'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { @@ -16,7 +16,7 @@ import { BuildAction } from './build.action'; export class StartAction extends BuildAction { public async handle( - commandInputs: Input[], + commandInputs: CommandStorage, commandOptions: CommandStorage, ) { try { @@ -25,8 +25,7 @@ export class StartAction extends BuildAction { true, ).value; const configuration = await this.loader.load(configFileName); - const appName = commandInputs.find((input) => input.name === 'app')! - .value as string; + const appName = commandInputs.get('app', true).value; const pathToTsconfig = getTscConfigPath( configuration, diff --git a/commands/add.command.ts b/commands/add.command.ts index 27f82b8f4..9b29824e1 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandStorage } from './command.input'; +import { CommandStorage } from './command.input'; export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -29,8 +29,8 @@ export class AddCommand extends AbstractCommand { value: command.project, }); - const inputs: Input[] = []; - inputs.push({ name: 'library', value: library }); + const inputs = new CommandStorage(); + inputs.add({ name: 'library', value: library }); const flags = getRemainingFlags(program); try { diff --git a/commands/build.command.ts b/commands/build.command.ts index 3f2f3ae45..de398277b 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandStorage } from './command.input'; +import { CommandStorage } from './command.input'; export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -70,8 +70,8 @@ export class BuildCommand extends AbstractCommand { value: command.typeCheck, }); - const inputs: Input[] = []; - inputs.push({ name: 'app', value: app }); + const inputs = new CommandStorage() + inputs.add({ name: 'app', value: app }); await this.action.handle(inputs, commandOptions); }); } diff --git a/commands/command.input.ts b/commands/command.input.ts index 733eac116..89081c973 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -1,36 +1,44 @@ -export interface Input { +export interface CommandStorageEntry { name: string; value: TValue; options?: any; } export class CommandStorage { - private readonly inputsByName = new Map(); + private readonly inputsByName = new Map(); - toArray(): Input[] { + /** + * @returns A new array containing all the inputs. + */ + toArray(): CommandStorageEntry[] { return Array.from(this.inputsByName.values()); } - add(input: Input) { - this.inputsByName.set(input.name, input); + /** + * Add a new input to the storage if it does not exist yet. + */ + add(input: CommandStorageEntry) { + if (!this.inputsByName.has(input.name)) { + this.inputsByName.set(input.name, input); + } } get( - inputName: Input['name'], - ): Input | undefined; + inputName: CommandStorageEntry['name'], + ): CommandStorageEntry | undefined; get( - inputName: Input['name'], + inputName: CommandStorageEntry['name'], errorOnMissing: false, - ): Input | undefined; + ): CommandStorageEntry | undefined; get( - inputName: Input['name'], + inputName: CommandStorageEntry['name'], errorOnMissing: true, - ): Input; + ): CommandStorageEntry; get( - inputName: Input['name'], + inputName: CommandStorageEntry['name'], errorOnMissing = false, - ): Input | undefined { - const input = this.inputsByName.get(inputName) as Input | undefined; + ): CommandStorageEntry | undefined { + const input = this.inputsByName.get(inputName) as CommandStorageEntry | undefined; if (errorOnMissing) { if (!input) { throw new Error(`The input ${inputName} is missing!`); @@ -38,4 +46,14 @@ export class CommandStorage { } return input; } + + /** + * Copy all inputs of the other command storage with this one. + * Note that if an input already exists, it will **not** be overwritten. + */ + mergeWith(otherStorage: CommandStorage): void { + for (const input of otherStorage.inputsByName.values()) { + this.add(input); + } + } } diff --git a/commands/generate.command.ts b/commands/generate.command.ts index 2f0e637b3..e57688651 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -5,7 +5,7 @@ import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandStorage } from './command.input'; +import { CommandStorage } from './command.input'; export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { @@ -94,10 +94,10 @@ export class GenerateCommand extends AbstractCommand { value: command.skipImport, }); - const inputs: Input[] = []; - inputs.push({ name: 'schematic', value: schematic }); - inputs.push({ name: 'name', value: name }); - inputs.push({ name: 'path', value: path }); + const inputs = new CommandStorage() + inputs.add({ name: 'schematic', value: schematic }); + inputs.add({ name: 'name', value: name }); + inputs.add({ name: 'path', value: path }); await this.action.handle(inputs, commandOptions); }, diff --git a/commands/new.command.ts b/commands/new.command.ts index 550c5eb2d..0d3be2b72 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -1,7 +1,7 @@ import { Command, CommanderStatic } from 'commander'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandStorage } from './command.input'; +import { CommandStorage } from './command.input'; export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { @@ -81,8 +81,8 @@ export class NewCommand extends AbstractCommand { value: command.language, }); - const inputs: Input[] = []; - inputs.push({ name: 'name', value: name }); + const inputs = new CommandStorage() + inputs.add({ name: 'name', value: name }); await this.action.handle(inputs, commandOptions); }); diff --git a/commands/start.command.ts b/commands/start.command.ts index 78edfdaee..e3db9baac 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { Input, CommandStorage } from './command.input'; +import { CommandStorage } from './command.input'; import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { @@ -109,8 +109,8 @@ export class StartCommand extends AbstractCommand { value: command.typeCheck, }); - const inputs: Input[] = []; - inputs.push({ name: 'app', value: app }); + const inputs = new CommandStorage() + inputs.add({ name: 'app', value: app }); const flags = getRemainingFlags(program); try { diff --git a/lib/compiler/helpers/get-value-or-default.ts b/lib/compiler/helpers/get-value-or-default.ts index 49e403cec..4ecfe6ab8 100644 --- a/lib/compiler/helpers/get-value-or-default.ts +++ b/lib/compiler/helpers/get-value-or-default.ts @@ -1,4 +1,4 @@ -import { Input, CommandStorage } from '../../../commands'; +import { CommandStorageEntry, CommandStorage } from '../../../commands'; import { Configuration } from '../../configuration'; export function getValueOrDefault( @@ -14,7 +14,7 @@ export function getValueOrDefault( | 'exec' | 'builder' | 'typeCheck', - options: Input[] | CommandStorage = [], + options: CommandStorageEntry[] | CommandStorage = [], defaultValue?: T, ): T { const item = Array.isArray(options) diff --git a/lib/utils/project-utils.ts b/lib/utils/project-utils.ts index 0cc9867bc..57d505eb1 100644 --- a/lib/utils/project-utils.ts +++ b/lib/utils/project-utils.ts @@ -8,7 +8,7 @@ import { generateSelect } from '../questions/questions'; export function shouldAskForProject( schematic: string, configurationProjects: { [key: string]: ProjectConfiguration }, - appName: string, + appName: string | undefined, ) { return ( ['app', 'sub-app', 'library', 'lib'].includes(schematic) === false && diff --git a/test/lib/questions/questions.spec.ts b/test/lib/questions/questions.spec.ts index 98e782807..9bb1cf0af 100644 --- a/test/lib/questions/questions.spec.ts +++ b/test/lib/questions/questions.spec.ts @@ -1,5 +1,5 @@ import { Question } from 'inquirer'; -import { Input } from '../../../commands/command.input'; +import { CommandStorageEntry } from '../../../commands/command.input'; import { generateInput, generateSelect, @@ -8,7 +8,7 @@ import { describe('Questions', () => { describe('generateInput', () => { it('should return an input question', () => { - const input: Input = { + const input: CommandStorageEntry = { name: 'name', value: 'test', }; From 53e0fae87e61be46a8eb0b3287df7ef818bfa4e4 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 12:05:08 -0400 Subject: [PATCH 06/17] style: fix formatting --- actions/add.action.ts | 6 ++++-- actions/build.action.ts | 5 +---- actions/generate.action.ts | 26 ++++++++++++-------------- actions/new.action.ts | 18 +++++++----------- actions/start.action.ts | 8 ++------ commands/build.command.ts | 2 +- commands/command.input.ts | 13 ++++++++++--- commands/generate.command.ts | 2 +- commands/new.command.ts | 2 +- commands/start.command.ts | 2 +- lib/compiler/defaults/swc-defaults.ts | 2 +- lib/runners/abstract.runner.ts | 5 ++++- lib/schematics/abstract.collection.ts | 5 ++++- lib/schematics/schematic.option.ts | 5 ++++- 14 files changed, 53 insertions(+), 48 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index a88e64471..3776ed334 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -57,7 +57,9 @@ export class AddAction extends AbstractAction { } } - private async getSourceRoot(storages: CommandStorage[]): Promise { + private async getSourceRoot( + storages: CommandStorage[], + ): Promise { const configuration = await loadConfiguration(); const configurationProjects = configuration.projects; @@ -160,7 +162,7 @@ export class AddAction extends AbstractAction { } private getLibraryName(inputs: CommandStorage): string { - const libraryInput = inputs.get('library') + const libraryInput = inputs.get('library'); if (!libraryInput) { throw new Error('No library found in command input'); diff --git a/actions/build.action.ts b/actions/build.action.ts index c3c3f0937..633c7b166 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -73,10 +73,7 @@ export class BuildAction extends AbstractAction { isDebugEnabled = false, onSuccess?: () => void, ) { - const configFileName = commandOptions.get( - 'config', - true, - ).value; + const configFileName = commandOptions.get('config', true).value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.get('app', true).value; diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 22029ff0a..27e962aae 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -22,22 +22,20 @@ import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { public async handle(inputs: CommandStorage, options: CommandStorage) { - inputs.mergeWith(options) - await generateFiles( - inputs - ); + inputs.mergeWith(options); + await generateFiles(inputs); } } const generateFiles = async (storage: CommandStorage) => { const configuration = await loadConfiguration(); - const collectionOption = storage.get('collection', true).value - const schematic = storage.get('schematic', true).value - const appName = storage.get('project', true).value - const spec = storage.get('spec', true) - const flat = storage.get('flat', true) - const specFileSuffix = storage.get('specFileSuffix', true) + const collectionOption = storage.get('collection', true).value; + const schematic = storage.get('schematic', true).value; + const appName = storage.get('project', true).value; + const spec = storage.get('spec', true); + const flat = storage.get('flat', true); + const specFileSuffix = storage.get('specFileSuffix', true); const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, @@ -52,10 +50,10 @@ const generateFiles = async (storage: CommandStorage) => { ? getValueOrDefault(configuration, 'sourceRoot', appName) : configuration.sourceRoot; - const specValue = spec.value + const specValue = spec.value; const flatValue = !!flat?.value; - const specFileSuffixValue = specFileSuffix.value - const specOptions = spec.options + const specFileSuffixValue = specFileSuffix.value; + const specOptions = spec.options; let generateSpec = shouldGenerateSpec( configuration, schematic, @@ -130,7 +128,7 @@ const generateFiles = async (storage: CommandStorage) => { new SchematicOption('specFileSuffix', generateSpecFileSuffix), ); try { - const schematicInput = storage.get('schematic') + const schematicInput = storage.get('schematic'); if (!schematicInput) { throw new Error('Unable to find a schematic for this configuration'); } diff --git a/actions/new.action.ts b/actions/new.action.ts index bb6925611..76af455e4 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -32,8 +32,7 @@ export class NewAction extends AbstractAction { await askForMissingInformation(inputs, options); await generateApplicationFiles(inputs, options).catch(exit); - const shouldSkipInstall = - options.get('skip-install')?.value; + const shouldSkipInstall = options.get('skip-install')?.value; const shouldSkipGit = !!options.get('skip-git')?.value; const projectDirectory = getProjectDirectory( getApplicationNameInput(inputs), @@ -67,8 +66,7 @@ const getProjectDirectory = ( directoryOption: CommandStorageEntry | undefined, ): string => { return ( - (directoryOption?.value) || - normalizeToKebabOrSnakeCase(applicationName.value) + directoryOption?.value || normalizeToKebabOrSnakeCase(applicationName.value) ); }; @@ -114,8 +112,7 @@ const generateApplicationFiles = async ( args: CommandStorage, options: CommandStorage, ) => { - const collectionName = options.get('collection', true) - ?.value; + const collectionName = options.get('collection', true)?.value; const collection: AbstractCollection = CollectionFactory.create( (collectionName as Collection) || Collection.NESTJS, ); @@ -126,7 +123,9 @@ const generateApplicationFiles = async ( console.info(); }; -const mapSchematicOptions = (options: CommandStorageEntry[]): SchematicOption[] => { +const mapSchematicOptions = ( + options: CommandStorageEntry[], +): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: CommandStorageEntry) => { if (option.name !== 'skip-install') { @@ -143,10 +142,7 @@ const installPackages = async ( dryRunMode: boolean, installDirectory: string, ) => { - const inputPackageManager = options.get( - 'packageManager', - true, - ).value; + const inputPackageManager = options.get('packageManager', true).value; let packageManager: AbstractPackageManager; if (dryRunMode) { diff --git a/actions/start.action.ts b/actions/start.action.ts index 58bff5079..2b0236471 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -20,10 +20,7 @@ export class StartAction extends BuildAction { commandOptions: CommandStorage, ) { try { - const configFileName = commandOptions.get( - 'config', - true, - ).value; + const configFileName = commandOptions.get('config', true).value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.get('app', true).value; @@ -33,8 +30,7 @@ export class StartAction extends BuildAction { appName, ); - const isWatchEnabled = - !!commandOptions.get('watch')?.value; + const isWatchEnabled = !!commandOptions.get('watch')?.value; const isWatchAssetsEnabled = !!commandOptions.get('watchAssets')?.value; const debugFlag = commandOptions.get('debug')?.value; diff --git a/commands/build.command.ts b/commands/build.command.ts index de398277b..5544018e8 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -70,7 +70,7 @@ export class BuildCommand extends AbstractCommand { value: command.typeCheck, }); - const inputs = new CommandStorage() + const inputs = new CommandStorage(); inputs.add({ name: 'app', value: app }); await this.action.handle(inputs, commandOptions); }); diff --git a/commands/command.input.ts b/commands/command.input.ts index 89081c973..0babc332c 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -1,11 +1,16 @@ -export interface CommandStorageEntry { +export interface CommandStorageEntry< + TValue extends boolean | string = boolean | string, +> { name: string; value: TValue; options?: any; } export class CommandStorage { - private readonly inputsByName = new Map(); + private readonly inputsByName = new Map< + CommandStorageEntry['name'], + CommandStorageEntry + >(); /** * @returns A new array containing all the inputs. @@ -38,7 +43,9 @@ export class CommandStorage { inputName: CommandStorageEntry['name'], errorOnMissing = false, ): CommandStorageEntry | undefined { - const input = this.inputsByName.get(inputName) as CommandStorageEntry | undefined; + const input = this.inputsByName.get(inputName) as + | CommandStorageEntry + | undefined; if (errorOnMissing) { if (!input) { throw new Error(`The input ${inputName} is missing!`); diff --git a/commands/generate.command.ts b/commands/generate.command.ts index e57688651..fcbe2dc23 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -94,7 +94,7 @@ export class GenerateCommand extends AbstractCommand { value: command.skipImport, }); - const inputs = new CommandStorage() + const inputs = new CommandStorage(); inputs.add({ name: 'schematic', value: schematic }); inputs.add({ name: 'name', value: name }); inputs.add({ name: 'path', value: path }); diff --git a/commands/new.command.ts b/commands/new.command.ts index 0d3be2b72..22a287973 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -81,7 +81,7 @@ export class NewCommand extends AbstractCommand { value: command.language, }); - const inputs = new CommandStorage() + const inputs = new CommandStorage(); inputs.add({ name: 'name', value: name }); await this.action.handle(inputs, commandOptions); diff --git a/commands/start.command.ts b/commands/start.command.ts index e3db9baac..0b009336e 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -109,7 +109,7 @@ export class StartCommand extends AbstractCommand { value: command.typeCheck, }); - const inputs = new CommandStorage() + const inputs = new CommandStorage(); inputs.add({ name: 'app', value: app }); const flags = getRemainingFlags(program); diff --git a/lib/compiler/defaults/swc-defaults.ts b/lib/compiler/defaults/swc-defaults.ts index c26ec2ec1..8c85a7196 100644 --- a/lib/compiler/defaults/swc-defaults.ts +++ b/lib/compiler/defaults/swc-defaults.ts @@ -25,7 +25,7 @@ export const swcDefaultsFactory = ( transform: { legacyDecorator: true, decoratorMetadata: true, - useDefineForClassFields: false + useDefineForClassFields: false, }, keepClassNames: true, baseUrl: tsOptions?.baseUrl, diff --git a/lib/runners/abstract.runner.ts b/lib/runners/abstract.runner.ts index 2118030c5..cea9e821b 100644 --- a/lib/runners/abstract.runner.ts +++ b/lib/runners/abstract.runner.ts @@ -3,7 +3,10 @@ import { ChildProcess, spawn, SpawnOptions } from 'child_process'; import { MESSAGES } from '../ui'; export class AbstractRunner { - constructor(protected binary: string, protected args: string[] = []) {} + constructor( + protected binary: string, + protected args: string[] = [], + ) {} public async run( command: string, diff --git a/lib/schematics/abstract.collection.ts b/lib/schematics/abstract.collection.ts index 88a25a85e..bf268583d 100644 --- a/lib/schematics/abstract.collection.ts +++ b/lib/schematics/abstract.collection.ts @@ -3,7 +3,10 @@ import { Schematic } from './nest.collection'; import { SchematicOption } from './schematic.option'; export abstract class AbstractCollection { - constructor(protected collection: string, protected runner: AbstractRunner) {} + constructor( + protected collection: string, + protected runner: AbstractRunner, + ) {} public async execute( name: string, diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index b0bb4bf85..51b603df6 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -1,7 +1,10 @@ import { normalizeToKebabOrSnakeCase } from '../utils/formatting'; export class SchematicOption { - constructor(private name: string, private value: boolean | string) {} + constructor( + private name: string, + private value: boolean | string, + ) {} get normalizedName() { return normalizeToKebabOrSnakeCase(this.name); From 027b76faf8c560f845cbe8653dd07717b977ca0a Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 12:15:46 -0400 Subject: [PATCH 07/17] style: remove unused type assertion --- actions/generate.action.ts | 2 +- actions/new.action.ts | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 27e962aae..e78072fb1 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -132,7 +132,7 @@ const generateFiles = async (storage: CommandStorage) => { if (!schematicInput) { throw new Error('Unable to find a schematic for this configuration'); } - await collection.execute(schematicInput.value as string, schematicOptions); + await collection.execute(schematicInput.value, schematicOptions); } catch (error) { if (error && error.message) { console.error(chalk.red(error.message)); diff --git a/actions/new.action.ts b/actions/new.action.ts index 76af455e4..2896cc520 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -40,11 +40,7 @@ export class NewAction extends AbstractAction { ); if (!shouldSkipInstall) { - await installPackages( - options, - isDryRunEnabled as boolean, - projectDirectory, - ); + await installPackages(options, isDryRunEnabled, projectDirectory); } if (!isDryRunEnabled) { if (!shouldSkipGit) { From b36c6af81adc35c45b7746ccabffb90e3b42ee13 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 12:59:46 -0400 Subject: [PATCH 08/17] refactor: remove useless non-null assertion operator --- actions/new.action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/new.action.ts b/actions/new.action.ts index 2896cc520..ccdf03c91 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -76,7 +76,7 @@ const askForMissingInformation = async ( const prompt: inquirer.PromptModule = inquirer.createPromptModule(); const nameInput = getApplicationNameInput(inputs); - if (!nameInput!.value) { + if (!nameInput.value) { const message = 'What name would you like to use for the new project?'; const questions = [generateInput('name', message)('nest-app')]; const answers: Answers = await prompt(questions as ReadonlyArray); From 27f7db87b8968309d10f8aecdfbc2d8e61f8c592 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 16:47:22 -0400 Subject: [PATCH 09/17] refactor: drop `toArray` from `CommandStorage` --- actions/generate.action.ts | 14 +++++++-- actions/new.action.ts | 60 ++++++++++++++++++++++---------------- commands/command.input.ts | 21 ++++++++----- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index e78072fb1..cedeed946 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -143,9 +143,17 @@ const generateFiles = async (storage: CommandStorage) => { const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; - storage.toArray().forEach((input) => { - if (!excludedInputNames.includes(input.name) && input.value !== undefined) { - options.push(new SchematicOption(input.name, input.value)); + storage.forEachEntry((commandStorageEntry) => { + if ( + !excludedInputNames.includes(commandStorageEntry.name) && + commandStorageEntry.value !== undefined + ) { + options.push( + new SchematicOption( + commandStorageEntry.name, + commandStorageEntry.value, + ), + ); } }); return options; diff --git a/actions/new.action.ts b/actions/new.action.ts index ccdf03c91..1105155ad 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -91,17 +91,18 @@ const askForMissingInformation = async ( }; const replaceInputMissingInformation = ( - inputs: CommandStorageEntry[] | CommandStorage, + inputs: CommandStorage, answers: Answers, -): CommandStorageEntry[] => { - if (!Array.isArray(inputs)) { - inputs = inputs.toArray(); - } - return inputs.map( - (input) => - (input.value = - input.value !== undefined ? input.value : answers[input.name]), - ); +): void => { + inputs.forEachEntry((input) => { + if (input.value === undefined) { + const maybeInputAnswer = answers[input.name]; + inputs.set({ + name: input.name, + value: maybeInputAnswer, + }); + } + }); }; const generateApplicationFiles = async ( @@ -112,25 +113,34 @@ const generateApplicationFiles = async ( const collection: AbstractCollection = CollectionFactory.create( (collectionName as Collection) || Collection.NESTJS, ); - const schematicOptions: SchematicOption[] = mapSchematicOptions( - args.toArray().concat(options.toArray()), - ); + + const argsAndOptionStorage = new CommandStorage(); + argsAndOptionStorage.mergeWith(args); + argsAndOptionStorage.mergeWith(options); + const schematicOptions: SchematicOption[] = + mapSchematicOptions(argsAndOptionStorage); await collection.execute('application', schematicOptions); + console.info(); }; -const mapSchematicOptions = ( - options: CommandStorageEntry[], -): SchematicOption[] => { - return options.reduce( - (schematicOptions: SchematicOption[], option: CommandStorageEntry) => { - if (option.name !== 'skip-install') { - schematicOptions.push(new SchematicOption(option.name, option.value)); - } - return schematicOptions; - }, - [], - ); +const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { + const excludedInputNames = ['skip-install']; + const options: SchematicOption[] = []; + storage.forEachEntry((commandStorageEntry) => { + if ( + !excludedInputNames.includes(commandStorageEntry.name) && + commandStorageEntry.value !== undefined + ) { + options.push( + new SchematicOption( + commandStorageEntry.name, + commandStorageEntry.value, + ), + ); + } + }); + return options; }; const installPackages = async ( diff --git a/commands/command.input.ts b/commands/command.input.ts index 0babc332c..a99c96f36 100644 --- a/commands/command.input.ts +++ b/commands/command.input.ts @@ -1,9 +1,9 @@ export interface CommandStorageEntry< TValue extends boolean | string = boolean | string, > { - name: string; - value: TValue; - options?: any; + readonly name: string; + readonly value: TValue; + readonly options?: any; } export class CommandStorage { @@ -12,11 +12,8 @@ export class CommandStorage { CommandStorageEntry >(); - /** - * @returns A new array containing all the inputs. - */ - toArray(): CommandStorageEntry[] { - return Array.from(this.inputsByName.values()); + forEachEntry(callback: (input: CommandStorageEntry) => void): void { + this.inputsByName.forEach((input) => callback(input)); } /** @@ -28,6 +25,14 @@ export class CommandStorage { } } + /** + * Overwrite a existing input to a new value the storage or create a new entry + * if it does not exist yet. + */ + set(input: CommandStorageEntry) { + this.inputsByName.set(input.name, input); + } + get( inputName: CommandStorageEntry['name'], ): CommandStorageEntry | undefined; From 603512f0a140fd26d9fa5fe1506f26b00ab24ea2 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 17:08:08 -0400 Subject: [PATCH 10/17] feat: type coupling command class with their action --- commands/abstract.command.ts | 4 ++-- commands/add.command.ts | 3 ++- commands/build.command.ts | 3 ++- commands/generate.command.ts | 3 ++- commands/info.command.ts | 3 ++- commands/new.command.ts | 3 ++- commands/start.command.ts | 7 +++---- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/commands/abstract.command.ts b/commands/abstract.command.ts index 97969b2c0..a5bb6bfb5 100644 --- a/commands/abstract.command.ts +++ b/commands/abstract.command.ts @@ -1,8 +1,8 @@ import { CommanderStatic } from 'commander'; import { AbstractAction } from '../actions/abstract.action'; -export abstract class AbstractCommand { - constructor(protected action: AbstractAction) {} +export abstract class AbstractCommand { + constructor(protected action: T) {} public abstract load(program: CommanderStatic): void; } diff --git a/commands/add.command.ts b/commands/add.command.ts index 9b29824e1..b51eebe57 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -1,9 +1,10 @@ import { Command, CommanderStatic } from 'commander'; +import type { AddAction } from '../actions'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; import { CommandStorage } from './command.input'; -export class AddCommand extends AbstractCommand { +export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { program .command('add ') diff --git a/commands/build.command.ts b/commands/build.command.ts index 5544018e8..5c8c998d7 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -1,9 +1,10 @@ import { Command, CommanderStatic } from 'commander'; +import type { BuildAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; import { CommandStorage } from './command.input'; -export class BuildCommand extends AbstractCommand { +export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { program .command('build [app]') diff --git a/commands/generate.command.ts b/commands/generate.command.ts index fcbe2dc23..1e26ada2d 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -1,13 +1,14 @@ import * as chalk from 'chalk'; import * as Table from 'cli-table3'; import { Command, CommanderStatic } from 'commander'; +import type { GenerateAction } from '../actions'; import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; import { CommandStorage } from './command.input'; -export class GenerateCommand extends AbstractCommand { +export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { program .command('generate [name] [path]') diff --git a/commands/info.command.ts b/commands/info.command.ts index 1d968badc..f83722fbd 100644 --- a/commands/info.command.ts +++ b/commands/info.command.ts @@ -1,7 +1,8 @@ import { CommanderStatic } from 'commander'; +import type { InfoAction } from '../actions'; import { AbstractCommand } from './abstract.command'; -export class InfoCommand extends AbstractCommand { +export class InfoCommand extends AbstractCommand { public load(program: CommanderStatic) { program .command('info') diff --git a/commands/new.command.ts b/commands/new.command.ts index 22a287973..3d51a66b6 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -1,9 +1,10 @@ import { Command, CommanderStatic } from 'commander'; +import type { NewAction } from '../actions'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; import { CommandStorage } from './command.input'; -export class NewCommand extends AbstractCommand { +export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { program .command('new [name]') diff --git a/commands/start.command.ts b/commands/start.command.ts index 0b009336e..5e5e08924 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -1,11 +1,11 @@ import { Command, CommanderStatic } from 'commander'; +import type { StartAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; -import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; import { CommandStorage } from './command.input'; import type { BuilderVariant } from '../lib/configuration'; -export class StartCommand extends AbstractCommand { +export class StartCommand extends AbstractCommand { public load(program: CommanderStatic): void { program .command('start [app]') @@ -111,10 +111,9 @@ export class StartCommand extends AbstractCommand { const inputs = new CommandStorage(); inputs.add({ name: 'app', value: app }); - const flags = getRemainingFlags(program); try { - await this.action.handle(inputs, commandOptions, flags); + await this.action.handle(inputs, commandOptions); } catch (err) { process.exit(1); } From 2ceb047439e212ad2bd6186b2b6be28f8a4aed8a Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 17:09:09 -0400 Subject: [PATCH 11/17] refactor: better naming to command storage abstraction file name --- commands/add.command.ts | 2 +- commands/build.command.ts | 2 +- commands/{command.input.ts => command-storage.ts} | 0 commands/generate.command.ts | 2 +- commands/index.ts | 2 +- commands/new.command.ts | 2 +- commands/start.command.ts | 2 +- test/lib/questions/questions.spec.ts | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename commands/{command.input.ts => command-storage.ts} (100%) diff --git a/commands/add.command.ts b/commands/add.command.ts index b51eebe57..b8c70ee61 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { AddAction } from '../actions'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command.input'; +import { CommandStorage } from './command-storage'; export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { diff --git a/commands/build.command.ts b/commands/build.command.ts index 5c8c998d7..6bc9d02b6 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { BuildAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command.input'; +import { CommandStorage } from './command-storage'; export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { diff --git a/commands/command.input.ts b/commands/command-storage.ts similarity index 100% rename from commands/command.input.ts rename to commands/command-storage.ts diff --git a/commands/generate.command.ts b/commands/generate.command.ts index 1e26ada2d..b304be82f 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -6,7 +6,7 @@ import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command.input'; +import { CommandStorage } from './command-storage'; export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { diff --git a/commands/index.ts b/commands/index.ts index 8047da9f2..e5face162 100644 --- a/commands/index.ts +++ b/commands/index.ts @@ -1,2 +1,2 @@ export * from './command.loader'; -export * from './command.input'; +export * from './command-storage'; diff --git a/commands/new.command.ts b/commands/new.command.ts index 3d51a66b6..a71f4746c 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { NewAction } from '../actions'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command.input'; +import { CommandStorage } from './command-storage'; export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { diff --git a/commands/start.command.ts b/commands/start.command.ts index 5e5e08924..b68fd7242 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { StartAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command.input'; +import { CommandStorage } from './command-storage'; import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { diff --git a/test/lib/questions/questions.spec.ts b/test/lib/questions/questions.spec.ts index 9bb1cf0af..23e1e9acc 100644 --- a/test/lib/questions/questions.spec.ts +++ b/test/lib/questions/questions.spec.ts @@ -1,5 +1,5 @@ import { Question } from 'inquirer'; -import { CommandStorageEntry } from '../../../commands/command.input'; +import { CommandStorageEntry } from '../../../commands/command-storage'; import { generateInput, generateSelect, From 79e7cfcac6c6e6d39753c398b515c4bb263bad6f Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 17:11:31 -0400 Subject: [PATCH 12/17] refactor: remove useless from command generate action --- actions/generate.action.ts | 3 +-- commands/generate.command.ts | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index cedeed946..27de3b2ee 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -21,8 +21,7 @@ import { import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { - public async handle(inputs: CommandStorage, options: CommandStorage) { - inputs.mergeWith(options); + public async handle(inputs: CommandStorage) { await generateFiles(inputs); } } diff --git a/commands/generate.command.ts b/commands/generate.command.ts index b304be82f..d8542ccef 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -56,15 +56,15 @@ export class GenerateCommand extends AbstractCommand { path: string, command: Command, ) => { - const commandOptions = new CommandStorage(); + const commandInputs = new CommandStorage(); - commandOptions.add({ name: 'dry-run', value: !!command.dryRun }); + commandInputs.add({ name: 'dry-run', value: !!command.dryRun }); if (command.flat !== undefined) { - commandOptions.add({ name: 'flat', value: command.flat }); + commandInputs.add({ name: 'flat', value: command.flat }); } - commandOptions.add({ + commandInputs.add({ name: 'spec', value: typeof command.spec === 'boolean' @@ -77,30 +77,29 @@ export class GenerateCommand extends AbstractCommand { : command.spec.passedAsInput, }, }); - commandOptions.add({ + commandInputs.add({ name: 'specFileSuffix', value: command.specFileSuffix, }); - commandOptions.add({ + commandInputs.add({ name: 'collection', value: command.collection, }); - commandOptions.add({ + commandInputs.add({ name: 'project', value: command.project, }); - commandOptions.add({ + commandInputs.add({ name: 'skipImport', value: command.skipImport, }); - const inputs = new CommandStorage(); - inputs.add({ name: 'schematic', value: schematic }); - inputs.add({ name: 'name', value: name }); - inputs.add({ name: 'path', value: path }); + commandInputs.add({ name: 'schematic', value: schematic }); + commandInputs.add({ name: 'name', value: name }); + commandInputs.add({ name: 'path', value: path }); - await this.action.handle(inputs, commandOptions); + await this.action.handle(commandInputs); }, ); } From 519ecbf7444563097bde04c5d9a99c07b84b5d53 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 17:15:02 -0400 Subject: [PATCH 13/17] refactor: remove useless type assertion --- actions/new.action.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/new.action.ts b/actions/new.action.ts index 1105155ad..5191583c7 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -109,10 +109,10 @@ const generateApplicationFiles = async ( args: CommandStorage, options: CommandStorage, ) => { - const collectionName = options.get('collection', true)?.value; - const collection: AbstractCollection = CollectionFactory.create( - (collectionName as Collection) || Collection.NESTJS, - ); + const collectionName = + options.get('collection')?.value || Collection.NESTJS; + const collection: AbstractCollection = + CollectionFactory.create(collectionName); const argsAndOptionStorage = new CommandStorage(); argsAndOptionStorage.mergeWith(args); From 5a3f6e512f23a901d01e8959ed48a49acd45d19d Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 22 Jul 2023 17:32:24 -0400 Subject: [PATCH 14/17] fix: minor changes to met the current behavior - emit error on retrieve inputs with 'undefined' value - leave 'project' option optional as before - leave 'collection' optional as before - allow 'specFileSuffix' to be undefined - allow 'config' option to be undefined on build command --- actions/build.action.ts | 6 ++++-- actions/generate.action.ts | 10 +++++----- commands/command-storage.ts | 2 +- lib/utils/project-utils.ts | 10 +++++----- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/actions/build.action.ts b/actions/build.action.ts index eb3a32363..82a505a1d 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -69,7 +69,7 @@ export class BuildAction extends AbstractAction { isDebugEnabled = false, onSuccess?: () => void, ) { - const configFileName = commandOptions.get('config', true).value; + const configFileName = commandOptions.get('config')?.value; const configuration = await this.loader.load(configFileName); const appName = commandInputs.get('app', true).value; @@ -178,7 +178,9 @@ export class BuildAction extends AbstractAction { watchMode: boolean, onSuccess: (() => void) | undefined, ) { - const { WebpackCompiler } = await import('../lib/compiler/webpack-compiler') + const { WebpackCompiler } = await import( + '../lib/compiler/webpack-compiler' + ); const webpackCompiler = new WebpackCompiler(this.pluginsLoader); const webpackPath = diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 27de3b2ee..1fe30f83a 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -29,12 +29,12 @@ export class GenerateAction extends AbstractAction { const generateFiles = async (storage: CommandStorage) => { const configuration = await loadConfiguration(); - const collectionOption = storage.get('collection', true).value; + const collectionOption = storage.get('collection')?.value; const schematic = storage.get('schematic', true).value; - const appName = storage.get('project', true).value; + const appName = storage.get('project')?.value; const spec = storage.get('spec', true); - const flat = storage.get('flat', true); - const specFileSuffix = storage.get('specFileSuffix', true); + const flat = storage.get('flat'); + const specFileSuffix = storage.get('specFileSuffix'); const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, @@ -51,7 +51,7 @@ const generateFiles = async (storage: CommandStorage) => { const specValue = spec.value; const flatValue = !!flat?.value; - const specFileSuffixValue = specFileSuffix.value; + const specFileSuffixValue = specFileSuffix?.value; const specOptions = spec.options; let generateSpec = shouldGenerateSpec( configuration, diff --git a/commands/command-storage.ts b/commands/command-storage.ts index a99c96f36..621a7647b 100644 --- a/commands/command-storage.ts +++ b/commands/command-storage.ts @@ -52,7 +52,7 @@ export class CommandStorage { | CommandStorageEntry | undefined; if (errorOnMissing) { - if (!input) { + if (!input || input.value === undefined) { throw new Error(`The input ${inputName} is missing!`); } } diff --git a/lib/utils/project-utils.ts b/lib/utils/project-utils.ts index 57d505eb1..aa58b9476 100644 --- a/lib/utils/project-utils.ts +++ b/lib/utils/project-utils.ts @@ -21,7 +21,7 @@ export function shouldAskForProject( export function shouldGenerateSpec( configuration: Required, schematic: string, - appName: string, + appName: string | undefined, specValue: boolean, specPassedAsInput?: boolean, ) { @@ -70,7 +70,7 @@ export function shouldGenerateSpec( export function shouldGenerateFlat( configuration: Required, - appName: string, + appName: string | undefined, flatValue: boolean, ): boolean { // CLI parameters have the highest priority @@ -91,8 +91,8 @@ export function shouldGenerateFlat( export function getSpecFileSuffix( configuration: Required, - appName: string, - specFileSuffixValue: string, + appName: string | undefined, + specFileSuffixValue: string | undefined, ): string { // CLI parameters have the highest priority if (specFileSuffixValue) { @@ -110,7 +110,7 @@ export function getSpecFileSuffix( if (typeof specFileSuffixConfiguration === 'string') { return specFileSuffixConfiguration; } - return specFileSuffixValue; + throw new Error('No spec file suffix was defined!'); } export async function askForProjectName( From da4dd704eed75aa79ab17f51d2da6f834f10b9a5 Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 29 Jul 2023 20:48:37 -0400 Subject: [PATCH 15/17] refactor: make default values more clear to command actions --- actions/build.action.ts | 6 +++--- actions/generate.action.ts | 20 +++++++++----------- actions/new.action.ts | 8 ++++---- actions/start.action.ts | 9 +++++---- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/actions/build.action.ts b/actions/build.action.ts index 82a505a1d..98cab0bf7 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -41,9 +41,9 @@ export class BuildAction extends AbstractAction { commandOptions: CommandStorage, ) { try { - const watchMode = !!commandOptions.get('watch')?.value; + const watchMode = commandOptions.get('watch')?.value ?? false; const watchAssetsMode = - !!commandOptions.get('watchAssets')?.value; + commandOptions.get('watchAssets')?.value ?? false; await this.runBuild( commandInputs, @@ -222,7 +222,7 @@ export class BuildAction extends AbstractAction { this.tsLoader, ); const isPreserveWatchOutputEnabled = - options.get('preserveWatchOutput')?.value || false; + options.get('preserveWatchOutput')?.value ?? false; watchCompiler.run( configuration, pathToTsconfig, diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 1fe30f83a..af946edef 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -26,20 +26,17 @@ export class GenerateAction extends AbstractAction { } } -const generateFiles = async (storage: CommandStorage) => { +const generateFiles = async (inputs: CommandStorage) => { const configuration = await loadConfiguration(); - const collectionOption = storage.get('collection')?.value; - const schematic = storage.get('schematic', true).value; - const appName = storage.get('project')?.value; - const spec = storage.get('spec', true); - const flat = storage.get('flat'); - const specFileSuffix = storage.get('specFileSuffix'); + const collectionOption = inputs.get('collection')?.value; + const schematic = inputs.get('schematic', true).value; + const appName = inputs.get('project')?.value; const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, ); - const schematicOptions: SchematicOption[] = mapSchematicOptions(storage); + const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); schematicOptions.push( new SchematicOption('language', configuration.language), ); @@ -49,9 +46,10 @@ const generateFiles = async (storage: CommandStorage) => { ? getValueOrDefault(configuration, 'sourceRoot', appName) : configuration.sourceRoot; + const spec = inputs.get('spec', true); + const flatValue = inputs.get('flat')?.value ?? false; const specValue = spec.value; - const flatValue = !!flat?.value; - const specFileSuffixValue = specFileSuffix?.value; + const specFileSuffixValue = inputs.get('specFileSuffix')?.value; const specOptions = spec.options; let generateSpec = shouldGenerateSpec( configuration, @@ -127,7 +125,7 @@ const generateFiles = async (storage: CommandStorage) => { new SchematicOption('specFileSuffix', generateSpecFileSuffix), ); try { - const schematicInput = storage.get('schematic'); + const schematicInput = inputs.get('schematic'); if (!schematicInput) { throw new Error('Unable to find a schematic for this configuration'); } diff --git a/actions/new.action.ts b/actions/new.action.ts index 5191583c7..b019f3827 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -26,14 +26,14 @@ import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { public async handle(inputs: CommandStorage, options: CommandStorage) { const directoryOption = options.get('directory'); - const dryRunOption = options.get('dry-run'); - const isDryRunEnabled = !!dryRunOption?.value; + const isDryRunEnabled = options.get('dry-run')?.value ?? false; await askForMissingInformation(inputs, options); await generateApplicationFiles(inputs, options).catch(exit); - const shouldSkipInstall = options.get('skip-install')?.value; - const shouldSkipGit = !!options.get('skip-git')?.value; + const shouldSkipInstall = + options.get('skip-install')?.value ?? false; + const shouldSkipGit = options.get('skip-git')?.value ?? false; const projectDirectory = getProjectDirectory( getApplicationNameInput(inputs), directoryOption, diff --git a/actions/start.action.ts b/actions/start.action.ts index 2b0236471..6580a61d4 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -30,10 +30,11 @@ export class StartAction extends BuildAction { appName, ); - const isWatchEnabled = !!commandOptions.get('watch')?.value; + const isWatchEnabled = + commandOptions.get('watch')?.value ?? false; const isWatchAssetsEnabled = - !!commandOptions.get('watchAssets')?.value; - const debugFlag = commandOptions.get('debug')?.value; + commandOptions.get('watchAssets')?.value ?? false; + const debugFlag = commandOptions.get('debug')?.value ?? false; const binaryToRun = getValueOrDefault( configuration, 'exec', @@ -75,7 +76,7 @@ export class StartAction extends BuildAction { commandOptions, isWatchEnabled, isWatchAssetsEnabled, - !!debugFlag, + debugFlag, onSuccess, ); } catch (err) { From 0cd95b5d0636e220ed47527a20e14e8e2062dc4d Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sat, 29 Jul 2023 21:07:49 -0400 Subject: [PATCH 16/17] refactor: move partial handler funcion from `GenerateAction` --- actions/generate.action.ts | 193 ++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 98 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index af946edef..457c1eb4e 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -22,120 +22,117 @@ import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { public async handle(inputs: CommandStorage) { - await generateFiles(inputs); - } -} - -const generateFiles = async (inputs: CommandStorage) => { - const configuration = await loadConfiguration(); - - const collectionOption = inputs.get('collection')?.value; - const schematic = inputs.get('schematic', true).value; - const appName = inputs.get('project')?.value; + const configuration = await loadConfiguration(); - const collection: AbstractCollection = CollectionFactory.create( - collectionOption || configuration.collection || Collection.NESTJS, - ); - const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); - schematicOptions.push( - new SchematicOption('language', configuration.language), - ); - const configurationProjects = configuration.projects; + const collectionOption = inputs.get('collection')?.value; + const schematic = inputs.get('schematic', true).value; + const appName = inputs.get('project')?.value; - let sourceRoot = appName - ? getValueOrDefault(configuration, 'sourceRoot', appName) - : configuration.sourceRoot; - - const spec = inputs.get('spec', true); - const flatValue = inputs.get('flat')?.value ?? false; - const specValue = spec.value; - const specFileSuffixValue = inputs.get('specFileSuffix')?.value; - const specOptions = spec.options; - let generateSpec = shouldGenerateSpec( - configuration, - schematic, - appName, - specValue, - specOptions.passedAsInput, - ); - let generateFlat = shouldGenerateFlat(configuration, appName, flatValue); - let generateSpecFileSuffix = getSpecFileSuffix( - configuration, - appName, - specFileSuffixValue, - ); + const collection: AbstractCollection = CollectionFactory.create( + collectionOption || configuration.collection || Collection.NESTJS, + ); + const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); + schematicOptions.push( + new SchematicOption('language', configuration.language), + ); + const configurationProjects = configuration.projects; - // If you only add a `lib` we actually don't have monorepo: true BUT we do have "projects" - // Ensure we don't run for new app/libs schematics - if (shouldAskForProject(schematic, configurationProjects, appName)) { - const defaultLabel = ' [ Default ]'; - let defaultProjectName: string = configuration.sourceRoot + defaultLabel; + let sourceRoot = appName + ? getValueOrDefault(configuration, 'sourceRoot', appName) + : configuration.sourceRoot; - for (const property in configurationProjects) { - if ( - configurationProjects[property].sourceRoot === configuration.sourceRoot - ) { - defaultProjectName = property + defaultLabel; - break; - } - } - - const projects = moveDefaultProjectToStart( + const spec = inputs.get('spec', true); + const flatValue = inputs.get('flat')?.value ?? false; + const specValue = spec.value; + const specFileSuffixValue = inputs.get('specFileSuffix')?.value; + const specOptions = spec.options; + let generateSpec = shouldGenerateSpec( configuration, - defaultProjectName, - defaultLabel, + schematic, + appName, + specValue, + specOptions.passedAsInput, ); - - const answers: Answers = await askForProjectName( - MESSAGES.PROJECT_SELECTION_QUESTION, - projects, + let generateFlat = shouldGenerateFlat(configuration, appName, flatValue); + let generateSpecFileSuffix = getSpecFileSuffix( + configuration, + appName, + specFileSuffixValue, ); - const project: string = answers.appName.replace(defaultLabel, ''); - if (project !== configuration.sourceRoot) { - sourceRoot = configurationProjects[project].sourceRoot; - } + // If you only add a `lib` we actually don't have monorepo: true BUT we do have "projects" + // Ensure we don't run for new app/libs schematics + if (shouldAskForProject(schematic, configurationProjects, appName)) { + const defaultLabel = ' [ Default ]'; + let defaultProjectName: string = configuration.sourceRoot + defaultLabel; - if (answers.appName !== defaultProjectName) { - // Only overwrite if the appName is not the default- as it has already been loaded above - generateSpec = shouldGenerateSpec( - configuration, - schematic, - answers.appName, - specValue, - specOptions.passedAsInput, - ); - generateFlat = shouldGenerateFlat( + for (const property in configurationProjects) { + if ( + configurationProjects[property].sourceRoot === + configuration.sourceRoot + ) { + defaultProjectName = property + defaultLabel; + break; + } + } + + const projects = moveDefaultProjectToStart( configuration, - answers.appNames, - flatValue, + defaultProjectName, + defaultLabel, ); - generateSpecFileSuffix = getSpecFileSuffix( - configuration, - appName, - specFileSuffixValue, + + const answers: Answers = await askForProjectName( + MESSAGES.PROJECT_SELECTION_QUESTION, + projects, ); - } - } - schematicOptions.push(new SchematicOption('sourceRoot', sourceRoot)); - schematicOptions.push(new SchematicOption('spec', generateSpec)); - schematicOptions.push(new SchematicOption('flat', generateFlat)); - schematicOptions.push( - new SchematicOption('specFileSuffix', generateSpecFileSuffix), - ); - try { - const schematicInput = inputs.get('schematic'); - if (!schematicInput) { - throw new Error('Unable to find a schematic for this configuration'); + const project: string = answers.appName.replace(defaultLabel, ''); + if (project !== configuration.sourceRoot) { + sourceRoot = configurationProjects[project].sourceRoot; + } + + if (answers.appName !== defaultProjectName) { + // Only overwrite if the appName is not the default- as it has already been loaded above + generateSpec = shouldGenerateSpec( + configuration, + schematic, + answers.appName, + specValue, + specOptions.passedAsInput, + ); + generateFlat = shouldGenerateFlat( + configuration, + answers.appNames, + flatValue, + ); + generateSpecFileSuffix = getSpecFileSuffix( + configuration, + appName, + specFileSuffixValue, + ); + } } - await collection.execute(schematicInput.value, schematicOptions); - } catch (error) { - if (error && error.message) { - console.error(chalk.red(error.message)); + + schematicOptions.push(new SchematicOption('sourceRoot', sourceRoot)); + schematicOptions.push(new SchematicOption('spec', generateSpec)); + schematicOptions.push(new SchematicOption('flat', generateFlat)); + schematicOptions.push( + new SchematicOption('specFileSuffix', generateSpecFileSuffix), + ); + try { + const schematicInput = inputs.get('schematic'); + if (!schematicInput) { + throw new Error('Unable to find a schematic for this configuration'); + } + await collection.execute(schematicInput.value, schematicOptions); + } catch (error) { + if (error && error.message) { + console.error(chalk.red(error.message)); + } } } -}; +} const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; From de97d47ca965cf9d8c09fa47ed49281a88d8185a Mon Sep 17 00:00:00 2001 From: "Micael Levi L. Cavalcante" Date: Sun, 29 Oct 2023 12:48:49 -0400 Subject: [PATCH 17/17] refactor: rename `CommandStorage` to `CommandContext` --- actions/abstract.action.ts | 6 ++-- actions/add.action.ts | 16 ++++----- actions/build.action.ts | 16 ++++----- actions/generate.action.ts | 6 ++-- actions/new.action.ts | 26 +++++++------- actions/start.action.ts | 6 ++-- commands/add.command.ts | 6 ++-- commands/build.command.ts | 6 ++-- ...{command-storage.ts => command-context.ts} | 34 +++++++++---------- commands/generate.command.ts | 4 +-- commands/index.ts | 2 +- commands/new.command.ts | 6 ++-- commands/start.command.ts | 6 ++-- lib/compiler/helpers/get-builder.ts | 4 +-- lib/compiler/helpers/get-tsc-config.path.ts | 4 +-- lib/compiler/helpers/get-value-or-default.ts | 4 +-- .../helpers/get-webpack-config-path.ts | 4 +-- lib/compiler/webpack-compiler.ts | 4 +-- lib/utils/project-utils.ts | 4 +-- test/lib/questions/questions.spec.ts | 4 +-- 20 files changed, 84 insertions(+), 84 deletions(-) rename commands/{command-storage.ts => command-context.ts} (66%) diff --git a/actions/abstract.action.ts b/actions/abstract.action.ts index a2b049aa8..5acdcefa9 100644 --- a/actions/abstract.action.ts +++ b/actions/abstract.action.ts @@ -1,9 +1,9 @@ -import { CommandStorage } from '../commands'; +import { CommandContext } from '../commands'; export abstract class AbstractAction { public abstract handle( - inputs?: CommandStorage, - options?: CommandStorage, + inputs?: CommandContext, + options?: CommandContext, extraFlags?: string[], ): Promise; } diff --git a/actions/add.action.ts b/actions/add.action.ts index 3776ed334..0a87c8472 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -1,5 +1,5 @@ import * as chalk from 'chalk'; -import { CommandStorage, CommandStorageEntry } from '../commands'; +import { CommandContext, CommandContextEntry } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractPackageManager, @@ -24,8 +24,8 @@ const schematicName = 'nest-add'; export class AddAction extends AbstractAction { public async handle( - inputs: CommandStorage, - options: CommandStorage, + inputs: CommandContext, + options: CommandContext, extraFlags: string[], ) { const libraryName = this.getLibraryName(inputs); @@ -36,7 +36,7 @@ export class AddAction extends AbstractAction { const packageInstallSuccess = skipInstall || (await this.installPackage(collectionName, tagName)); if (packageInstallSuccess) { - const sourceRootOption: CommandStorageEntry = await this.getSourceRoot([ + const sourceRootOption: CommandContextEntry = await this.getSourceRoot([ inputs, options, ]); @@ -58,8 +58,8 @@ export class AddAction extends AbstractAction { } private async getSourceRoot( - storages: CommandStorage[], - ): Promise { + storages: CommandContext[], + ): Promise { const configuration = await loadConfiguration(); const configurationProjects = configuration.projects; @@ -132,7 +132,7 @@ export class AddAction extends AbstractAction { private async addLibrary( collectionName: string, - options: CommandStorage, + options: CommandContext, extraFlags: string[], ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); @@ -161,7 +161,7 @@ export class AddAction extends AbstractAction { } } - private getLibraryName(inputs: CommandStorage): string { + private getLibraryName(inputs: CommandContext): string { const libraryInput = inputs.get('library'); if (!libraryInput) { diff --git a/actions/build.action.ts b/actions/build.action.ts index 98cab0bf7..77cb6089e 100644 --- a/actions/build.action.ts +++ b/actions/build.action.ts @@ -1,7 +1,7 @@ import * as chalk from 'chalk'; import { join } from 'path'; import * as ts from 'typescript'; -import { CommandStorage } from '../commands'; +import { CommandContext } from '../commands'; import { AssetsManager } from '../lib/compiler/assets-manager'; import { getBuilder } from '../lib/compiler/helpers/get-builder'; import { getTscConfigPath } from '../lib/compiler/helpers/get-tsc-config.path'; @@ -37,8 +37,8 @@ export class BuildAction extends AbstractAction { protected readonly workspaceUtils = new WorkspaceUtils(); public async handle( - commandInputs: CommandStorage, - commandOptions: CommandStorage, + commandInputs: CommandContext, + commandOptions: CommandContext, ) { try { const watchMode = commandOptions.get('watch')?.value ?? false; @@ -62,8 +62,8 @@ export class BuildAction extends AbstractAction { } public async runBuild( - commandInputs: CommandStorage, - commandOptions: CommandStorage, + commandInputs: CommandContext, + commandOptions: CommandContext, watchMode: boolean, watchAssetsMode: boolean, isDebugEnabled = false, @@ -143,7 +143,7 @@ export class BuildAction extends AbstractAction { appName: string, pathToTsconfig: string, watchMode: boolean, - options: CommandStorage, + options: CommandContext, tsOptions: ts.CompilerOptions, onSuccess: (() => void) | undefined, ) { @@ -172,7 +172,7 @@ export class BuildAction extends AbstractAction { private async runWebpack( configuration: Required, appName: string, - commandOptions: CommandStorage, + commandOptions: CommandContext, pathToTsconfig: string, debug: boolean, watchMode: boolean, @@ -208,7 +208,7 @@ export class BuildAction extends AbstractAction { private async runTsc( watchMode: boolean, - options: CommandStorage, + options: CommandContext, configuration: Required, pathToTsconfig: string, appName: string, diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 457c1eb4e..18b1dca6b 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk'; import { Answers } from 'inquirer'; -import { CommandStorage } from '../commands'; +import { CommandContext } from '../commands'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { AbstractCollection, @@ -21,7 +21,7 @@ import { import { AbstractAction } from './abstract.action'; export class GenerateAction extends AbstractAction { - public async handle(inputs: CommandStorage) { + public async handle(inputs: CommandContext) { const configuration = await loadConfiguration(); const collectionOption = inputs.get('collection')?.value; @@ -134,7 +134,7 @@ export class GenerateAction extends AbstractAction { } } -const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { +const mapSchematicOptions = (storage: CommandContext): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; storage.forEachEntry((commandStorageEntry) => { diff --git a/actions/new.action.ts b/actions/new.action.ts index b019f3827..c2673375e 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; import { join } from 'path'; -import { CommandStorage, CommandStorageEntry } from '../commands'; +import { CommandContext, CommandContextEntry } from '../commands'; import { defaultGitIgnore } from '../lib/configuration/defaults'; import { AbstractPackageManager, @@ -24,7 +24,7 @@ import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { - public async handle(inputs: CommandStorage, options: CommandStorage) { + public async handle(inputs: CommandContext, options: CommandContext) { const directoryOption = options.get('directory'); const isDryRunEnabled = options.get('dry-run')?.value ?? false; @@ -54,12 +54,12 @@ export class NewAction extends AbstractAction { } } -const getApplicationNameInput = (inputs: CommandStorage) => +const getApplicationNameInput = (inputs: CommandContext) => inputs.get('name', true); const getProjectDirectory = ( - applicationName: CommandStorageEntry, - directoryOption: CommandStorageEntry | undefined, + applicationName: CommandContextEntry, + directoryOption: CommandContextEntry | undefined, ): string => { return ( directoryOption?.value || normalizeToKebabOrSnakeCase(applicationName.value) @@ -67,8 +67,8 @@ const getProjectDirectory = ( }; const askForMissingInformation = async ( - inputs: CommandStorage, - options: CommandStorage, + inputs: CommandContext, + options: CommandContext, ) => { console.info(MESSAGES.PROJECT_INFORMATION_START); console.info(); @@ -91,7 +91,7 @@ const askForMissingInformation = async ( }; const replaceInputMissingInformation = ( - inputs: CommandStorage, + inputs: CommandContext, answers: Answers, ): void => { inputs.forEachEntry((input) => { @@ -106,15 +106,15 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async ( - args: CommandStorage, - options: CommandStorage, + args: CommandContext, + options: CommandContext, ) => { const collectionName = options.get('collection')?.value || Collection.NESTJS; const collection: AbstractCollection = CollectionFactory.create(collectionName); - const argsAndOptionStorage = new CommandStorage(); + const argsAndOptionStorage = new CommandContext(); argsAndOptionStorage.mergeWith(args); argsAndOptionStorage.mergeWith(options); const schematicOptions: SchematicOption[] = @@ -124,7 +124,7 @@ const generateApplicationFiles = async ( console.info(); }; -const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { +const mapSchematicOptions = (storage: CommandContext): SchematicOption[] => { const excludedInputNames = ['skip-install']; const options: SchematicOption[] = []; storage.forEachEntry((commandStorageEntry) => { @@ -144,7 +144,7 @@ const mapSchematicOptions = (storage: CommandStorage): SchematicOption[] => { }; const installPackages = async ( - options: CommandStorage, + options: CommandContext, dryRunMode: boolean, installDirectory: string, ) => { diff --git a/actions/start.action.ts b/actions/start.action.ts index 6580a61d4..c5e617e6c 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process'; import * as fs from 'fs'; import { join } from 'path'; import * as killProcess from 'tree-kill'; -import { CommandStorage } from '../commands'; +import { CommandContext } from '../commands'; import { getTscConfigPath } from '../lib/compiler/helpers/get-tsc-config.path'; import { getValueOrDefault } from '../lib/compiler/helpers/get-value-or-default'; import { @@ -16,8 +16,8 @@ import { BuildAction } from './build.action'; export class StartAction extends BuildAction { public async handle( - commandInputs: CommandStorage, - commandOptions: CommandStorage, + commandInputs: CommandContext, + commandOptions: CommandContext, ) { try { const configFileName = commandOptions.get('config', true).value; diff --git a/commands/add.command.ts b/commands/add.command.ts index b8c70ee61..3df1579a0 100644 --- a/commands/add.command.ts +++ b/commands/add.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { AddAction } from '../actions'; import { getRemainingFlags } from '../lib/utils/remaining-flags'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command-storage'; +import { CommandContext } from './command-context'; export class AddCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -18,7 +18,7 @@ export class AddCommand extends AbstractCommand { .option('-p, --project [project]', 'Project in which to generate files.') .usage(' [options] [library-specific-options]') .action(async (library: string, command: Command) => { - const commandOptions = new CommandStorage(); + const commandOptions = new CommandContext(); commandOptions.add({ name: 'dry-run', value: !!command.dryRun }); commandOptions.add({ @@ -30,7 +30,7 @@ export class AddCommand extends AbstractCommand { value: command.project, }); - const inputs = new CommandStorage(); + const inputs = new CommandContext(); inputs.add({ name: 'library', value: library }); const flags = getRemainingFlags(program); diff --git a/commands/build.command.ts b/commands/build.command.ts index a36cc6d84..2f4b27ce5 100644 --- a/commands/build.command.ts +++ b/commands/build.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { BuildAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command-storage'; +import { CommandContext } from './command-context'; export class BuildCommand extends AbstractCommand { public load(program: CommanderStatic): void { @@ -26,7 +26,7 @@ export class BuildCommand extends AbstractCommand { ) .description('Build Nest application.') .action(async (app: string, command: Command) => { - const commandOptions = new CommandStorage(); + const commandOptions = new CommandContext(); commandOptions.add({ name: 'config', @@ -83,7 +83,7 @@ export class BuildCommand extends AbstractCommand { !isWebpackEnabled, }); - const inputs = new CommandStorage(); + const inputs = new CommandContext(); inputs.add({ name: 'app', value: app }); await this.action.handle(inputs, commandOptions); }); diff --git a/commands/command-storage.ts b/commands/command-context.ts similarity index 66% rename from commands/command-storage.ts rename to commands/command-context.ts index 621a7647b..f99d75e19 100644 --- a/commands/command-storage.ts +++ b/commands/command-context.ts @@ -1,4 +1,4 @@ -export interface CommandStorageEntry< +export interface CommandContextEntry< TValue extends boolean | string = boolean | string, > { readonly name: string; @@ -6,20 +6,20 @@ export interface CommandStorageEntry< readonly options?: any; } -export class CommandStorage { +export class CommandContext { private readonly inputsByName = new Map< - CommandStorageEntry['name'], - CommandStorageEntry + CommandContextEntry['name'], + CommandContextEntry >(); - forEachEntry(callback: (input: CommandStorageEntry) => void): void { + forEachEntry(callback: (input: CommandContextEntry) => void): void { this.inputsByName.forEach((input) => callback(input)); } /** * Add a new input to the storage if it does not exist yet. */ - add(input: CommandStorageEntry) { + add(input: CommandContextEntry) { if (!this.inputsByName.has(input.name)) { this.inputsByName.set(input.name, input); } @@ -29,27 +29,27 @@ export class CommandStorage { * Overwrite a existing input to a new value the storage or create a new entry * if it does not exist yet. */ - set(input: CommandStorageEntry) { + set(input: CommandContextEntry) { this.inputsByName.set(input.name, input); } get( - inputName: CommandStorageEntry['name'], - ): CommandStorageEntry | undefined; + inputName: CommandContextEntry['name'], + ): CommandContextEntry | undefined; get( - inputName: CommandStorageEntry['name'], + inputName: CommandContextEntry['name'], errorOnMissing: false, - ): CommandStorageEntry | undefined; + ): CommandContextEntry | undefined; get( - inputName: CommandStorageEntry['name'], + inputName: CommandContextEntry['name'], errorOnMissing: true, - ): CommandStorageEntry; + ): CommandContextEntry; get( - inputName: CommandStorageEntry['name'], + inputName: CommandContextEntry['name'], errorOnMissing = false, - ): CommandStorageEntry | undefined { + ): CommandContextEntry | undefined { const input = this.inputsByName.get(inputName) as - | CommandStorageEntry + | CommandContextEntry | undefined; if (errorOnMissing) { if (!input || input.value === undefined) { @@ -63,7 +63,7 @@ export class CommandStorage { * Copy all inputs of the other command storage with this one. * Note that if an input already exists, it will **not** be overwritten. */ - mergeWith(otherStorage: CommandStorage): void { + mergeWith(otherStorage: CommandContext): void { for (const input of otherStorage.inputsByName.values()) { this.add(input); } diff --git a/commands/generate.command.ts b/commands/generate.command.ts index d8542ccef..ea23643b7 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -6,7 +6,7 @@ import { AbstractCollection, CollectionFactory } from '../lib/schematics'; import { Schematic } from '../lib/schematics/nest.collection'; import { loadConfiguration } from '../lib/utils/load-configuration'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command-storage'; +import { CommandContext } from './command-context'; export class GenerateCommand extends AbstractCommand { public async load(program: CommanderStatic): Promise { @@ -56,7 +56,7 @@ export class GenerateCommand extends AbstractCommand { path: string, command: Command, ) => { - const commandInputs = new CommandStorage(); + const commandInputs = new CommandContext(); commandInputs.add({ name: 'dry-run', value: !!command.dryRun }); diff --git a/commands/index.ts b/commands/index.ts index e5face162..021a72a3b 100644 --- a/commands/index.ts +++ b/commands/index.ts @@ -1,2 +1,2 @@ export * from './command.loader'; -export * from './command-storage'; +export * from './command-context'; diff --git a/commands/new.command.ts b/commands/new.command.ts index a71f4746c..486eb6cd0 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { NewAction } from '../actions'; import { Collection } from '../lib/schematics'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command-storage'; +import { CommandContext } from './command-context'; export class NewCommand extends AbstractCommand { public load(program: CommanderStatic) { @@ -34,7 +34,7 @@ export class NewCommand extends AbstractCommand { ) .option('--strict', 'Enables strict mode in TypeScript.', false) .action(async (name: string, command: Command) => { - const commandOptions = new CommandStorage(); + const commandOptions = new CommandContext(); commandOptions.add({ name: 'directory', @@ -82,7 +82,7 @@ export class NewCommand extends AbstractCommand { value: command.language, }); - const inputs = new CommandStorage(); + const inputs = new CommandContext(); inputs.add({ name: 'name', value: name }); await this.action.handle(inputs, commandOptions); diff --git a/commands/start.command.ts b/commands/start.command.ts index 7ab5c1705..377e61ad6 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -2,7 +2,7 @@ import { Command, CommanderStatic } from 'commander'; import type { StartAction } from '../actions'; import { ERROR_PREFIX, INFO_PREFIX } from '../lib/ui'; import { AbstractCommand } from './abstract.command'; -import { CommandStorage } from './command-storage'; +import { CommandContext } from './command-context'; import type { BuilderVariant } from '../lib/configuration'; export class StartCommand extends AbstractCommand { @@ -40,7 +40,7 @@ export class StartCommand extends AbstractCommand { ) .description('Run Nest application.') .action(async (app: string, command: Command) => { - const commandOptions = new CommandStorage(); + const commandOptions = new CommandContext(); commandOptions.add({ name: 'config', @@ -109,7 +109,7 @@ export class StartCommand extends AbstractCommand { value: command.typeCheck, }); - const inputs = new CommandStorage(); + const inputs = new CommandContext(); inputs.add({ name: 'app', value: app }); try { diff --git a/lib/compiler/helpers/get-builder.ts b/lib/compiler/helpers/get-builder.ts index 3a43ce90f..16935d631 100644 --- a/lib/compiler/helpers/get-builder.ts +++ b/lib/compiler/helpers/get-builder.ts @@ -1,4 +1,4 @@ -import { CommandStorage } from '../../../commands'; +import { CommandContext } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getBuilder( configuration: Required, - cmdOptions: CommandStorage, + cmdOptions: CommandContext, appName: string, ) { const builderValue = getValueOrDefault( diff --git a/lib/compiler/helpers/get-tsc-config.path.ts b/lib/compiler/helpers/get-tsc-config.path.ts index 320f50550..1be7bd8f7 100644 --- a/lib/compiler/helpers/get-tsc-config.path.ts +++ b/lib/compiler/helpers/get-tsc-config.path.ts @@ -1,4 +1,4 @@ -import { CommandStorage } from '../../../commands'; +import { CommandContext } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getDefaultTsconfigPath } from '../../utils/get-default-tsconfig-path'; import { getValueOrDefault } from './get-value-or-default'; @@ -12,7 +12,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getTscConfigPath( configuration: Required, - cmdOptions: CommandStorage, + cmdOptions: CommandContext, appName: string, ) { let tsconfigPath = getValueOrDefault( diff --git a/lib/compiler/helpers/get-value-or-default.ts b/lib/compiler/helpers/get-value-or-default.ts index 4ecfe6ab8..29506cc72 100644 --- a/lib/compiler/helpers/get-value-or-default.ts +++ b/lib/compiler/helpers/get-value-or-default.ts @@ -1,4 +1,4 @@ -import { CommandStorageEntry, CommandStorage } from '../../../commands'; +import { CommandContextEntry, CommandContext } from '../../../commands'; import { Configuration } from '../../configuration'; export function getValueOrDefault( @@ -14,7 +14,7 @@ export function getValueOrDefault( | 'exec' | 'builder' | 'typeCheck', - options: CommandStorageEntry[] | CommandStorage = [], + options: CommandContextEntry[] | CommandContext = [], defaultValue?: T, ): T { const item = Array.isArray(options) diff --git a/lib/compiler/helpers/get-webpack-config-path.ts b/lib/compiler/helpers/get-webpack-config-path.ts index f53836216..738db39f0 100644 --- a/lib/compiler/helpers/get-webpack-config-path.ts +++ b/lib/compiler/helpers/get-webpack-config-path.ts @@ -1,4 +1,4 @@ -import { CommandStorage } from '../../../commands'; +import { CommandContext } from '../../../commands'; import { Builder, Configuration } from '../../configuration'; import { getValueOrDefault } from './get-value-or-default'; @@ -11,7 +11,7 @@ import { getValueOrDefault } from './get-value-or-default'; */ export function getWebpackConfigPath( configuration: Required, - cmdOptions: CommandStorage, + cmdOptions: CommandContext, appName: string, ) { let webpackPath = getValueOrDefault( diff --git a/lib/compiler/webpack-compiler.ts b/lib/compiler/webpack-compiler.ts index 56eb83d83..17732f539 100644 --- a/lib/compiler/webpack-compiler.ts +++ b/lib/compiler/webpack-compiler.ts @@ -1,6 +1,6 @@ import { existsSync } from 'fs'; import { join } from 'path'; -import { CommandStorage } from '../../commands'; +import { CommandContext } from '../../commands'; import { Configuration } from '../configuration'; import { INFO_PREFIX } from '../ui'; import { AssetsManager } from './assets-manager'; @@ -20,7 +20,7 @@ type WebpackConfigFactoryOrConfig = | webpack.Configuration; type WebpackCompilerExtras = { - inputs: CommandStorage; + inputs: CommandContext; assetsManager: AssetsManager; webpackConfigFactoryOrConfig: | WebpackConfigFactoryOrConfig diff --git a/lib/utils/project-utils.ts b/lib/utils/project-utils.ts index aa58b9476..c28a689f7 100644 --- a/lib/utils/project-utils.ts +++ b/lib/utils/project-utils.ts @@ -1,6 +1,6 @@ import * as inquirer from 'inquirer'; import { Answers, Question } from 'inquirer'; -import { CommandStorage } from '../../commands'; +import { CommandContext } from '../../commands'; import { getValueOrDefault } from '../compiler/helpers/get-value-or-default'; import { Configuration, ProjectConfiguration } from '../configuration'; import { generateSelect } from '../questions/questions'; @@ -142,7 +142,7 @@ export function moveDefaultProjectToStart( export function hasValidOptionFlag( queriedOptionName: string, - options: CommandStorage, + options: CommandContext, queriedValue: string | number | boolean = true, ): boolean { const option = options.get(queriedOptionName); diff --git a/test/lib/questions/questions.spec.ts b/test/lib/questions/questions.spec.ts index 23e1e9acc..2faaa6932 100644 --- a/test/lib/questions/questions.spec.ts +++ b/test/lib/questions/questions.spec.ts @@ -1,5 +1,5 @@ import { Question } from 'inquirer'; -import { CommandStorageEntry } from '../../../commands/command-storage'; +import { CommandContextEntry } from '../../../commands/command-context'; import { generateInput, generateSelect, @@ -8,7 +8,7 @@ import { describe('Questions', () => { describe('generateInput', () => { it('should return an input question', () => { - const input: CommandStorageEntry = { + const input: CommandContextEntry = { name: 'name', value: 'test', };