Skip to content

Release v2.3.0 #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 71 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e36ffdd
test: add test for 'isConsiderableCharSequence' method
benjGam Sep 24, 2024
2468531
feat: implement 'isConsiderableCharSequence' logic
benjGam Sep 24, 2024
b7b01a1
Merge branch 'feature/is-word-method-implementation' into develop
benjGam Sep 24, 2024
f3174d1
refactor: use 'isConsiderableCharSequence' method invokation instead …
benjGam Sep 24, 2024
f58ed54
chore(files): rename 'words-ending-utils.ts' -> 'word.ts'
benjGam Sep 24, 2024
2273aa5
test: add tests for 'removeBlankChars' method
benjGam Sep 24, 2024
8fba658
feat: implement 'removeBlankChars' logic
benjGam Sep 24, 2024
f4f4371
test: add tests for 'toCamelCase' method
benjGam Sep 24, 2024
d4d0030
test: add tests for 'blendIrrelevantStringsInRelevantOnes' method
benjGam Sep 24, 2024
4cae8e2
fix: add 'this' to input to pass test
benjGam Sep 24, 2024
25a656a
feat: add 'blendIrrelevantStringsInRelevantOnes' logic
benjGam Sep 24, 2024
06f4676
test: add tests for 'containsConsiderableCharSequence' method
benjGam Sep 24, 2024
5675126
test: add test
benjGam Sep 24, 2024
99a6c30
feat: implement 'containsConsiderableCharSequence' logic
benjGam Sep 24, 2024
173747c
refactor: use 'containsConsiderableCharSequence' in 'blendIrrelevant.…
benjGam Sep 24, 2024
5f1e81c
feat: implement 'toCamelCase' logic
benjGam Sep 24, 2024
faff18f
refactor: rework condition in 'toCamelCase' method
benjGam Sep 24, 2024
a976a97
test: add test for 'toPascalCase' methdo
benjGam Sep 24, 2024
25c2bda
fix: change output to pass test
benjGam Sep 24, 2024
04945e6
feat: implement 'toPascalCase' logic
benjGam Sep 24, 2024
47795ed
test: add test for 'toSnakeCase' method
benjGam Sep 24, 2024
531bd7e
feat: implement 'toSnakeCase' logic
benjGam Sep 24, 2024
e02940b
fix: add case determination check
benjGam Sep 24, 2024
d1225e2
fix: fix testing for snake case methdo
benjGam Sep 24, 2024
9c36b84
fix: rework 'toSnakeCase' behavior to pass tests
benjGam Sep 24, 2024
a8c59fa
test(coverage): add new tests to get 100% branch coverage
benjGam Sep 24, 2024
4e4712c
fix: add 'toLowerCase 'for snakeCase to pass new tests
benjGam Sep 24, 2024
5cafb8a
refactor: remove 'toLowerCase'
benjGam Sep 24, 2024
e1545eb
refactor: remove useless condition check
benjGam Sep 24, 2024
3fb5acc
Merge branch 'feature/implement-dedicated-method-for-case-convert' in…
benjGam Sep 24, 2024
2495e07
refactor: use a wrapped method to avoid code rewriting
benjGam Sep 24, 2024
79a99c0
feat: implement 'Case' abstract class
benjGam Sep 24, 2024
8e44b86
refactor: do not use '_name' field anymore in 'Case' abstract class
benjGam Sep 24, 2024
e7e5bf1
fix: add parameters to 'Case' method signatures
benjGam Sep 24, 2024
4cb0fe9
feat: implement 'CamelCase' class
benjGam Sep 24, 2024
a8648fa
feat: redefine scope of attributes
benjGam Sep 24, 2024
7c0c296
fix: implement '_matcher' & '_splitter' for CamelCase class
benjGam Sep 24, 2024
1c2feae
feat: implement 'PascalCase' class
benjGam Sep 24, 2024
94908d6
feat: implement 'SnakeCase' class
benjGam Sep 24, 2024
615a3d3
feat: implement 'LowerCase' class
benjGam Sep 24, 2024
4f6c813
feat: implement 'PascalCase' class
benjGam Sep 24, 2024
354de87
refactor: refactor 'StringUtilsCase' class to pass tests with new cas…
benjGam Sep 24, 2024
a250d54
docs: TSDOC add docs comments for 'Case' abstract methods
benjGam Sep 24, 2024
03393ce
refactor: do not use destucturation to get prototype of methods befor…
benjGam Sep 24, 2024
acbe728
docs: add comments to 'convertToCaseLogic'
benjGam Sep 24, 2024
71e4c03
fix typo in var name
benjGam Sep 24, 2024
99a88c9
docs: add comments for camelCase class
benjGam Sep 24, 2024
80d3667
Merge branch 'feature/improve-case-convertion-reliability' into develop
benjGam Sep 24, 2024
8cf4456
test: add test for 'containsOnlyConsiderableCharSequence' method
benjGam Sep 24, 2024
be0be11
feat: implement 'containsOnlyConsiderableCharSequence' logic
benjGam Sep 24, 2024
444f240
refactor: use 'containsOnlyConsiderableCharSequences' to avoid proces…
benjGam Sep 24, 2024
08b7124
feat: implement 'JestUtils' class
benjGam Sep 24, 2024
e4b1616
fix: use function to test invokation
benjGam Sep 24, 2024
15a892b
fix: rework class to be usable in that context
benjGam Sep 24, 2024
3af17e2
fix: add way to split params when needed SHOULD BE REWORK LATER
benjGam Sep 24, 2024
1501065
feat: add expected output in test message
benjGam Sep 25, 2024
58ba8a8
refactor: do not rewrite same code
benjGam Sep 25, 2024
82e8dee
try a fix for now
benjGam Sep 25, 2024
563b364
fix: use function to determine when it's a multi args or other stuffs
benjGam Sep 25, 2024
8c1ed2b
chore: move param & args position for runner.runBasicTest
benjGam Sep 25, 2024
2b4e23d
feat: add way to test a specific field in a object
benjGam Sep 25, 2024
f00c729
refactor: use runner for 'case' test
benjGam Sep 25, 2024
123d1c9
refactor: use runner for 'words'
benjGam Sep 25, 2024
4eb57f8
refactor: kinda refactor test utils
benjGam Sep 25, 2024
02dab10
docs(CHANGELOG): add 'Added' case logs
benjGam Sep 25, 2024
dd91e6e
docs(CHANGELOG): add 'StringUtils' class added changes
benjGam Sep 25, 2024
6d2a332
docs: add 'Enhancement', 'Removed', 'Refactor' sections
benjGam Sep 25, 2024
0e7a515
chore(version): update package to v2.3.0
benjGam Sep 25, 2024
b686933
Merge branch 'release/v2.3.0'
benjGam Sep 25, 2024
9f17909
Merge tag 'v2.3.0' into develop
benjGam Sep 25, 2024
7195a03
fix(CD): try to fix workflow triggering
benjGam Sep 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
name: Node.js CD

