@e22m4u/js-repository-mongodb-adapter 0.5.6 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,7 @@
1
+ export * from './pluralize.js';
1
2
  export * from './is-iso-date.js';
2
3
  export * from './is-object-id.js';
4
+ export * from './to-camel-case.js';
3
5
  export * from './create-mongodb-url.js';
4
6
  export * from './transform-values-deep.js';
7
+ export * from './model-name-to-collection-name.js';
@@ -0,0 +1,26 @@
1
+ import {pluralize} from './pluralize.js';
2
+ import {toCamelCase} from './to-camel-case.js';
3
+
4
+ /**
5
+ * Создает имя таблицы/коллекции по названию модели.
6
+ *
7
+ * @param {string} modelName
8
+ * @returns {string}
9
+ */
10
+ export function modelNameToCollectionName(modelName) {
11
+ // приведение имени класса к стандартному camelCase
12
+ // "UserModel" -> "userModel", "Article" -> "article"
13
+ const ccName = toCamelCase(modelName);
14
+ // удаление постфикса "Model" с конца строки
15
+ // "userModel" -> "user", "myModel" -> "my"
16
+ const woModel = ccName.replace(/Model$/i, '');
17
+ // если базовое имя слишком короткое (как "my" для "myModel"),
18
+ // то используется имя, включающее постфикс "Model"
19
+ if (woModel.length <= 2) {
20
+ // pluralize('myModel') -> "myModels"
21
+ return pluralize(ccName);
22
+ }
23
+ // для обычных имен обрабатывается без суффикса
24
+ // pluralize('user') -> "users"
25
+ return pluralize(woModel);
26
+ }
@@ -0,0 +1,89 @@
1
+ import {expect} from 'chai';
2
+ import {modelNameToCollectionName} from './model-name-to-collection-name.js';
3
+
4
+ describe('modelNameToCollectionName', function () {
5
+ it('should correctly pluralize and remove the "Model" suffix for standard names', function () {
6
+ expect(modelNameToCollectionName('userModel')).to.equal('users');
7
+ expect(modelNameToCollectionName('UserModel')).to.equal('users');
8
+ expect(modelNameToCollectionName('user_model')).to.equal('users');
9
+ expect(modelNameToCollectionName('USER_MODEL')).to.equal('users');
10
+ expect(modelNameToCollectionName('articleModel')).to.equal('articles');
11
+ expect(modelNameToCollectionName('ArticleModel')).to.equal('articles');
12
+ expect(modelNameToCollectionName('article_model')).to.equal('articles');
13
+ expect(modelNameToCollectionName('ARTICLE_MODEL')).to.equal('articles');
14
+ });
15
+
16
+ it('should just pluralize names that do not have the "Model" suffix', function () {
17
+ expect(modelNameToCollectionName('user')).to.equal('users');
18
+ expect(modelNameToCollectionName('User')).to.equal('users');
19
+ expect(modelNameToCollectionName('USER')).to.equal('users');
20
+ expect(modelNameToCollectionName('article')).to.equal('articles');
21
+ expect(modelNameToCollectionName('Article')).to.equal('articles');
22
+ expect(modelNameToCollectionName('ARTICLE')).to.equal('articles');
23
+ });
24
+
25
+ it('should correctly handle already pluralized names with the "Model" suffix', function () {
26
+ expect(modelNameToCollectionName('usersModel')).to.equal('users');
27
+ expect(modelNameToCollectionName('UsersModel')).to.equal('users');
28
+ expect(modelNameToCollectionName('users_model')).to.equal('users');
29
+ expect(modelNameToCollectionName('USERS_MODEL')).to.equal('users');
30
+ expect(modelNameToCollectionName('articlesModel')).to.equal('articles');
31
+ expect(modelNameToCollectionName('ArticlesModel')).to.equal('articles');
32
+ expect(modelNameToCollectionName('articles_model')).to.equal('articles');
33
+ expect(modelNameToCollectionName('ARTICLES_MODEL')).to.equal('articles');
34
+ });
35
+
36
+ it('should correctly handle already pluralized names', function () {
37
+ expect(modelNameToCollectionName('users')).to.equal('users');
38
+ expect(modelNameToCollectionName('Users')).to.equal('users');
39
+ expect(modelNameToCollectionName('USERS')).to.equal('users');
40
+ expect(modelNameToCollectionName('articles')).to.equal('articles');
41
+ expect(modelNameToCollectionName('Articles')).to.equal('articles');
42
+ expect(modelNameToCollectionName('ARTICLES')).to.equal('articles');
43
+ });
44
+
45
+ it('should correctly handle different pluralization rules (like y -> ies)', function () {
46
+ expect(modelNameToCollectionName('companyModel')).to.equal('companies');
47
+ expect(modelNameToCollectionName('CompanyModel')).to.equal('companies');
48
+ expect(modelNameToCollectionName('company_model')).to.equal('companies');
49
+ expect(modelNameToCollectionName('COMPANY_MODEL')).to.equal('companies');
50
+ });
51
+
52
+ it('should correctly handle exceptions from pluralize (like status -> statuses)', function () {
53
+ expect(modelNameToCollectionName('statusModel')).to.equal('statuses');
54
+ expect(modelNameToCollectionName('StatusModel')).to.equal('statuses');
55
+ expect(modelNameToCollectionName('status_model')).to.equal('statuses');
56
+ expect(modelNameToCollectionName('STATUS_MODEL')).to.equal('statuses');
57
+ });
58
+
59
+ it('should handle edge cases where removing "Model" leaves a short word', function () {
60
+ expect(modelNameToCollectionName('myModel')).to.equal('myModels');
61
+ expect(modelNameToCollectionName('MyModel')).to.equal('myModels');
62
+ expect(modelNameToCollectionName('my_model')).to.equal('myModels');
63
+ expect(modelNameToCollectionName('MY_MODEL')).to.equal('myModels');
64
+ expect(modelNameToCollectionName('doModel')).to.equal('doModels');
65
+ expect(modelNameToCollectionName('DoModel')).to.equal('doModels');
66
+ expect(modelNameToCollectionName('do_model')).to.equal('doModels');
67
+ expect(modelNameToCollectionName('DO_MODEL')).to.equal('doModels');
68
+ });
69
+
70
+ it('should remove the "Model" suffix case-insensitively', function () {
71
+ expect(modelNameToCollectionName('Usermodel')).to.equal('users');
72
+ expect(modelNameToCollectionName('USERMODEL')).to.equal('users');
73
+ });
74
+
75
+ it('should handle names that contain "Model" but not at the end', function () {
76
+ expect(modelNameToCollectionName('remodelAction')).to.equal(
77
+ 'remodelActions',
78
+ );
79
+ expect(modelNameToCollectionName('RemodelAction')).to.equal(
80
+ 'remodelActions',
81
+ );
82
+ expect(modelNameToCollectionName('remodel_action')).to.equal(
83
+ 'remodelActions',
84
+ );
85
+ expect(modelNameToCollectionName('REMODEL_ACTION')).to.equal(
86
+ 'remodelActions',
87
+ );
88
+ });
89
+ });
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Список регулярных выражений для проверки слов-исключений в единственном
3
+ * числе, которые заканчиваются на "s" и требуют особого обращения.
4
+ * Использование /word$/i позволяет корректно обрабатывать camelCase
5
+ * и snake_case строки.
6
+ */
7
+ const singularExceptions = [
8
+ /access$/i,
9
+ /address$/i,
10
+ /alias$/i,
11
+ /bonus$/i,
12
+ /boss$/i,
13
+ /bus$/i,
14
+ /business$/i,
15
+ /canvas$/i,
16
+ /class$/i,
17
+ /cross$/i,
18
+ /dress$/i,
19
+ /focus$/i,
20
+ /gas$/i,
21
+ /glass$/i,
22
+ /kiss$/i,
23
+ /lens$/i,
24
+ /loss$/i,
25
+ /pass$/i,
26
+ /plus$/i,
27
+ /process$/i,
28
+ /status$/i,
29
+ /success$/i,
30
+ /virus$/i,
31
+ ];
32
+
33
+ /**
34
+ * Pluralize.
35
+ *
36
+ * @param {string} input
37
+ * @returns {string}
38
+ */
39
+ export function pluralize(input) {
40
+ if (!input || typeof input !== 'string') {
41
+ return input;
42
+ }
43
+ // если слово уже во множественном числе,
44
+ // то возвращается без изменений
45
+ if (/s$/i.test(input) && !singularExceptions.some(re => re.test(input))) {
46
+ return input;
47
+ }
48
+ // определение регистра для окончания по последнему символу
49
+ // учитывает случай, когда последним символом является цифра
50
+ const lastChar = input.slice(-1);
51
+ const isLastCharUpper =
52
+ lastChar === lastChar.toUpperCase() && lastChar !== lastChar.toLowerCase();
53
+ // если заканчивается на s, x, z, ch, sh -> добавляем "es"
54
+ if (/(s|x|z|ch|sh)$/i.test(input)) {
55
+ return input + (isLastCharUpper ? 'ES' : 'es');
56
+ }
57
+ // если заканчивается на согласную + y -> меняем "y" на "ies"
58
+ if (/[^aeiou]y$/i.test(input)) {
59
+ return input.slice(0, -1) + (isLastCharUpper ? 'IES' : 'ies');
60
+ }
61
+ // по умолчанию добавляется "s"
62
+ return input + (isLastCharUpper ? 'S' : 's');
63
+ }
@@ -0,0 +1,110 @@
1
+ import {expect} from 'chai';
2
+ import {pluralize} from './pluralize.js';
3
+
4
+ describe('pluralize function', function () {
5
+ describe('basic pluralization rules', function () {
6
+ it('should add "s" to simple nouns', function () {
7
+ expect(pluralize('apple')).to.equal('apples');
8
+ expect(pluralize('table')).to.equal('tables');
9
+ });
10
+
11
+ it('should add "es" to nouns ending in s, x, z, ch, sh', function () {
12
+ expect(pluralize('box')).to.equal('boxes');
13
+ expect(pluralize('watch')).to.equal('watches');
14
+ expect(pluralize('dish')).to.equal('dishes');
15
+ expect(pluralize('buzz')).to.equal('buzzes');
16
+ });
17
+
18
+ it('should change "y" to "ies" for nouns ending in a consonant + y', function () {
19
+ expect(pluralize('city')).to.equal('cities');
20
+ expect(pluralize('party')).to.equal('parties');
21
+ });
22
+
23
+ it('should just add "s" for nouns ending in a vowel + y', function () {
24
+ expect(pluralize('boy')).to.equal('boys');
25
+ expect(pluralize('key')).to.equal('keys');
26
+ });
27
+ });
28
+
29
+ describe('handling already plural nouns', function () {
30
+ it('should not change nouns that are already plural', function () {
31
+ expect(pluralize('apples')).to.equal('apples');
32
+ expect(pluralize('boxes')).to.equal('boxes');
33
+ expect(pluralize('cities')).to.equal('cities');
34
+ });
35
+ });
36
+
37
+ describe('handling singular exceptions', function () {
38
+ it('should correctly pluralize nouns from the exceptions list', function () {
39
+ expect(pluralize('bus')).to.equal('buses');
40
+ expect(pluralize('process')).to.equal('processes');
41
+ expect(pluralize('status')).to.equal('statuses');
42
+ expect(pluralize('business')).to.equal('businesses');
43
+ });
44
+
45
+ it('should not change already pluralized exceptions', function () {
46
+ expect(pluralize('buses')).to.equal('buses');
47
+ expect(pluralize('processes')).to.equal('processes');
48
+ });
49
+
50
+ it('should not change words that were removed from exceptions (like analysis)', function () {
51
+ expect(pluralize('analysis')).to.equal('analysis');
52
+ expect(pluralize('thesis')).to.equal('thesis');
53
+ });
54
+ });
55
+
56
+ describe('handling capital letters', function () {
57
+ it('should preserve case for the base word and add lowercase endings', function () {
58
+ expect(pluralize('Apple')).to.equal('Apples');
59
+ expect(pluralize('Bus')).to.equal('Buses');
60
+ expect(pluralize('City')).to.equal('Cities');
61
+ });
62
+
63
+ it('should add uppercase endings for all-caps words', function () {
64
+ expect(pluralize('APPLE')).to.equal('APPLES');
65
+ expect(pluralize('BUS')).to.equal('BUSES');
66
+ expect(pluralize('CITY')).to.equal('CITIES');
67
+ });
68
+ });
69
+
70
+ describe('handling multi-word strings and different casings', function () {
71
+ it('should pluralize the end of a camelCase string', function () {
72
+ expect(pluralize('userProfile')).to.equal('userProfiles');
73
+ expect(pluralize('accessPass')).to.equal('accessPasses');
74
+ expect(pluralize('dataEntry')).to.equal('dataEntries');
75
+ });
76
+
77
+ it('should pluralize the end of a PascalCase string', function () {
78
+ expect(pluralize('UserProfile')).to.equal('UserProfiles');
79
+ expect(pluralize('AccessPass')).to.equal('AccessPasses');
80
+ expect(pluralize('DataEntry')).to.equal('DataEntries');
81
+ });
82
+
83
+ it('should pluralize the end of a snake_case string', function () {
84
+ expect(pluralize('user_profile')).to.equal('user_profiles');
85
+ expect(pluralize('access_pass')).to.equal('access_passes');
86
+ expect(pluralize('data_entry')).to.equal('data_entries');
87
+ });
88
+
89
+ it('should pluralize the end of an UPPER_CASE string', function () {
90
+ expect(pluralize('USER_PROFILE')).to.equal('USER_PROFILES');
91
+ expect(pluralize('ACCESS_PASS')).to.equal('ACCESS_PASSES');
92
+ expect(pluralize('DATA_ENTRY')).to.equal('DATA_ENTRIES');
93
+ // проверка сохранения регистра для 'y' -> 'ies'
94
+ expect(pluralize('API_KEY')).to.equal('API_KEYS');
95
+ expect(pluralize('COMPANY_PARTY')).to.equal('COMPANY_PARTIES');
96
+ });
97
+
98
+ it('should not change already pluralized multi-word strings', function () {
99
+ expect(pluralize('userProfiles')).to.equal('userProfiles');
100
+ expect(pluralize('access_passes')).to.equal('access_passes');
101
+ expect(pluralize('DATA_ENTRIES')).to.equal('DATA_ENTRIES');
102
+ });
103
+ });
104
+
105
+ describe('edge cases and invalid input', function () {
106
+ it('should return the input unchanged for empty strings', function () {
107
+ expect(pluralize('')).to.equal('');
108
+ });
109
+ });
110
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * To camel case.
3
+ *
4
+ * @param {string} input
5
+ * @returns {string}
6
+ */
7
+ export function toCamelCase(input) {
8
+ if (!input) return '';
9
+ const spacedString = String(input)
10
+ .replace(/([-_])/g, ' ')
11
+ .replace(/([a-z])([A-Z])/g, '$1 $2');
12
+ const intermediateCased = spacedString
13
+ .toLowerCase()
14
+ .replace(/\s(.)/g, $1 => $1.toUpperCase())
15
+ .replace(/\s/g, '');
16
+ if (!intermediateCased) return '';
17
+ return intermediateCased.charAt(0).toLowerCase() + intermediateCased.slice(1);
18
+ }
@@ -0,0 +1,75 @@
1
+ import {expect} from 'chai';
2
+ import {toCamelCase} from './to-camel-case.js';
3
+
4
+ describe('toCamelCase function', function () {
5
+ describe('PascalCase inputs', function () {
6
+ it('should convert a simple PascalCase string to camelCase', function () {
7
+ expect(toCamelCase('UserModel')).to.equal('userModel');
8
+ });
9
+
10
+ it('should convert a single PascalCase word to lowercase', function () {
11
+ expect(toCamelCase('User')).to.equal('user');
12
+ });
13
+
14
+ it('should correctly split multiple words in PascalCase', function () {
15
+ expect(toCamelCase('UserLoginAttempt')).to.equal('userLoginAttempt');
16
+ });
17
+ });
18
+
19
+ describe('snake_case and kebab-case inputs', function () {
20
+ it('should convert a snake_case string to camelCase', function () {
21
+ expect(toCamelCase('user_model')).to.equal('userModel');
22
+ });
23
+
24
+ it('should convert a kebab-case string to camelCase', function () {
25
+ expect(toCamelCase('user-model-data')).to.equal('userModelData');
26
+ });
27
+
28
+ it('should handle leading and trailing separators', function () {
29
+ expect(toCamelCase('_user_model_')).to.equal('userModel');
30
+ expect(toCamelCase('-user-model-')).to.equal('userModel');
31
+ });
32
+ });
33
+
34
+ describe('ALL_CAPS inputs', function () {
35
+ it('should convert an ALL_CAPS word to lowercase', function () {
36
+ expect(toCamelCase('USER')).to.equal('user');
37
+ });
38
+
39
+ it('should convert an ALL_CAPS_SNAKE_CASE string to camelCase', function () {
40
+ expect(toCamelCase('USER_MODEL')).to.equal('userModel');
41
+ });
42
+
43
+ it('should handle ALL_CAPS words without separators', function () {
44
+ expect(toCamelCase('USERMODEL')).to.equal('usermodel');
45
+ });
46
+ });
47
+
48
+ describe('Mixed and complex inputs', function () {
49
+ it('should be idempotent (not change an already camelCased string)', function () {
50
+ expect(toCamelCase('userModel')).to.equal('userModel');
51
+ });
52
+
53
+ it('should handle mixed cases like Pascal_Snake_Case', function () {
54
+ expect(toCamelCase('User_Login_Attempt')).to.equal('userLoginAttempt');
55
+ });
56
+
57
+ it('should handle strings with numbers', function () {
58
+ expect(toCamelCase('Version1_2_3')).to.equal('version123');
59
+ expect(toCamelCase('user_model_v2')).to.equal('userModelV2');
60
+ });
61
+ });
62
+
63
+ describe('Edge cases and invalid inputs', function () {
64
+ it('should return an empty string for falsy or empty inputs', function () {
65
+ expect(toCamelCase('')).to.equal('');
66
+ expect(toCamelCase(null)).to.equal('');
67
+ expect(toCamelCase(undefined)).to.equal('');
68
+ });
69
+
70
+ it('should handle a string that is just a separator', function () {
71
+ expect(toCamelCase('_')).to.equal('');
72
+ expect(toCamelCase('__--__')).to.equal('');
73
+ });
74
+ });
75
+ });