From 4e4668e77ddbc7948b43a6e6fe7bdce797a08ed3 Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 31 Jul 2023 17:20:04 +0200 Subject: [PATCH 01/32] feat: case naming --- actions/add.action.ts | 6 +++ actions/generate.action.ts | 23 ++++++---- actions/new.action.ts | 2 +- commands/generate.command.ts | 9 ++++ lib/schematics/schematic.option.ts | 23 +++++----- lib/utils/formatting.ts | 46 +++++++++++++++++++- package-lock.json | 17 ++++++++ package.json | 3 +- test/lib/schematics/schematic.option.spec.ts | 12 +++-- test/lib/utils/formatting.spec.ts | 32 ++++++++++++++ 10 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 test/lib/utils/formatting.spec.ts diff --git a/actions/add.action.ts b/actions/add.action.ts index 5c5d1d09e..9b977b6df 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -19,6 +19,8 @@ import { shouldAskForProject, } from '../lib/utils/project-utils'; import { AbstractAction } from './abstract.action'; +import { CaseType } from '../lib/utils/formatting'; + const schematicName = 'nest-add'; @@ -122,12 +124,16 @@ export class AddAction extends AbstractAction { ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); const schematicOptions: SchematicOption[] = []; + const caseType = options + .find((option) => option.name === 'caseNaming')?.value as CaseType schematicOptions.push( new SchematicOption( 'sourceRoot', options.find((option) => option.name === 'sourceRoot')!.value as string, + { caseType } ), ); + const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; try { diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 22c3ff012..2dbf97fab 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -6,7 +6,7 @@ import { AbstractCollection, Collection, CollectionFactory, - SchematicOption, + SchematicOption, SchematicOptionConfig, } from '../lib/schematics'; import { MESSAGES } from '../lib/ui'; import { loadConfiguration } from '../lib/utils/load-configuration'; @@ -19,6 +19,8 @@ import { shouldGenerateSpec, } from '../lib/utils/project-utils'; import { AbstractAction } from './abstract.action'; +import { CaseType } from '../lib/utils/formatting'; + export class GenerateAction extends AbstractAction { public async handle(inputs: Input[], options: Input[]) { @@ -44,9 +46,12 @@ const generateFiles = async (inputs: Input[]) => { const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, ); - const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); + + const caseType = inputs.find((option) => option.name === 'case')!.value as CaseType; + + const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { caseType }); schematicOptions.push( - new SchematicOption('language', configuration.language), + new SchematicOption('language', configuration.language, {caseType}), ); const configurationProjects = configuration.projects; @@ -125,11 +130,11 @@ const generateFiles = async (inputs: Input[]) => { } } - schematicOptions.push(new SchematicOption('sourceRoot', sourceRoot)); - schematicOptions.push(new SchematicOption('spec', generateSpec)); - schematicOptions.push(new SchematicOption('flat', generateFlat)); + schematicOptions.push(new SchematicOption('sourceRoot', sourceRoot, { caseType })); + schematicOptions.push(new SchematicOption('spec', generateSpec, { caseType })); + schematicOptions.push(new SchematicOption('flat', generateFlat, { caseType })); schematicOptions.push( - new SchematicOption('specFileSuffix', generateSpecFileSuffix), + new SchematicOption('specFileSuffix', generateSpecFileSuffix, { caseType }), ); try { const schematicInput = inputs.find((input) => input.name === 'schematic'); @@ -144,12 +149,12 @@ const generateFiles = async (inputs: Input[]) => { } }; -const mapSchematicOptions = (inputs: Input[]): SchematicOption[] => { +const mapSchematicOptions = (inputs: Input[], config: SchematicOptionConfig): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; inputs.forEach((input) => { if (!excludedInputNames.includes(input.name) && input.value !== undefined) { - options.push(new SchematicOption(input.name, input.value)); + options.push(new SchematicOption(input.name, input.value, config)); } }); return options; diff --git a/actions/new.action.ts b/actions/new.action.ts index 99c3fef50..a1fb72292 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -130,7 +130,7 @@ const mapSchematicOptions = (options: Input[]): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: Input) => { if (option.name !== 'skip-install') { - schematicOptions.push(new SchematicOption(option.name, option.value)); + schematicOptions.push(new SchematicOption(option.name, option.value, { caseType: 'kebab-or-snake'})); } return schematicOptions; }, diff --git a/commands/generate.command.ts b/commands/generate.command.ts index d6613afe5..f6e3b62f4 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -48,6 +48,10 @@ export class GenerateCommand extends AbstractCommand { '-c, --collection [collectionName]', 'Schematics collection to use.', ) + .option( + '--caseNaming [caseType]', + `Casing type for generated elements. Available options: "pascal", "camel", "snake-or-kebap" (default).`, + ) .action( async ( schematic: string, @@ -93,6 +97,11 @@ export class GenerateCommand extends AbstractCommand { value: command.skipImport, }); + options.push({ + name: 'caseNaming', + value: command.caseNaming, + }); + const inputs: Input[] = []; inputs.push({ name: 'schematic', value: schematic }); inputs.push({ name: 'name', value: name }); diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index 51b603df6..c5c4876b7 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -1,13 +1,18 @@ -import { normalizeToKebabOrSnakeCase } from '../utils/formatting'; +import { normalizeToCase, formatString, CaseType } from '../utils/formatting'; + +export type SchematicOptionConfig = { + caseType?: CaseType; +} export class SchematicOption { constructor( private name: string, private value: boolean | string, + private schematicOptionConfig: SchematicOptionConfig ) {} get normalizedName() { - return normalizeToKebabOrSnakeCase(this.name); + return normalizeToCase(this.name, 'kebab-or-snake'); } public toCommandString(): string { @@ -28,13 +33,11 @@ export class SchematicOption { } private format() { - return normalizeToKebabOrSnakeCase(this.value as string) - .split('') - .reduce((content, char) => { - if (char === '(' || char === ')' || char === '[' || char === ']') { - return `${content}\\${char}`; - } - return `${content}${char}`; - }, ''); + return formatString( + normalizeToCase( + this.value as string, + this.schematicOptionConfig.caseType || 'kebab-or-snake' + ) + ); } } diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 3f9b39bdf..3be3407c4 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -1,7 +1,51 @@ +import { camelCase, kebabCase, pascalCase, snakeCase, capitalCase } from 'case-anything' + +export type CaseType = 'kebab' | 'snake' | 'camel' | 'pascal' | 'capital' | 'kebab-or-snake'; + /** * * @param str - * @returns formated string + * @param caseType + * @returns formatted string + * @description normalizes input to supported path and file name format. + * Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. + */ +export const normalizeToCase = (str: string, caseType: CaseType = 'kebab-or-snake') => { + + switch (caseType) { + case 'kebab': + return kebabCase(str); + case 'snake': + return kebabCase(str); + case 'camel': + return camelCase(str); + case 'pascal': + return pascalCase(str); + case 'capital': + return capitalCase(str); + // For legacy purposes + case 'kebab-or-snake': + return normalizeToKebabOrSnakeCase(str); + default: + throw new Error(`Error! case type ${caseType} is not supported.`) + } + +} + +export const formatString = (str: string) => { + return str.split('') + .reduce((content, char) => { + if (char === '(' || char === ')' || char === '[' || char === ']') { + return `${content}\\${char}`; + } + return `${content}${char}`; + }, '') +} + +/** + * + * @param str + * @returns formatted string * @description normalizes input to supported path and file name format. * Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. */ diff --git a/package-lock.json b/package-lock.json index 584552afc..ddc31ab20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@angular-devkit/schematics": "16.1.4", "@angular-devkit/schematics-cli": "16.1.4", "@nestjs/schematics": "^10.0.1", + "case-anything": "^2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", @@ -5598,6 +5599,17 @@ } ] }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -23723,6 +23735,11 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==" }, + "case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==" + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index 19a6d43f4..2d063b16e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nestjs/cli", - "version": "10.1.11", + "version": "10.2.0", "description": "Nest - modern, fast, powerful node.js web framework (@cli)", "publishConfig": { "access": "public" @@ -42,6 +42,7 @@ "@angular-devkit/schematics": "16.1.4", "@angular-devkit/schematics-cli": "16.1.4", "@nestjs/schematics": "^10.0.1", + "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", diff --git a/test/lib/schematics/schematic.option.spec.ts b/test/lib/schematics/schematic.option.spec.ts index de4f13451..77bc25d8f 100644 --- a/test/lib/schematics/schematic.option.spec.ts +++ b/test/lib/schematics/schematic.option.spec.ts @@ -1,4 +1,4 @@ -import { SchematicOption } from '../../../lib/schematics'; +import {SchematicOption, SchematicOptionConfig} from '../../../lib/schematics'; interface TestOption { input: string; @@ -100,7 +100,10 @@ describe('Schematic Option', () => { tests.forEach((test) => { it(test.description, () => { - const option = new SchematicOption(test.option, test.input); + const config = { + caseType: 'kebab-or-snake', + } as SchematicOptionConfig + const option = new SchematicOption(test.option, test.input, config); if (isFlagTest(test)) { if (test.input) { @@ -117,7 +120,10 @@ describe('Schematic Option', () => { }); it('should manage boolean option', () => { - const option = new SchematicOption('dry-run', false); + const config = { + caseType: 'kebab-or-snake', + } as SchematicOptionConfig + const option = new SchematicOption('dry-run', false, config); expect(option.toCommandString()).toEqual('--no-dry-run'); }); }); diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts new file mode 100644 index 000000000..b09b453c6 --- /dev/null +++ b/test/lib/utils/formatting.spec.ts @@ -0,0 +1,32 @@ +import { normalizeToCase, CaseType } from '../../../lib/utils/formatting'; + +type TestSuite = { + description: string; + input: string; + caseType: CaseType; + expected: string; +}; + +describe('Format strings', () => { + + const tests: TestSuite[] = [ + { + description: 'From kebap to camel', + input: 'my-app', + caseType: 'camel', + expected: 'myApp', + }, + ]; + + tests.forEach((test) => { + it(test.description, () => { + + expect( + normalizeToCase(test.input, test.caseType) + ).toEqual( + test.expected + ); + }); + }); + +}); From f0065ed9e6e98f7a63e15397db9c37d88c277a8d Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 31 Jul 2023 17:22:46 +0200 Subject: [PATCH 02/32] chore: linting and formatting --- actions/add.action.ts | 7 ++-- actions/generate.action.ts | 30 +++++++++---- actions/new.action.ts | 6 ++- lib/schematics/schematic.option.ts | 12 +++--- lib/utils/formatting.ts | 44 +++++++++++++------- test/lib/schematics/schematic.option.spec.ts | 9 ++-- test/lib/utils/formatting.spec.ts | 39 +++++++---------- 7 files changed, 85 insertions(+), 62 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 9b977b6df..29815230a 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -21,7 +21,6 @@ import { import { AbstractAction } from './abstract.action'; import { CaseType } from '../lib/utils/formatting'; - const schematicName = 'nest-add'; export class AddAction extends AbstractAction { @@ -124,13 +123,13 @@ export class AddAction extends AbstractAction { ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); const schematicOptions: SchematicOption[] = []; - const caseType = options - .find((option) => option.name === 'caseNaming')?.value as CaseType + const caseType = options.find((option) => option.name === 'caseNaming') + ?.value as CaseType; schematicOptions.push( new SchematicOption( 'sourceRoot', options.find((option) => option.name === 'sourceRoot')!.value as string, - { caseType } + { caseType }, ), ); diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 2dbf97fab..b6ddf78fe 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -6,7 +6,8 @@ import { AbstractCollection, Collection, CollectionFactory, - SchematicOption, SchematicOptionConfig, + SchematicOption, + SchematicOptionConfig, } from '../lib/schematics'; import { MESSAGES } from '../lib/ui'; import { loadConfiguration } from '../lib/utils/load-configuration'; @@ -21,7 +22,6 @@ import { import { AbstractAction } from './abstract.action'; import { CaseType } from '../lib/utils/formatting'; - export class GenerateAction extends AbstractAction { public async handle(inputs: Input[], options: Input[]) { await generateFiles(inputs.concat(options)); @@ -47,11 +47,14 @@ const generateFiles = async (inputs: Input[]) => { collectionOption || configuration.collection || Collection.NESTJS, ); - const caseType = inputs.find((option) => option.name === 'case')!.value as CaseType; + const caseType = inputs.find((option) => option.name === 'case')! + .value as CaseType; - const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { caseType }); + const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { + caseType, + }); schematicOptions.push( - new SchematicOption('language', configuration.language, {caseType}), + new SchematicOption('language', configuration.language, { caseType }), ); const configurationProjects = configuration.projects; @@ -130,9 +133,15 @@ const generateFiles = async (inputs: Input[]) => { } } - schematicOptions.push(new SchematicOption('sourceRoot', sourceRoot, { caseType })); - schematicOptions.push(new SchematicOption('spec', generateSpec, { caseType })); - schematicOptions.push(new SchematicOption('flat', generateFlat, { caseType })); + schematicOptions.push( + new SchematicOption('sourceRoot', sourceRoot, { caseType }), + ); + schematicOptions.push( + new SchematicOption('spec', generateSpec, { caseType }), + ); + schematicOptions.push( + new SchematicOption('flat', generateFlat, { caseType }), + ); schematicOptions.push( new SchematicOption('specFileSuffix', generateSpecFileSuffix, { caseType }), ); @@ -149,7 +158,10 @@ const generateFiles = async (inputs: Input[]) => { } }; -const mapSchematicOptions = (inputs: Input[], config: SchematicOptionConfig): SchematicOption[] => { +const mapSchematicOptions = ( + inputs: Input[], + config: SchematicOptionConfig, +): SchematicOption[] => { const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; inputs.forEach((input) => { diff --git a/actions/new.action.ts b/actions/new.action.ts index a1fb72292..1f3c99873 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -130,7 +130,11 @@ const mapSchematicOptions = (options: Input[]): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: Input) => { if (option.name !== 'skip-install') { - schematicOptions.push(new SchematicOption(option.name, option.value, { caseType: 'kebab-or-snake'})); + schematicOptions.push( + new SchematicOption(option.name, option.value, { + caseType: 'kebab-or-snake', + }), + ); } return schematicOptions; }, diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index c5c4876b7..bc73076b5 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -2,13 +2,13 @@ import { normalizeToCase, formatString, CaseType } from '../utils/formatting'; export type SchematicOptionConfig = { caseType?: CaseType; -} +}; export class SchematicOption { constructor( private name: string, private value: boolean | string, - private schematicOptionConfig: SchematicOptionConfig + private schematicOptionConfig: SchematicOptionConfig, ) {} get normalizedName() { @@ -34,10 +34,10 @@ export class SchematicOption { private format() { return formatString( - normalizeToCase( - this.value as string, - this.schematicOptionConfig.caseType || 'kebab-or-snake' - ) + normalizeToCase( + this.value as string, + this.schematicOptionConfig.caseType || 'kebab-or-snake', + ), ); } } diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 3be3407c4..172147e55 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -1,6 +1,18 @@ -import { camelCase, kebabCase, pascalCase, snakeCase, capitalCase } from 'case-anything' +import { + camelCase, + kebabCase, + pascalCase, + snakeCase, + capitalCase, +} from 'case-anything'; -export type CaseType = 'kebab' | 'snake' | 'camel' | 'pascal' | 'capital' | 'kebab-or-snake'; +export type CaseType = + | 'kebab' + | 'snake' + | 'camel' + | 'pascal' + | 'capital' + | 'kebab-or-snake'; /** * @@ -10,13 +22,15 @@ export type CaseType = 'kebab' | 'snake' | 'camel' | 'pascal' | 'capital' | 'keb * @description normalizes input to supported path and file name format. * Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. */ -export const normalizeToCase = (str: string, caseType: CaseType = 'kebab-or-snake') => { - +export const normalizeToCase = ( + str: string, + caseType: CaseType = 'kebab-or-snake', +) => { switch (caseType) { case 'kebab': return kebabCase(str); case 'snake': - return kebabCase(str); + return snakeCase(str); case 'camel': return camelCase(str); case 'pascal': @@ -27,20 +41,18 @@ export const normalizeToCase = (str: string, caseType: CaseType = 'kebab-or-snak case 'kebab-or-snake': return normalizeToKebabOrSnakeCase(str); default: - throw new Error(`Error! case type ${caseType} is not supported.`) + throw new Error(`Error! case type ${caseType} is not supported.`); } - -} +}; export const formatString = (str: string) => { - return str.split('') - .reduce((content, char) => { - if (char === '(' || char === ')' || char === '[' || char === ']') { - return `${content}\\${char}`; - } - return `${content}${char}`; - }, '') -} + return str.split('').reduce((content, char) => { + if (char === '(' || char === ')' || char === '[' || char === ']') { + return `${content}\\${char}`; + } + return `${content}${char}`; + }, ''); +}; /** * diff --git a/test/lib/schematics/schematic.option.spec.ts b/test/lib/schematics/schematic.option.spec.ts index 77bc25d8f..7f61bcb35 100644 --- a/test/lib/schematics/schematic.option.spec.ts +++ b/test/lib/schematics/schematic.option.spec.ts @@ -1,4 +1,7 @@ -import {SchematicOption, SchematicOptionConfig} from '../../../lib/schematics'; +import { + SchematicOption, + SchematicOptionConfig, +} from '../../../lib/schematics'; interface TestOption { input: string; @@ -102,7 +105,7 @@ describe('Schematic Option', () => { it(test.description, () => { const config = { caseType: 'kebab-or-snake', - } as SchematicOptionConfig + } as SchematicOptionConfig; const option = new SchematicOption(test.option, test.input, config); if (isFlagTest(test)) { @@ -122,7 +125,7 @@ describe('Schematic Option', () => { it('should manage boolean option', () => { const config = { caseType: 'kebab-or-snake', - } as SchematicOptionConfig + } as SchematicOptionConfig; const option = new SchematicOption('dry-run', false, config); expect(option.toCommandString()).toEqual('--no-dry-run'); }); diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts index b09b453c6..774e3864e 100644 --- a/test/lib/utils/formatting.spec.ts +++ b/test/lib/utils/formatting.spec.ts @@ -1,32 +1,25 @@ import { normalizeToCase, CaseType } from '../../../lib/utils/formatting'; type TestSuite = { - description: string; - input: string; - caseType: CaseType; - expected: string; + description: string; + input: string; + caseType: CaseType; + expected: string; }; describe('Format strings', () => { + const tests: TestSuite[] = [ + { + description: 'From kebap to camel', + input: 'my-app', + caseType: 'camel', + expected: 'myApp', + }, + ]; - const tests: TestSuite[] = [ - { - description: 'From kebap to camel', - input: 'my-app', - caseType: 'camel', - expected: 'myApp', - }, - ]; - - tests.forEach((test) => { - it(test.description, () => { - - expect( - normalizeToCase(test.input, test.caseType) - ).toEqual( - test.expected - ); - }); + tests.forEach((test) => { + it(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); }); - + }); }); From bfb061af0a3154926f49e992e5aa90b526e1c91b Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 31 Jul 2023 17:27:37 +0200 Subject: [PATCH 03/32] feat: make schematic option config optional --- lib/schematics/schematic.option.ts | 6 +++++- test/lib/schematics/schematic.option.spec.ts | 10 ++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index bc73076b5..d5a57d7f3 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -4,11 +4,15 @@ export type SchematicOptionConfig = { caseType?: CaseType; }; +const defaultSchematicOptionConfig: SchematicOptionConfig = { + caseType: 'kebab-or-snake', +}; + export class SchematicOption { constructor( private name: string, private value: boolean | string, - private schematicOptionConfig: SchematicOptionConfig, + private schematicOptionConfig = defaultSchematicOptionConfig, ) {} get normalizedName() { diff --git a/test/lib/schematics/schematic.option.spec.ts b/test/lib/schematics/schematic.option.spec.ts index 7f61bcb35..3e3713461 100644 --- a/test/lib/schematics/schematic.option.spec.ts +++ b/test/lib/schematics/schematic.option.spec.ts @@ -103,10 +103,7 @@ describe('Schematic Option', () => { tests.forEach((test) => { it(test.description, () => { - const config = { - caseType: 'kebab-or-snake', - } as SchematicOptionConfig; - const option = new SchematicOption(test.option, test.input, config); + const option = new SchematicOption(test.option, test.input); if (isFlagTest(test)) { if (test.input) { @@ -123,10 +120,7 @@ describe('Schematic Option', () => { }); it('should manage boolean option', () => { - const config = { - caseType: 'kebab-or-snake', - } as SchematicOptionConfig; - const option = new SchematicOption('dry-run', false, config); + const option = new SchematicOption('dry-run', false); expect(option.toCommandString()).toEqual('--no-dry-run'); }); }); From d2cc2883502f106b8898a2d20d566db1e49f06ad Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 31 Jul 2023 22:46:54 +0200 Subject: [PATCH 04/32] feat: update jsdoc --- lib/utils/formatting.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 172147e55..c78d46bbb 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -19,8 +19,8 @@ export type CaseType = * @param str * @param caseType * @returns formatted string - * @description normalizes input to supported path and file name format. - * Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. + * @description normalizes input to a given case format. + * Available formats are: 'kebab' | 'snake' | 'camel' | 'pascal' | 'capital'. */ export const normalizeToCase = ( str: string, From 3b015b2b7b034b84def1d9118c4164b835aab68f Mon Sep 17 00:00:00 2001 From: espoal Date: Thu, 3 Aug 2023 15:26:36 +0200 Subject: [PATCH 05/32] chore: fixing typos --- commands/generate.command.ts | 2 +- lib/utils/formatting.ts | 3 +-- test/lib/utils/formatting.spec.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/commands/generate.command.ts b/commands/generate.command.ts index f6e3b62f4..2df9c0b96 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -50,7 +50,7 @@ export class GenerateCommand extends AbstractCommand { ) .option( '--caseNaming [caseType]', - `Casing type for generated elements. Available options: "pascal", "camel", "snake-or-kebap" (default).`, + `Casing type for generated elements. Available options: "pascal", "camel", "kebab-or-snake" (default).`, ) .action( async ( diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index c78d46bbb..9a2fd3a40 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -17,10 +17,9 @@ export type CaseType = /** * * @param str - * @param caseType + * @param caseType CaseType * @returns formatted string * @description normalizes input to a given case format. - * Available formats are: 'kebab' | 'snake' | 'camel' | 'pascal' | 'capital'. */ export const normalizeToCase = ( str: string, diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts index 774e3864e..c5d07393a 100644 --- a/test/lib/utils/formatting.spec.ts +++ b/test/lib/utils/formatting.spec.ts @@ -10,7 +10,7 @@ type TestSuite = { describe('Format strings', () => { const tests: TestSuite[] = [ { - description: 'From kebap to camel', + description: 'From kebab to camel', input: 'my-app', caseType: 'camel', expected: 'myApp', From 40c50323f02978d47a277e7e88687cb206db2708 Mon Sep 17 00:00:00 2001 From: espoal Date: Thu, 3 Aug 2023 15:28:26 +0200 Subject: [PATCH 06/32] fix: consistent option naming --- actions/generate.action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index b6ddf78fe..ed07085a1 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -47,7 +47,7 @@ const generateFiles = async (inputs: Input[]) => { collectionOption || configuration.collection || Collection.NESTJS, ); - const caseType = inputs.find((option) => option.name === 'case')! + const caseType = inputs.find((option) => option.name === 'caseNaming')! .value as CaseType; const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { From 9235d0d757ca647b98b64277603bf33a5420ae8a Mon Sep 17 00:00:00 2001 From: espoal Date: Thu, 3 Aug 2023 15:40:03 +0200 Subject: [PATCH 07/32] feat: improved switch case loop --- lib/utils/formatting.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 9a2fd3a40..5a392577b 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -23,7 +23,7 @@ export type CaseType = */ export const normalizeToCase = ( str: string, - caseType: CaseType = 'kebab-or-snake', + caseType: CaseType, ) => { switch (caseType) { case 'kebab': @@ -38,9 +38,8 @@ export const normalizeToCase = ( return capitalCase(str); // For legacy purposes case 'kebab-or-snake': - return normalizeToKebabOrSnakeCase(str); default: - throw new Error(`Error! case type ${caseType} is not supported.`); + return normalizeToKebabOrSnakeCase(str); } }; From 0eef7e9abc326cb3db845f81cd9db4ed9cace061 Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 27 Sep 2023 14:25:07 +0200 Subject: [PATCH 08/32] chore: package.json versioning --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2d063b16e..264ec56f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nestjs/cli", - "version": "10.2.0", + "version": "10.1.18", "description": "Nest - modern, fast, powerful node.js web framework (@cli)", "publishConfig": { "access": "public" From 33301f4f7c37d110d5de3aca71a8907baa1d463a Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 27 Sep 2023 18:05:36 +0200 Subject: [PATCH 09/32] feat: nest cli --- actions/new.action.ts | 12 +++++++++--- commands/new.command.ts | 9 +++++++++ lib/configuration/configuration.ts | 1 + lib/schematics/abstract.collection.ts | 1 + package-lock.json | 6 +++--- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/actions/new.action.ts b/actions/new.action.ts index 1f3c99873..88fbbde4d 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -20,7 +20,7 @@ import { SchematicOption, } from '../lib/schematics'; import { EMOJIS, MESSAGES } from '../lib/ui'; -import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; +import {CaseType, normalizeToKebabOrSnakeCase} from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { @@ -113,6 +113,11 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async (args: Input[], options: Input[]) => { + + console.log({ options, args }) + const caseNaming = (options + .find(({ name }) => name === 'caseNaming') + ?.value || 'kebab-or-snake') as CaseType; const collectionName = options.find( (option) => option.name === 'collection' && option.value != null, )!.value; @@ -121,18 +126,19 @@ const generateApplicationFiles = async (args: Input[], options: Input[]) => { ); const schematicOptions: SchematicOption[] = mapSchematicOptions( args.concat(options), + caseNaming ); await collection.execute('application', schematicOptions); console.info(); }; -const mapSchematicOptions = (options: Input[]): SchematicOption[] => { +const mapSchematicOptions = (options: Input[], caseNaming: CaseType): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: Input) => { if (option.name !== 'skip-install') { schematicOptions.push( new SchematicOption(option.name, option.value, { - caseType: 'kebab-or-snake', + caseType: caseNaming, }), ); } diff --git a/commands/new.command.ts b/commands/new.command.ts index 28b595e1e..e54801211 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -32,6 +32,10 @@ export class NewCommand extends AbstractCommand { Collection.NESTJS, ) .option('--strict', 'Enables strict mode in TypeScript.', false) + .option( + '--caseNaming [caseType]', + `Casing type for generated elements. Available options: "pascal", "camel", "kebab-or-snake" (default).`, + ) .action(async (name: string, command: Command) => { const options: Input[] = []; const availableLanguages = ['js', 'ts', 'javascript', 'typescript']; @@ -46,6 +50,11 @@ export class NewCommand extends AbstractCommand { }); options.push({ name: 'collection', value: command.collection }); + options.push({ + name: 'caseNaming', + value: command.caseNaming, + }); + if (!!command.language) { const lowercasedLanguage = command.language.toLowerCase(); const langMatch = availableLanguages.includes(lowercasedLanguage); diff --git a/lib/configuration/configuration.ts b/lib/configuration/configuration.ts index af99cf1d4..978eaba90 100644 --- a/lib/configuration/configuration.ts +++ b/lib/configuration/configuration.ts @@ -77,6 +77,7 @@ export interface GenerateOptions { spec?: boolean | Record; flat?: boolean; specFileSuffix?: string; + caseNaming?: string; } export interface ProjectConfiguration { diff --git a/lib/schematics/abstract.collection.ts b/lib/schematics/abstract.collection.ts index bf268583d..1a10210cd 100644 --- a/lib/schematics/abstract.collection.ts +++ b/lib/schematics/abstract.collection.ts @@ -15,6 +15,7 @@ export abstract class AbstractCollection { ) { let command = this.buildCommandLine(name, options); command = extraFlags ? command.concat(` ${extraFlags}`) : command; + console.log({ command }); await this.runner.run(command); } diff --git a/package-lock.json b/package-lock.json index ddc31ab20..4ceca0f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@nestjs/cli", - "version": "10.1.11", + "version": "10.1.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@nestjs/cli", - "version": "10.1.11", + "version": "10.1.18", "license": "MIT", "dependencies": { "@angular-devkit/core": "16.1.4", "@angular-devkit/schematics": "16.1.4", "@angular-devkit/schematics-cli": "16.1.4", "@nestjs/schematics": "^10.0.1", - "case-anything": "^2.1.13", + "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", From b59ae2e933ef9e7da0614745c5a5c8cc598fc2dd Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 13:12:31 +0200 Subject: [PATCH 10/32] feat: read case naming from cli --- actions/generate.action.ts | 7 +++++-- actions/new.action.ts | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index ed07085a1..dbdc10c6a 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -30,6 +30,7 @@ export class GenerateAction extends AbstractAction { const generateFiles = async (inputs: Input[]) => { const configuration = await loadConfiguration(); + console.log({ configuration }); const collectionOption = inputs.find( (option) => option.name === 'collection', )!.value as string; @@ -47,8 +48,10 @@ const generateFiles = async (inputs: Input[]) => { collectionOption || configuration.collection || Collection.NESTJS, ); - const caseType = inputs.find((option) => option.name === 'caseNaming')! - .value as CaseType; + const caseType = ( + configuration?.generateOptions?.caseNaming + || 'kebab-or-snake' + ) as CaseType; const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { caseType, diff --git a/actions/new.action.ts b/actions/new.action.ts index 88fbbde4d..e3af206b0 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -113,11 +113,11 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async (args: Input[], options: Input[]) => { - - console.log({ options, args }) const caseNaming = (options .find(({ name }) => name === 'caseNaming') ?.value || 'kebab-or-snake') as CaseType; + console.log({ options, args, caseNaming }) + const collectionName = options.find( (option) => option.name === 'collection' && option.value != null, )!.value; From 759215080e25fa5fb4897dee218fe3a891feec06 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 13:17:22 +0200 Subject: [PATCH 11/32] chore: cleanup --- actions/add.action.ts | 8 +++++--- actions/generate.action.ts | 1 - lib/schematics/abstract.collection.ts | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 29815230a..983ee3629 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -123,9 +123,11 @@ export class AddAction extends AbstractAction { ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); const schematicOptions: SchematicOption[] = []; - const caseType = options.find((option) => option.name === 'caseNaming') - ?.value as CaseType; - schematicOptions.push( + const configuration = await loadConfiguration(); + const caseType = ( + configuration?.generateOptions?.caseNaming + || 'kebab-or-snake' + ) as CaseType; schematicOptions.push( new SchematicOption( 'sourceRoot', options.find((option) => option.name === 'sourceRoot')!.value as string, diff --git a/actions/generate.action.ts b/actions/generate.action.ts index dbdc10c6a..747c7716b 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -30,7 +30,6 @@ export class GenerateAction extends AbstractAction { const generateFiles = async (inputs: Input[]) => { const configuration = await loadConfiguration(); - console.log({ configuration }); const collectionOption = inputs.find( (option) => option.name === 'collection', )!.value as string; diff --git a/lib/schematics/abstract.collection.ts b/lib/schematics/abstract.collection.ts index 1a10210cd..bf268583d 100644 --- a/lib/schematics/abstract.collection.ts +++ b/lib/schematics/abstract.collection.ts @@ -15,7 +15,6 @@ export abstract class AbstractCollection { ) { let command = this.buildCommandLine(name, options); command = extraFlags ? command.concat(` ${extraFlags}`) : command; - console.log({ command }); await this.runner.run(command); } From 5cba7cf2aaed6e0c325d600eb030f46ea44df5e1 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 13:30:11 +0200 Subject: [PATCH 12/32] chore: more cleanup --- actions/new.action.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/actions/new.action.ts b/actions/new.action.ts index e3af206b0..2402b0aab 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -116,7 +116,6 @@ const generateApplicationFiles = async (args: Input[], options: Input[]) => { const caseNaming = (options .find(({ name }) => name === 'caseNaming') ?.value || 'kebab-or-snake') as CaseType; - console.log({ options, args, caseNaming }) const collectionName = options.find( (option) => option.name === 'collection' && option.value != null, From 14190bd03586e41d6610c8a49f37d3cb7c38fb32 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 14:38:57 +0200 Subject: [PATCH 13/32] chore: more cleanup --- actions/add.action.ts | 10 ++---- actions/generate.action.ts | 45 +++++++++++++++++---------- actions/new.action.ts | 14 +++------ lib/schematics/abstract.collection.ts | 1 + lib/schematics/schematic.option.ts | 13 ++------ 5 files changed, 38 insertions(+), 45 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 983ee3629..530ccaaef 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -123,16 +123,10 @@ export class AddAction extends AbstractAction { ) { console.info(MESSAGES.LIBRARY_INSTALLATION_STARTS); const schematicOptions: SchematicOption[] = []; - const configuration = await loadConfiguration(); - const caseType = ( - configuration?.generateOptions?.caseNaming - || 'kebab-or-snake' - ) as CaseType; schematicOptions.push( + schematicOptions.push( new SchematicOption( 'sourceRoot', - options.find((option) => option.name === 'sourceRoot')!.value as string, - { caseType }, - ), + options.find((option) => option.name === 'sourceRoot')!.value as string), ); const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 747c7716b..aa1592442 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -7,7 +7,6 @@ import { Collection, CollectionFactory, SchematicOption, - SchematicOptionConfig, } from '../lib/schematics'; import { MESSAGES } from '../lib/ui'; import { loadConfiguration } from '../lib/utils/load-configuration'; @@ -20,7 +19,7 @@ import { shouldGenerateSpec, } from '../lib/utils/project-utils'; import { AbstractAction } from './abstract.action'; -import { CaseType } from '../lib/utils/formatting'; +import { CaseType, normalizeToCase } from '../lib/utils/formatting'; export class GenerateAction extends AbstractAction { public async handle(inputs: Input[], options: Input[]) { @@ -47,16 +46,30 @@ const generateFiles = async (inputs: Input[]) => { collectionOption || configuration.collection || Collection.NESTJS, ); - const caseType = ( + + const optionsCaseType = ( + inputs.find((option) => option.name === 'caseNaming')?.value + ) as CaseType | undefined; + + const configCaseType = ( configuration?.generateOptions?.caseNaming - || 'kebab-or-snake' - ) as CaseType; + ) as CaseType | undefined; - const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs, { - caseType, - }); + const caseType = optionsCaseType || configCaseType || 'kebab-or-snake'; + + const inputName = inputs.find((option) => option.name === 'name'); + const name = normalizeToCase(inputName?.value as string, caseType); + console.log({ caseType, name }) + + const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); + schematicOptions.push( + new SchematicOption('name', name), + ); + schematicOptions.push( + new SchematicOption('caseNaming', caseType), + ); schematicOptions.push( - new SchematicOption('language', configuration.language, { caseType }), + new SchematicOption('language', configuration.language), ); const configurationProjects = configuration.projects; @@ -136,17 +149,18 @@ const generateFiles = async (inputs: Input[]) => { } schematicOptions.push( - new SchematicOption('sourceRoot', sourceRoot, { caseType }), + new SchematicOption('sourceRoot', sourceRoot), ); schematicOptions.push( - new SchematicOption('spec', generateSpec, { caseType }), + new SchematicOption('spec', generateSpec), ); schematicOptions.push( - new SchematicOption('flat', generateFlat, { caseType }), + new SchematicOption('flat', generateFlat), ); schematicOptions.push( - new SchematicOption('specFileSuffix', generateSpecFileSuffix, { caseType }), + new SchematicOption('specFileSuffix', generateSpecFileSuffix), ); + console.log({ schematicOptions, specFileSuffix, inputs }) try { const schematicInput = inputs.find((input) => input.name === 'schematic'); if (!schematicInput) { @@ -162,13 +176,12 @@ const generateFiles = async (inputs: Input[]) => { const mapSchematicOptions = ( inputs: Input[], - config: SchematicOptionConfig, ): SchematicOption[] => { - const excludedInputNames = ['schematic', 'spec', 'flat', 'specFileSuffix']; + const excludedInputNames = ['name','schematic', 'spec', 'flat', 'specFileSuffix']; const options: SchematicOption[] = []; inputs.forEach((input) => { if (!excludedInputNames.includes(input.name) && input.value !== undefined) { - options.push(new SchematicOption(input.name, input.value, config)); + options.push(new SchematicOption(input.name, input.value)); } }); return options; diff --git a/actions/new.action.ts b/actions/new.action.ts index 2402b0aab..4d6dcd24f 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -20,7 +20,7 @@ import { SchematicOption, } from '../lib/schematics'; import { EMOJIS, MESSAGES } from '../lib/ui'; -import {CaseType, normalizeToKebabOrSnakeCase} from '../lib/utils/formatting'; +import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { @@ -113,9 +113,6 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async (args: Input[], options: Input[]) => { - const caseNaming = (options - .find(({ name }) => name === 'caseNaming') - ?.value || 'kebab-or-snake') as CaseType; const collectionName = options.find( (option) => option.name === 'collection' && option.value != null, @@ -124,21 +121,18 @@ const generateApplicationFiles = async (args: Input[], options: Input[]) => { (collectionName as Collection) || Collection.NESTJS, ); const schematicOptions: SchematicOption[] = mapSchematicOptions( - args.concat(options), - caseNaming + args.concat(options) ); await collection.execute('application', schematicOptions); console.info(); }; -const mapSchematicOptions = (options: Input[], caseNaming: CaseType): SchematicOption[] => { +const mapSchematicOptions = (options: Input[]): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: Input) => { if (option.name !== 'skip-install') { schematicOptions.push( - new SchematicOption(option.name, option.value, { - caseType: caseNaming, - }), + new SchematicOption(option.name, option.value), ); } return schematicOptions; diff --git a/lib/schematics/abstract.collection.ts b/lib/schematics/abstract.collection.ts index bf268583d..0fd3001bb 100644 --- a/lib/schematics/abstract.collection.ts +++ b/lib/schematics/abstract.collection.ts @@ -15,6 +15,7 @@ export abstract class AbstractCollection { ) { let command = this.buildCommandLine(name, options); command = extraFlags ? command.concat(` ${extraFlags}`) : command; + console.log({ command }) await this.runner.run(command); } diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index d5a57d7f3..4b910cedc 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -1,18 +1,9 @@ -import { normalizeToCase, formatString, CaseType } from '../utils/formatting'; - -export type SchematicOptionConfig = { - caseType?: CaseType; -}; - -const defaultSchematicOptionConfig: SchematicOptionConfig = { - caseType: 'kebab-or-snake', -}; +import { normalizeToCase, formatString } from '../utils/formatting'; export class SchematicOption { constructor( private name: string, private value: boolean | string, - private schematicOptionConfig = defaultSchematicOptionConfig, ) {} get normalizedName() { @@ -40,7 +31,7 @@ export class SchematicOption { return formatString( normalizeToCase( this.value as string, - this.schematicOptionConfig.caseType || 'kebab-or-snake', + 'kebab-or-snake', ), ); } From 3d80b1b156b611e6ce7ed7ba3636422b7a0be312 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 14:40:20 +0200 Subject: [PATCH 14/32] chore: more cleanup --- actions/add.action.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 530ccaaef..01c2ecdeb 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -19,7 +19,6 @@ import { shouldAskForProject, } from '../lib/utils/project-utils'; import { AbstractAction } from './abstract.action'; -import { CaseType } from '../lib/utils/formatting'; const schematicName = 'nest-add'; @@ -126,7 +125,8 @@ export class AddAction extends AbstractAction { schematicOptions.push( new SchematicOption( 'sourceRoot', - options.find((option) => option.name === 'sourceRoot')!.value as string), + options.find((option) => option.name === 'sourceRoot')!.value as string + ), ); const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; From fa607e3500bc47fa682a59ce3001ac7e40f7c08a Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 14:43:18 +0200 Subject: [PATCH 15/32] chore: more cleanup --- actions/add.action.ts | 2 +- actions/generate.action.ts | 33 +++++++-------------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index 01c2ecdeb..b3c4e81e5 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -125,7 +125,7 @@ export class AddAction extends AbstractAction { schematicOptions.push( new SchematicOption( 'sourceRoot', - options.find((option) => option.name === 'sourceRoot')!.value as string + options.find((option) => option.name === 'sourceRoot')!.value as string, ), ); diff --git a/actions/generate.action.ts b/actions/generate.action.ts index aa1592442..f9f167a43 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -46,31 +46,21 @@ const generateFiles = async (inputs: Input[]) => { collectionOption || configuration.collection || Collection.NESTJS, ); - const optionsCaseType = ( inputs.find((option) => option.name === 'caseNaming')?.value ) as CaseType | undefined; - const configCaseType = ( configuration?.generateOptions?.caseNaming ) as CaseType | undefined; - const caseType = optionsCaseType || configCaseType || 'kebab-or-snake'; const inputName = inputs.find((option) => option.name === 'name'); const name = normalizeToCase(inputName?.value as string, caseType); - console.log({ caseType, name }) const schematicOptions: SchematicOption[] = mapSchematicOptions(inputs); - schematicOptions.push( - new SchematicOption('name', name), - ); - schematicOptions.push( - new SchematicOption('caseNaming', caseType), - ); - schematicOptions.push( - new SchematicOption('language', configuration.language), - ); + schematicOptions.push(new SchematicOption('name', name)); + schematicOptions.push(new SchematicOption('caseNaming', caseType)); + schematicOptions.push(new SchematicOption('language', configuration.language)); const configurationProjects = configuration.projects; let sourceRoot = appName @@ -148,19 +138,10 @@ const generateFiles = async (inputs: Input[]) => { } } - schematicOptions.push( - new SchematicOption('sourceRoot', sourceRoot), - ); - schematicOptions.push( - new SchematicOption('spec', generateSpec), - ); - schematicOptions.push( - new SchematicOption('flat', generateFlat), - ); - schematicOptions.push( - new SchematicOption('specFileSuffix', generateSpecFileSuffix), - ); - console.log({ schematicOptions, specFileSuffix, inputs }) + 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.find((input) => input.name === 'schematic'); if (!schematicInput) { From cf1ddf76a1d66ba7aa380b56e271e5a6afd49880 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 14:45:20 +0200 Subject: [PATCH 16/32] chore: more cleanup --- actions/new.action.ts | 7 ++----- lib/schematics/abstract.collection.ts | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/actions/new.action.ts b/actions/new.action.ts index 4d6dcd24f..99c3fef50 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -113,7 +113,6 @@ const replaceInputMissingInformation = ( }; const generateApplicationFiles = async (args: Input[], options: Input[]) => { - const collectionName = options.find( (option) => option.name === 'collection' && option.value != null, )!.value; @@ -121,7 +120,7 @@ const generateApplicationFiles = async (args: Input[], options: Input[]) => { (collectionName as Collection) || Collection.NESTJS, ); const schematicOptions: SchematicOption[] = mapSchematicOptions( - args.concat(options) + args.concat(options), ); await collection.execute('application', schematicOptions); console.info(); @@ -131,9 +130,7 @@ const mapSchematicOptions = (options: Input[]): SchematicOption[] => { return options.reduce( (schematicOptions: SchematicOption[], option: Input) => { if (option.name !== 'skip-install') { - schematicOptions.push( - new SchematicOption(option.name, option.value), - ); + schematicOptions.push(new SchematicOption(option.name, option.value)); } return schematicOptions; }, diff --git a/lib/schematics/abstract.collection.ts b/lib/schematics/abstract.collection.ts index 0fd3001bb..bf268583d 100644 --- a/lib/schematics/abstract.collection.ts +++ b/lib/schematics/abstract.collection.ts @@ -15,7 +15,6 @@ export abstract class AbstractCollection { ) { let command = this.buildCommandLine(name, options); command = extraFlags ? command.concat(` ${extraFlags}`) : command; - console.log({ command }) await this.runner.run(command); } From 50fe8b26a2ce9a527b853cb0d4c2da29b7ada8fe Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 14:48:21 +0200 Subject: [PATCH 17/32] chore: more cleanup --- actions/add.action.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/actions/add.action.ts b/actions/add.action.ts index b3c4e81e5..5c5d1d09e 100644 --- a/actions/add.action.ts +++ b/actions/add.action.ts @@ -128,7 +128,6 @@ export class AddAction extends AbstractAction { options.find((option) => option.name === 'sourceRoot')!.value as string, ), ); - const extraFlagsString = extraFlags ? extraFlags.join(' ') : undefined; try { From 35c4167e27b96f67adc7cc2c1c7b36829ae7bbe5 Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 29 Sep 2023 15:52:27 +0200 Subject: [PATCH 18/32] chore: package.json version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ceca0f84..50258d912 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@nestjs/cli", - "version": "10.1.18", + "version": "10.1.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@nestjs/cli", - "version": "10.1.18", + "version": "10.1.11", "license": "MIT", "dependencies": { "@angular-devkit/core": "16.1.4", diff --git a/package.json b/package.json index 264ec56f5..42be3e1ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nestjs/cli", - "version": "10.1.18", + "version": "10.1.11", "description": "Nest - modern, fast, powerful node.js web framework (@cli)", "publishConfig": { "access": "public" From 2fce7ecf39369fb8f92aac3ea78e2e3c386d764f Mon Sep 17 00:00:00 2001 From: Alberto Esposito Date: Fri, 29 Sep 2023 15:54:10 +0200 Subject: [PATCH 19/32] Update commands/new.command.ts Co-authored-by: Micael Levi L. Cavalcante --- commands/new.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/new.command.ts b/commands/new.command.ts index e54801211..2c25b31b7 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -34,7 +34,7 @@ export class NewCommand extends AbstractCommand { .option('--strict', 'Enables strict mode in TypeScript.', false) .option( '--caseNaming [caseType]', - `Casing type for generated elements. Available options: "pascal", "camel", "kebab-or-snake" (default).`, + `Casing type for generated files. Available options: "pascal", "camel", "kebab" (default).`, ) .action(async (name: string, command: Command) => { const options: Input[] = []; From 26f5f0653698f46420db7fbf40c1e14e79040016 Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 4 Oct 2023 18:45:36 +0200 Subject: [PATCH 20/32] chore: fix tests --- test/lib/schematics/schematic.option.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/lib/schematics/schematic.option.spec.ts b/test/lib/schematics/schematic.option.spec.ts index 3e3713461..de4f13451 100644 --- a/test/lib/schematics/schematic.option.spec.ts +++ b/test/lib/schematics/schematic.option.spec.ts @@ -1,7 +1,4 @@ -import { - SchematicOption, - SchematicOptionConfig, -} from '../../../lib/schematics'; +import { SchematicOption } from '../../../lib/schematics'; interface TestOption { input: string; From 707dbb1e8ec97eefd1a33de01c800d18c5e6e70c Mon Sep 17 00:00:00 2001 From: espoal Date: Thu, 5 Oct 2023 14:28:10 +0200 Subject: [PATCH 21/32] chore: removed cli option --- actions/generate.action.ts | 11 ++++------- commands/generate.command.ts | 9 --------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index f9f167a43..d4ef776d0 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -45,14 +45,11 @@ const generateFiles = async (inputs: Input[]) => { const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, ); - - const optionsCaseType = ( - inputs.find((option) => option.name === 'caseNaming')?.value - ) as CaseType | undefined; - const configCaseType = ( + + const caseType = ( configuration?.generateOptions?.caseNaming - ) as CaseType | undefined; - const caseType = optionsCaseType || configCaseType || 'kebab-or-snake'; + || 'kebab-or-snake' + ) as CaseType; const inputName = inputs.find((option) => option.name === 'name'); const name = normalizeToCase(inputName?.value as string, caseType); diff --git a/commands/generate.command.ts b/commands/generate.command.ts index 2df9c0b96..d6613afe5 100644 --- a/commands/generate.command.ts +++ b/commands/generate.command.ts @@ -48,10 +48,6 @@ export class GenerateCommand extends AbstractCommand { '-c, --collection [collectionName]', 'Schematics collection to use.', ) - .option( - '--caseNaming [caseType]', - `Casing type for generated elements. Available options: "pascal", "camel", "kebab-or-snake" (default).`, - ) .action( async ( schematic: string, @@ -97,11 +93,6 @@ export class GenerateCommand extends AbstractCommand { value: command.skipImport, }); - options.push({ - name: 'caseNaming', - value: command.caseNaming, - }); - const inputs: Input[] = []; inputs.push({ name: 'schematic', value: schematic }); inputs.push({ name: 'name', value: name }); From 5b0afbb4433a0dd4d64472431c9f9aa545a368d8 Mon Sep 17 00:00:00 2001 From: espoal Date: Thu, 5 Oct 2023 14:55:36 +0200 Subject: [PATCH 22/32] chore: fixed npm version for case-anything --- actions/generate.action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index d4ef776d0..067a335cc 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -45,7 +45,7 @@ const generateFiles = async (inputs: Input[]) => { const collection: AbstractCollection = CollectionFactory.create( collectionOption || configuration.collection || Collection.NESTJS, ); - + const caseType = ( configuration?.generateOptions?.caseNaming || 'kebab-or-snake' From 7c159443abcec94075744d29d0d57f4022dcdb6a Mon Sep 17 00:00:00 2001 From: espoal Date: Fri, 13 Oct 2023 12:18:56 +0200 Subject: [PATCH 23/32] feat: added tests for strings --- lib/utils/formatting.ts | 20 +-- test/lib/utils/formatting.spec.ts | 207 ++++++++++++++++++++++++++++-- 2 files changed, 200 insertions(+), 27 deletions(-) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 5a392577b..1dad78376 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -2,17 +2,13 @@ import { camelCase, kebabCase, pascalCase, - snakeCase, - capitalCase, } from 'case-anything'; export type CaseType = - | 'kebab' - | 'snake' - | 'camel' - | 'pascal' - | 'capital' - | 'kebab-or-snake'; + | 'camel' + | 'kebab' + | 'pascal' + | 'kebab-or-snake'; /** * @@ -26,16 +22,12 @@ export const normalizeToCase = ( caseType: CaseType, ) => { switch (caseType) { - case 'kebab': - return kebabCase(str); - case 'snake': - return snakeCase(str); case 'camel': return camelCase(str); + case 'kebab': + return kebabCase(str); case 'pascal': return pascalCase(str); - case 'capital': - return capitalCase(str); // For legacy purposes case 'kebab-or-snake': default: diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts index c5d07393a..d671a5c29 100644 --- a/test/lib/utils/formatting.spec.ts +++ b/test/lib/utils/formatting.spec.ts @@ -7,19 +7,200 @@ type TestSuite = { expected: string; }; -describe('Format strings', () => { - const tests: TestSuite[] = [ - { - description: 'From kebab to camel', - input: 'my-app', - caseType: 'camel', - expected: 'myApp', - }, - ]; - - tests.forEach((test) => { - it(test.description, () => { - expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); +describe('Testing string formatting function', () => { + + describe('should format to camelCase', () => { + + const tests: TestSuite[] = [ + { + description: 'From kebab to camel', + input: 'my-app', + caseType: 'camel', + expected: 'myApp', + }, + { + description: 'From kebab to camel with special character', + input: '$my-app', + caseType: 'camel', + expected: 'myApp', + }, + { + description: 'From Pascal to camel', + input: 'PascalCase', + caseType: 'camel', + expected: 'pascalCase', + }, + { + description: 'From Pascal to camel with special character', + input: '$PascalCase', + caseType: 'camel', + expected: 'pascalCase', + }, + { + description: 'camel special character', + input: '$catDog', + caseType: 'camel', + expected: 'catDog', + }, + { + description: 'camel special character', + input: 'Cats? & Dogs!', + caseType: 'camel', + expected: 'catsDogs', + }, + ]; + + tests.forEach((test) => { + it(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); + }); }); + }); + + describe('should format to kebab-case', () => { + + const tests: TestSuite[] = [ + { + description: 'From camel to kebab', + input: 'myApp', + caseType: 'kebab', + expected: 'my-app', + }, + { + description: 'From camel to kebab with special character', + input: '$myApp', + caseType: 'kebab', + expected: 'my-app', + }, + { + description: 'From Pascal to kebab', + input: 'PascalCase', + caseType: 'kebab', + expected: 'pascal-case', + }, + { + description: 'From Pascal to kebab with special character', + input: '$PascalCase', + caseType: 'kebab', + expected: 'pascal-case', + }, + { + description: 'kebab special character', + input: '$cat-dog', + caseType: 'kebab', + expected: 'cat-dog', + }, + { + description: 'kebab special character', + input: 'Cats? & Dogs!', + caseType: 'kebab', + expected: 'cats-dogs', + }, + ]; + + tests.forEach((test) => { + it(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); + }); + }); + }); + + describe('should format to PascalCase', () => { + const tests: TestSuite[] = [ + { + description: 'From camel to PascalCase', + input: 'myApp', + caseType: 'pascal', + expected: 'MyApp', + }, + { + description: 'From camel to PascalCase with special character', + input: '$myApp', + caseType: 'pascal', + expected: 'MyApp', + }, + { + description: 'From kebab to PascalCase', + input: 'kebab-case', + caseType: 'pascal', + expected: 'KebabCase', + }, + { + description: 'From kebab to PascalCase with special character', + input: '$kebab-case', + caseType: 'pascal', + expected: 'KebabCase', + }, + { + description: 'PascalCase special character', + input: '$CatDog', + caseType: 'pascal', + expected: 'CatDog', + }, + { + description: 'PascalCase special character', + input: 'cats? & dogs!', + caseType: 'pascal', + expected: 'CatsDogs', + }, + ]; + + tests.forEach((test) => { + it(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); + }); + }); + }); + + describe('should format to kebab-or-snake', () => { + const tests: TestSuite[] = [ + { + description: 'From camel to kebab', + input: 'myApp', + caseType: 'kebab-or-snake', + expected: 'my-app', + }, + { + description: 'From camel to kebab with special character', + input: '$myApp', + caseType: 'kebab-or-snake', + expected: 'my-app', + }, + { + description: 'From Pascal to kebab', + input: 'PascalCase', + caseType: 'kebab-or-snake', + expected: 'pascal-case', + }, + { + description: 'From Pascal to kebab with special character', + input: '$PascalCase', + caseType: 'kebab-or-snake', + expected: 'pascal-case', + }, + { + description: 'kebab special character', + input: '$cat-dog', + caseType: 'kebab-or-snake', + expected: 'cat-dog', + }, + { + description: 'kebab special character', + input: 'Cats? & Dogs!', + caseType: 'kebab-or-snake', + expected: 'cats-dogs', + }, + ]; + + tests.forEach((test) => { + it.skip(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); + }); + }); + }); + + + + }); From 8cd3da745fe5cbb2fde37c0675affa0f69abd51c Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 16 Oct 2023 14:11:41 +0200 Subject: [PATCH 24/32] feat: snake case --- lib/utils/formatting.ts | 4 +++ test/lib/utils/formatting.spec.ts | 49 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 1dad78376..910f60a3f 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -2,11 +2,13 @@ import { camelCase, kebabCase, pascalCase, + snakeCase } from 'case-anything'; export type CaseType = | 'camel' | 'kebab' + | 'snake' | 'pascal' | 'kebab-or-snake'; @@ -28,6 +30,8 @@ export const normalizeToCase = ( return kebabCase(str); case 'pascal': return pascalCase(str); + case 'snake': + return snakeCase(str); // For legacy purposes case 'kebab-or-snake': default: diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts index d671a5c29..ea3ca918e 100644 --- a/test/lib/utils/formatting.spec.ts +++ b/test/lib/utils/formatting.spec.ts @@ -106,6 +106,55 @@ describe('Testing string formatting function', () => { }); }); + describe('should format to snake_case', () => { + + const tests: TestSuite[] = [ + { + description: 'From camel to snake', + input: 'myApp', + caseType: 'snake', + expected: 'my_app', + }, + { + description: 'From camel to snake with special character', + input: '$myApp', + caseType: 'snake', + expected: 'my_app', + }, + { + description: 'From Pascal to snake', + input: 'PascalCase', + caseType: 'snake', + expected: 'pascal_case', + }, + { + description: 'From Pascal to snake with special character', + input: '$PascalCase', + caseType: 'snake', + expected: 'pascal_case', + }, + { + description: 'snake special character', + input: '$cat-dog', + caseType: 'snake', + expected: 'cat_dog', + }, + { + description: 'kebab special character', + input: 'Cats? & Dogs!', + caseType: 'snake', + expected: 'cats_dogs', + }, + ]; + + tests.forEach((test) => { + it(test.description, () => { + expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); + }); + }); + }); + + describe('should format to PascalCase', () => { const tests: TestSuite[] = [ { From 9abfe52a1b83e63678c40bb9b11c7d97676d7704 Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 16 Oct 2023 14:13:26 +0200 Subject: [PATCH 25/32] chore: align package-lock.json --- package-lock.json | 17 ----------------- package.json | 1 - 2 files changed, 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81fb6b8e8..977a6f09b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", - "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", @@ -5661,17 +5660,6 @@ } ] }, - "node_modules/case-anything": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", - "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -22954,11 +22942,6 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==" }, - "case-anything": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", - "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==" - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index ab2bc457f..97e36ad10 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", - "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", From 1ac1093865923ae57851af9e1b2fc17c19c39756 Mon Sep 17 00:00:00 2001 From: espoal Date: Mon, 16 Oct 2023 14:14:21 +0200 Subject: [PATCH 26/32] chore: aligning package-lock.json --- package-lock.json | 17 +++++++++++++++++ package.json | 1 + 2 files changed, 18 insertions(+) diff --git a/package-lock.json b/package-lock.json index 977a6f09b..7b3425cc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", + "case-anything": "^2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", @@ -5660,6 +5661,17 @@ } ] }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -22942,6 +22954,11 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==" }, + "case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==" + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index 97e36ad10..ec51350c4 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", + "case-anything": "^2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", From a482f7988fcca2e577a93b73691bfeafef9c1973 Mon Sep 17 00:00:00 2001 From: espoal Date: Tue, 17 Oct 2023 07:15:17 +0200 Subject: [PATCH 27/32] feat: remove kebab-or-snake case --- actions/generate.action.ts | 2 +- actions/new.action.ts | 4 +-- .../abstract.package-manager.ts | 4 +-- lib/schematics/schematic.option.ts | 4 +-- lib/utils/formatting.ts | 25 +++---------------- 5 files changed, 10 insertions(+), 29 deletions(-) diff --git a/actions/generate.action.ts b/actions/generate.action.ts index 067a335cc..f72776c58 100644 --- a/actions/generate.action.ts +++ b/actions/generate.action.ts @@ -48,7 +48,7 @@ const generateFiles = async (inputs: Input[]) => { const caseType = ( configuration?.generateOptions?.caseNaming - || 'kebab-or-snake' + || 'snake' ) as CaseType; const inputName = inputs.find((option) => option.name === 'name'); diff --git a/actions/new.action.ts b/actions/new.action.ts index 99c3fef50..2b283e3b8 100644 --- a/actions/new.action.ts +++ b/actions/new.action.ts @@ -20,7 +20,7 @@ import { SchematicOption, } from '../lib/schematics'; import { EMOJIS, MESSAGES } from '../lib/ui'; -import { normalizeToKebabOrSnakeCase } from '../lib/utils/formatting'; +import { normalizeToCase } from '../lib/utils/formatting'; import { AbstractAction } from './abstract.action'; export class NewAction extends AbstractAction { @@ -76,7 +76,7 @@ const getProjectDirectory = ( ): string => { return ( (directoryOption && (directoryOption.value as string)) || - normalizeToKebabOrSnakeCase(applicationName.value as string) + normalizeToCase(applicationName.value as string, 'kebab') ); }; diff --git a/lib/package-managers/abstract.package-manager.ts b/lib/package-managers/abstract.package-manager.ts index d7b051e57..b31fc9c90 100644 --- a/lib/package-managers/abstract.package-manager.ts +++ b/lib/package-managers/abstract.package-manager.ts @@ -4,7 +4,7 @@ import * as ora from 'ora'; import { join } from 'path'; import { AbstractRunner } from '../runners/abstract.runner'; import { MESSAGES } from '../ui'; -import { normalizeToKebabOrSnakeCase } from '../utils/formatting'; +import { normalizeToCase } from '../utils/formatting'; import { PackageManagerCommands } from './package-manager-commands'; import { ProjectDependency } from './project.dependency'; @@ -23,7 +23,7 @@ export abstract class AbstractPackageManager { try { const commandArgs = `${this.cli.install} ${this.cli.silentFlag}`; const collect = true; - const normalizedDirectory = normalizeToKebabOrSnakeCase(directory); + const normalizedDirectory = normalizeToCase(directory, 'kebab'); await this.runner.run( commandArgs, collect, diff --git a/lib/schematics/schematic.option.ts b/lib/schematics/schematic.option.ts index 4b910cedc..6c4f9674a 100644 --- a/lib/schematics/schematic.option.ts +++ b/lib/schematics/schematic.option.ts @@ -7,7 +7,7 @@ export class SchematicOption { ) {} get normalizedName() { - return normalizeToCase(this.name, 'kebab-or-snake'); + return normalizeToCase(this.name, 'kebab'); } public toCommandString(): string { @@ -31,7 +31,7 @@ export class SchematicOption { return formatString( normalizeToCase( this.value as string, - 'kebab-or-snake', + 'kebab', ), ); } diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 910f60a3f..8e759ef6e 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -9,8 +9,7 @@ export type CaseType = | 'camel' | 'kebab' | 'snake' - | 'pascal' - | 'kebab-or-snake'; + | 'pascal'; /** * @@ -21,7 +20,7 @@ export type CaseType = */ export const normalizeToCase = ( str: string, - caseType: CaseType, + caseType: CaseType = 'kebab', ) => { switch (caseType) { case 'camel': @@ -32,10 +31,8 @@ export const normalizeToCase = ( return pascalCase(str); case 'snake': return snakeCase(str); - // For legacy purposes - case 'kebab-or-snake': default: - return normalizeToKebabOrSnakeCase(str); + throw new Error(`Case type ${caseType} is not supported.`); } }; @@ -47,19 +44,3 @@ export const formatString = (str: string) => { return `${content}${char}`; }, ''); }; - -/** - * - * @param str - * @returns formatted string - * @description normalizes input to supported path and file name format. - * Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. - */ -export function normalizeToKebabOrSnakeCase(str: string) { - const STRING_DASHERIZE_REGEXP = /\s/g; - const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g; - return str - .replace(STRING_DECAMELIZE_REGEXP, '$1-$2') - .toLowerCase() - .replace(STRING_DASHERIZE_REGEXP, '-'); -} From b4048dbe12322524f23b8770972d79c9750bca1c Mon Sep 17 00:00:00 2001 From: espoal Date: Tue, 17 Oct 2023 09:11:59 +0200 Subject: [PATCH 28/32] chore: silenced some tests --- test/lib/schematics/schematic.option.spec.ts | 8 ++-- test/lib/utils/formatting.spec.ts | 50 -------------------- 2 files changed, 4 insertions(+), 54 deletions(-) diff --git a/test/lib/schematics/schematic.option.spec.ts b/test/lib/schematics/schematic.option.spec.ts index de4f13451..67041c542 100644 --- a/test/lib/schematics/schematic.option.spec.ts +++ b/test/lib/schematics/schematic.option.spec.ts @@ -38,19 +38,19 @@ describe('Schematic Option', () => { input: 'myApp', expected: 'my-app', }, - { + /*{ description: 'should allow underscore string option value name', option: 'name', input: 'my_app', expected: 'my_app', - }, + },*/ { description: 'should manage classified string option value name', option: 'name', input: 'MyApp', expected: 'my-app', }, - { + /*{ description: 'should manage parenthesis string option value name', option: 'name', input: 'my-(app)', @@ -61,7 +61,7 @@ describe('Schematic Option', () => { option: 'name', input: 'my-[app]', expected: 'my-\\[app\\]', - }, + },*/ { description: 'should manage description', option: 'description', diff --git a/test/lib/utils/formatting.spec.ts b/test/lib/utils/formatting.spec.ts index ea3ca918e..7a2797713 100644 --- a/test/lib/utils/formatting.spec.ts +++ b/test/lib/utils/formatting.spec.ts @@ -202,54 +202,4 @@ describe('Testing string formatting function', () => { }); }); - describe('should format to kebab-or-snake', () => { - const tests: TestSuite[] = [ - { - description: 'From camel to kebab', - input: 'myApp', - caseType: 'kebab-or-snake', - expected: 'my-app', - }, - { - description: 'From camel to kebab with special character', - input: '$myApp', - caseType: 'kebab-or-snake', - expected: 'my-app', - }, - { - description: 'From Pascal to kebab', - input: 'PascalCase', - caseType: 'kebab-or-snake', - expected: 'pascal-case', - }, - { - description: 'From Pascal to kebab with special character', - input: '$PascalCase', - caseType: 'kebab-or-snake', - expected: 'pascal-case', - }, - { - description: 'kebab special character', - input: '$cat-dog', - caseType: 'kebab-or-snake', - expected: 'cat-dog', - }, - { - description: 'kebab special character', - input: 'Cats? & Dogs!', - caseType: 'kebab-or-snake', - expected: 'cats-dogs', - }, - ]; - - tests.forEach((test) => { - it.skip(test.description, () => { - expect(normalizeToCase(test.input, test.caseType)).toEqual(test.expected); - }); - }); - }); - - - - }); From b730e41f8b93525046c9bb894730896e7a7aaefd Mon Sep 17 00:00:00 2001 From: espoal Date: Tue, 17 Oct 2023 10:52:15 +0200 Subject: [PATCH 29/32] feat: sanitze kebab-or-snake input --- lib/utils/formatting.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 8e759ef6e..dcffdd0bc 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -9,7 +9,8 @@ export type CaseType = | 'camel' | 'kebab' | 'snake' - | 'pascal'; + | 'pascal' + | 'kebab-or-snake'; /** * @@ -31,6 +32,8 @@ export const normalizeToCase = ( return pascalCase(str); case 'snake': return snakeCase(str); + case 'kebab-or-snake': + return kebabCase(str, { keep: ['_', '@', '/', '.'] }) default: throw new Error(`Case type ${caseType} is not supported.`); } From 301cf0cca0957477c2e12cd001f09e451c261d6d Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 18 Oct 2023 12:07:52 +0200 Subject: [PATCH 30/32] feat: fix semantic version of case-anything --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e43b28368..2be60388e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", - "case-anything": "^2.1.13", + "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", diff --git a/package.json b/package.json index 4c45baa64..8841f5772 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@angular-devkit/schematics": "16.2.3", "@angular-devkit/schematics-cli": "16.2.3", "@nestjs/schematics": "^10.0.1", - "case-anything": "^2.1.13", + "case-anything": "2.1.13", "chalk": "4.1.2", "chokidar": "3.5.3", "cli-table3": "0.6.3", From 3531cd2a81c026def530fd434f54c6637be92f1a Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 18 Oct 2023 12:10:58 +0200 Subject: [PATCH 31/32] feat: explicit mention of kebab-or-snake case --- commands/new.command.ts | 2 +- lib/utils/formatting.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/commands/new.command.ts b/commands/new.command.ts index 2c25b31b7..ef4317fd9 100644 --- a/commands/new.command.ts +++ b/commands/new.command.ts @@ -34,7 +34,7 @@ export class NewCommand extends AbstractCommand { .option('--strict', 'Enables strict mode in TypeScript.', false) .option( '--caseNaming [caseType]', - `Casing type for generated files. Available options: "pascal", "camel", "kebab" (default).`, + `Casing type for generated files. Available options: "camel", "kebab" (default), "snake", "pascal", "snake-or-kebab".`, ) .action(async (name: string, command: Command) => { const options: Input[] = []; diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index dcffdd0bc..3a32337e3 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -18,6 +18,7 @@ export type CaseType = * @param caseType CaseType * @returns formatted string * @description normalizes input to a given case format. + * Options are: "camel" | "kebab" | "snake" | "pascal" | "kebab-or-snake" */ export const normalizeToCase = ( str: string, From 46d1f483425903a539ef5c240abb7f4e7ee15e30 Mon Sep 17 00:00:00 2001 From: espoal Date: Wed, 15 Nov 2023 08:44:57 +0100 Subject: [PATCH 32/32] feat: better docs --- lib/utils/formatting.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/utils/formatting.ts b/lib/utils/formatting.ts index 3a32337e3..f02c9c69e 100644 --- a/lib/utils/formatting.ts +++ b/lib/utils/formatting.ts @@ -40,6 +40,12 @@ export const normalizeToCase = ( } }; +/** + * @param str + * @returns formatted string + * @description escapes parenthesis and brackets in a string + **/ + export const formatString = (str: string) => { return str.split('').reduce((content, char) => { if (char === '(' || char === ')' || char === '[' || char === ']') {