on:
push:
branches: ['main']
pull_request:
branches:
- main
types: [closed]

jobs:
if: ${{ github.event.pull_request.merged }}
publish-npm:
runs-on: ubuntu-latest
steps:
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# Version 2.3.0

- `Enhancement`:
- `Case convertion`: Previously algorithms responsible of converting a string to another case was obviously to light, so, the range of managed uses was too poor, i reworked those algorithms and they're now better from far that was they were. The new ones got tested and passes tests, it's more than sure that i didn't test all of cases, but an enhancement of this feature is truely brang to package.
- `Added`:
- `Case` abstract class is now used to reliably create case objects.
- `CamelCase` class implement logic of previous correspondant object.
- `PascalCase` class implement logic of previous correspondant object.
- `SnakeCase` class implement logic of previous correspondant object.
- `LowerCase` class implement logic of previous correspondant object.
- `UpperCase` class implement logic of previous correspondant object.
- `StringUtils (main.ts)`
- `isConsiderableCharSequence(str: string): boolean` method has been implemented and used to check if a given string contains atleast 2 chars (excepted blanks ones).
- `containsConsiderableCharSequence(stringTable: string[]): boolean` method has been implemented and used to check if a given table of string contains atleast one considerable (determined by `isConsiderableCharSequence` criterias) element.
- `containsOnlyConsiderableCharSequences(stringTable: string[]): boolean` method has been implemented and used to check if a given table of string contains only considerable (determined by `isConsiderableCharSequence` criterias) elements.
- `removeBlankChars(str): string` method has been implemented and could be use to remove blank chars from a given string.
- `blendIrrelevantStringsInRelevantOnes(str: string): string[]` method has been implemented and should be used to blend orphan chars (a char adjacent to blank chars) to the last considerable subsequence of char (determined by `isConsiderableCharSequence` criterias) in a given string.
- `Removed`:
- `[BREAKING CHANGES]`:
- `Case` type has been renamed `CaseName` to avoid collision between new `Case` object type and previous `Case` type.
- `ICase` interface has been removed, it was useless to keep working with it.
- `Refactor`:
- `[BREAKING CHANGES]`:
- `determineCase(str: string): ICase` method signature changed to `determineCase(str: string): Case`.
- `convertToCase(str: string, caseToConvert: Case): string` method signature moved to `convertToCase(str: string, caseToConvert: CaseName): string`.
- `knownCases` table is now a `:Case[]` instead of `:ICase[]`.
- Tests has been refactored to avoid rewriting loops again and again, they now use `JestRunner` utils class.

# Version 2.2.0

- `Added`:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "string-utils-ts",
"version": "2.2.0",
"version": "2.3.0",
"description": "Provide some useful functions for strings",
"main": "./lib",
"scripts": {
Expand Down
185 changes: 111 additions & 74 deletions src/case.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,32 @@
import StringUtilsWord from './word-ending-utils';

/**
* This interface provide a structure for literal objects
* It brings reliable way to add unmanaged cases without any other code writing
*
* @interface ICase
* @field {string} name is used to represent a matcher & splitter for case
* @field {RegExp} matcher is used with Regex operations to check if a given string match
* @field {RegExp | string} splitter is used to split given string who match `matcher`
*
*/
interface ICase {
name: Case;
matcher: RegExp;
splitter: RegExp | string;
}
import CamelCase from './case/camel-case';
import Case from './case/Case';
import LowerCase from './case/lower-case';
import PascalCase from './case/pascal-case';
import SnakeCase from './case/snake-case';
import UpperCase from './case/upper-case';
import { StringUtils } from './main';

/**
* This type is used to ensure case selection is reliable
*/

export type Case =
| 'snakeCase'
| 'pascalCase'
| 'lowerCase'
| 'upperCase'
| 'camelCase';
export type CaseName =
| 'SnakeCase'
| 'PascalCase'
| 'LowerCase'
| 'UpperCase'
| 'CamelCase';

/**
* This table store few basics cases.
*
* @method StringUtilsCase.determineCase <- Use it
*/
const knownCases: ICase[] = [
{
name: 'snakeCase',
matcher: /(\w+)_(\w+)/,
splitter: '_',
},
{
name: 'pascalCase',
matcher: /^[A-Z][a-z]+(?:[A-Z][a-z]+)*$/,
splitter: /([A-Z]+[a-z]*)/,
},
{
name: 'lowerCase',
matcher: /^[a-z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\s]*$/,
splitter: '',
},
{
name: 'upperCase',
matcher: /^[A-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\s]*$/,
splitter: '',
},
{
name: 'camelCase',
matcher: /^[a-z]+(?:[A-Z][a-z]+)*$/,
splitter: /([A-Z]+[a-z]*)/,
},
const knownCases: Case[] = [
new SnakeCase(),
new PascalCase(),
new LowerCase(),
new UpperCase(),
new CamelCase(),
];

export default class StringUtilsCase {
Expand All @@ -68,7 +37,7 @@ export default class StringUtilsCase {
*
* @returns {ICase} - The case of given string
*/
public static determineCase(str: string): ICase | undefined {
public static determineCase(str: string): Case | undefined {
return knownCases.find((caseObject) => caseObject.matcher.test(str));
}

Expand Down Expand Up @@ -105,31 +74,99 @@ export default class StringUtilsCase {
* case: snakeCase
* returns: this_is_a_test
*/
public static convertToCase(str: string, caseToConvert: Case): string {
if (str.trim().replaceAll(' ', '').length < 2) return str;
public static convertToCase(str: string, caseToConvert: CaseName): string {
switch (caseToConvert) {
case 'LowerCase':
return this.convertToCaseLogic('LowerCase', str);
case 'UpperCase':
return this.convertToCaseLogic('UpperCase', str);
case 'CamelCase':
return this.convertToCaseLogic('CamelCase', str);
case 'PascalCase':
return this.convertToCaseLogic('PascalCase', str);
case 'SnakeCase':
return this.convertToCaseLogic('SnakeCase', str);
}
}

const splittedByCaseString = this.splitByCase(str);
if (splittedByCaseString.length == 1) return str; // Case was unsucessfully determinated
/**
* Returns a given string converted to camelCase
*
* @param {string} str - The string to convert
*
* @example
* str: ThisIsMyExample
* returns: thisIsMyExample
* @example
* str: thisIsMyExample
* returns: thisIsMyExample
*/
public static toCamelCase(str: string): string {
return this.convertToCaseLogic('CamelCase', str);
}

switch (caseToConvert) {
case 'lowerCase':
return splittedByCaseString.join('').toLowerCase();
case 'upperCase':
return splittedByCaseString.join('').toUpperCase();
case 'camelCase':
return splittedByCaseString
.map((subSequence, index) =>
index == 0
? subSequence.toLowerCase()
: StringUtilsWord.formatWord(subSequence),
)
.join('');
case 'pascalCase':
return splittedByCaseString
.map((subSequence) => StringUtilsWord.formatWord(subSequence))
.join('');
case 'snakeCase':
return splittedByCaseString.join('_').toLowerCase();
/**
* Returns a given string converted to PamelCase
*
* @param {string} str - The string to convert
*
* @example
* str: ThisIsMyExample
* returns: ThisIsMyExample
* @example
* str: thisIsMyExample
* returns: ThisIsMyExample
*/
public static toPascalCase(str: string): string {
return this.convertToCaseLogic('PascalCase', str);
}

/**
* Returns a given string converted to snakeCase
*
* @param {string} str - The string to convert
*
* @example
* str: ThisIsMyExample
* returns: this_is_my_example
* @example
* str: thisIsMyExample
* returns: this_is_my_example
*/
public static toSnakeCase(str: string): string {
return this.convertToCaseLogic('SnakeCase', str);
}

private static convertToCaseLogic(toCase: CaseName, str: string): string {
const correspondantKnownCase = knownCases.find(
(caseInstance: Case) => caseInstance.name == toCase,
);

// do not apply any process overload for nothing
if (!StringUtils.isConsiderableCharSequence(str)) return str;

if (!str.includes(' ')) {
// str do not need blending operation
if (!this.determineCase(str)) return str;

return correspondantKnownCase.basicConversionReturnFn(
this.splitByCase(str),
str,
); //Apply stored behavior of correspondantKnownCase and return the processed value
}

// str need blending operation

const removedBlankChars = StringUtils.removeBlankChars(str);
if (this.determineCase(removedBlankChars).name == toCase) {
//str is per any chance alraedy cased as wanted but needed to be cleanedFrom any blank chars
return removedBlankChars;
}

return correspondantKnownCase.blendedConversionReturnFn(
this.splitByCase(str),
StringUtils.blendIrrelevantStringsInRelevantOnes(str),
str,
); //Apply stored behavior of correspondantKnownCase and return the processed value
}
}
42 changes: 42 additions & 0 deletions src/case/Case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export default abstract class Case {
protected abstract _matcher: RegExp;
protected abstract _splitter: RegExp | string;

/**
* This method is a wrapper for conversion to targeted case (when `str` is already cased in managed one)
*/
public abstract basicConversionReturnFn(
splittedByCase: string[],
str: string,
): string;

/**
* This method is a wrapper for conversion to targeted case (when `str` contains spaces)
*/
public abstract blendedConversionReturnFn(
splittedByCase: string[],
blended: string[],
str: string,
): string;

/**
* Returns the name of class
*/
public get name(): string {
return this.constructor.name;
}

/**
* Returns the matcher for Regex linked to targeted case class
*/
public get matcher(): RegExp {
return this._matcher;
}

/**
* Returns the splitter for Regex or string linked to targeted case class
*/
public get splitter(): RegExp | string {
return this._splitter;
}
}
54 changes: 54 additions & 0 deletions src/case/camel-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Case from './Case';
import StringUtilsCase from '../case';
import StringUtilsWord from '../word';
import { StringUtils } from '../main';

export default class CamelCase extends Case {
protected _matcher = /^[a-z]+(?:[A-Z][a-z]+)*$/;
protected _splitter = /([A-Z]+[a-z]*)/;

/*
* `str` do not need blending operation (there's no spaces into it)
* from `splittedByCase`, for each subsequence
* (at this step, it should be atleast a subSequence of length >= 2: because of check if it's a considerable str)
* if current subSequence index is index 0, then lower it
* otherwise format the current subSequence as a word (c.f: with first char uppered and the rest lowered)
*/
public basicConversionReturnFn(
splittedByCase: string[],
str: string,
): string {
return splittedByCase
.map((subSequence, index) =>
index == 0
? subSequence.toLowerCase()
: StringUtilsWord.formatWord(subSequence),
)
.join('');
}

/*
* `str` was blent (see StringUtils.blendIrrelevantStringsInRelevantOnes) and stored in `blended`
* if blended length < 2 (c.f: blend operation didn't produce more than 1 relevant string) then
* the table to work with in previous statement will be the result of splitted removed from blank chars
* version of `str` (which is never modified).
* Otherwise use the result of blend operation as table to work with (blended)
*/
public blendedConversionReturnFn(
splittedByCase: string[],
blended: string[],
str: string,
): string {
return (
blended.length < 2
? StringUtilsCase.splitByCase(StringUtils.removeBlankChars(str))
: blended
)
.map((subSequence, index) =>
index == 0
? subSequence.toLowerCase()
: StringUtilsWord.formatWord(subSequence),
)
.join('');
}
}
21 changes: 21 additions & 0 deletions src/case/lower-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Case from './Case';

export default class LowerCase extends Case {
protected _matcher = /^[a-z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\s]*$/;
protected _splitter = '';

public basicConversionReturnFn(
splittedByCase: string[],
str: string,
): string {
return str.toLowerCase();
}

public blendedConversionReturnFn(

Check warning on line 14 in src/case/lower-case.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
splittedByCase: string[],
blended: string[],
str: string,
): string {
return str.toLowerCase();

Check warning on line 19 in src/case/lower-case.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}
}
Loading
Loading