@fibery/pluralize 1.0.1 → 1.0.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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Pluralize or singularize a word based on the passed in count.
3
+ *
4
+ * @param {string} word The word to pluralize
5
+ * @param {number} count How many of the word exist
6
+ * @param {boolean} inclusive Whether to prefix with the number (e.g. 3 ducks)
7
+ * @return {string}
8
+ */
9
+ export declare function pluralize(word: string, count?: number, inclusive?: boolean): string;
10
+ export declare namespace pluralize {
11
+ var plural: (word: string) => string;
12
+ var isPlural: (word: string) => boolean;
13
+ var singular: (word: string) => string;
14
+ var isSingular: (word: string) => boolean;
15
+ var addPluralRule: (rule: string | RegExp, replacement: string) => void;
16
+ var addSingularRule: (rule: string | RegExp, replacement: string) => void;
17
+ var addUncountableRule: (word: string | RegExp) => void;
18
+ var addIrregularRule: (single: string, plural: string) => void;
19
+ }
@@ -1,26 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pluralize = pluralize;
1
4
  /* eslint-disable max-lines */
2
5
  // Rule storage - pluralize and singularize need to be run sequentially,
3
6
  // while other rules can be optimized using an object for instant lookups.
4
- const pluralRules: [RegExp, string][] = [];
5
- const singularRules: [RegExp, string][] = [];
6
- const uncountables: Record<string, boolean> = {};
7
- const irregularPlurals: Record<string, string> = {};
8
- const irregularSingles: Record<string, string> = {};
9
-
7
+ const pluralRules = [];
8
+ const singularRules = [];
9
+ const uncountables = {};
10
+ const irregularPlurals = {};
11
+ const irregularSingles = {};
10
12
  /**
11
13
  * Sanitize a pluralization rule to a usable regular expression.
12
14
  *
13
15
  * @param {(RegExp|string)} rule
14
16
  * @return {RegExp}
15
17
  */
16
- function sanitizeRule(rule: string | RegExp): RegExp {
17
- if (typeof rule === "string") {
18
- return new RegExp("^" + rule + "$", "i");
19
- }
20
-
21
- return rule;
18
+ function sanitizeRule(rule) {
19
+ if (typeof rule === "string") {
20
+ return new RegExp("^" + rule + "$", "i");
21
+ }
22
+ return rule;
22
23
  }
23
-
24
24
  /**
25
25
  * Pass in a word token to produce a function that can replicate the case on
26
26
  * another word.
@@ -29,31 +29,26 @@ function sanitizeRule(rule: string | RegExp): RegExp {
29
29
  * @param {string} token
30
30
  * @return {Function}
31
31
  */
32
- function restoreCase(word: string, token: string): string {
33
- // Tokens are an exact match.
34
- if (word === token) {
35
- return token;
36
- }
37
-
38
- // Lower cased words. E.g. "hello".
39
- if (word === word.toLowerCase()) {
32
+ function restoreCase(word, token) {
33
+ // Tokens are an exact match.
34
+ if (word === token) {
35
+ return token;
36
+ }
37
+ // Lower cased words. E.g. "hello".
38
+ if (word === word.toLowerCase()) {
39
+ return token.toLowerCase();
40
+ }
41
+ // Upper cased words. E.g. "WHISKY".
42
+ if (word === word.toUpperCase()) {
43
+ return token.toUpperCase();
44
+ }
45
+ // Title cased words. E.g. "Title".
46
+ if (word[0] === word[0].toUpperCase()) {
47
+ return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();
48
+ }
49
+ // Lower cased words. E.g. "test".
40
50
  return token.toLowerCase();
41
- }
42
-
43
- // Upper cased words. E.g. "WHISKY".
44
- if (word === word.toUpperCase()) {
45
- return token.toUpperCase();
46
- }
47
-
48
- // Title cased words. E.g. "Title".
49
- if (word[0] === word[0].toUpperCase()) {
50
- return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();
51
- }
52
-
53
- // Lower cased words. E.g. "test".
54
- return token.toLowerCase();
55
51
  }
56
-
57
52
  /**
58
53
  * Interpolate a regexp string.
59
54
  *
@@ -61,12 +56,11 @@ function restoreCase(word: string, token: string): string {
61
56
  * @param {Array} args
62
57
  * @return {string}
63
58
  */
64
- function interpolate(str: string, args: string[]): string {
65
- return str.replace(/\$(\d{1,2})/g, function (match, index) {
66
- return args[index] || "";
67
- });
59
+ function interpolate(str, args) {
60
+ return str.replace(/\$(\d{1,2})/g, function (match, index) {
61
+ return args[index] || "";
62
+ });
68
63
  }
69
-
70
64
  /**
71
65
  * Replace a word using a rule.
72
66
  *
@@ -74,19 +68,16 @@ function interpolate(str: string, args: string[]): string {
74
68
  * @param {Array} rule
75
69
  * @return {string}
76
70
  */
77
- function replace(word: string, rule: [RegExp, string]): string {
78
- return word.replace(rule[0], function (match, index) {
79
- // eslint-disable-next-line prefer-rest-params
80
- const result = interpolate(rule[1], arguments as unknown as string[]);
81
-
82
- if (match === "") {
83
- return restoreCase(word[index - 1], result);
84
- }
85
-
86
- return restoreCase(match, result);
87
- });
71
+ function replace(word, rule) {
72
+ return word.replace(rule[0], function (match, index) {
73
+ // eslint-disable-next-line prefer-rest-params
74
+ const result = interpolate(rule[1], arguments);
75
+ if (match === "") {
76
+ return restoreCase(word[index - 1], result);
77
+ }
78
+ return restoreCase(match, result);
79
+ });
88
80
  }
89
-
90
81
  /**
91
82
  * Sanitize a word by passing in the word and sanitization rules.
92
83
  *
@@ -95,26 +86,21 @@ function replace(word: string, rule: [RegExp, string]): string {
95
86
  * @param {Array} rules
96
87
  * @return {string}
97
88
  */
98
- function sanitizeWord(token: string, word: string, rules: [RegExp, string][]): string {
99
- // Empty string or doesn't need fixing.
100
- if (!token.length || Object.keys(uncountables).some((key) => token.includes(key))) {
101
- return word;
102
- }
103
-
104
- let len = rules.length;
105
-
106
- // Iterate over the sanitization rules and use the first one to match.
107
- while (len--) {
108
- const rule = rules[len];
109
-
110
- if (rule[0].test(word)) {
111
- return replace(word, rule);
89
+ function sanitizeWord(token, word, rules) {
90
+ // Empty string or doesn't need fixing.
91
+ if (!token.length || Object.keys(uncountables).some((key) => token.includes(key))) {
92
+ return word;
112
93
  }
113
- }
114
-
115
- return word;
94
+ let len = rules.length;
95
+ // Iterate over the sanitization rules and use the first one to match.
96
+ while (len--) {
97
+ const rule = rules[len];
98
+ if (rule[0].test(word)) {
99
+ return replace(word, rule);
100
+ }
101
+ }
102
+ return word;
116
103
  }
117
-
118
104
  /**
119
105
  * Replace a word with the updated word.
120
106
  *
@@ -123,52 +109,37 @@ function sanitizeWord(token: string, word: string, rules: [RegExp, string][]): s
123
109
  * @param {Array} rules
124
110
  * @return {Function}
125
111
  */
126
- function replaceWord(
127
- replaceMap: Record<string, string>,
128
- keepMap: Record<string, string>,
129
- rules: [RegExp, string][]
130
- ): (word: string) => string {
131
- return function (word) {
132
- // Get the correct token and case restoration functions.
133
- const token = word.toLowerCase();
134
-
135
- // Check against the keep object map.
136
- if (Object.hasOwn(keepMap, token)) {
137
- return restoreCase(word, token);
138
- }
139
-
140
- // Check against the replacement map for a direct word replacement.
141
- if (Object.hasOwn(replaceMap, token)) {
142
- return restoreCase(word, replaceMap[token]);
143
- }
144
-
145
- // Run all the rules against the word.
146
- return sanitizeWord(token, word, rules);
147
- };
112
+ function replaceWord(replaceMap, keepMap, rules) {
113
+ return function (word) {
114
+ // Get the correct token and case restoration functions.
115
+ const token = word.toLowerCase();
116
+ // Check against the keep object map.
117
+ if (Object.hasOwn(keepMap, token)) {
118
+ return restoreCase(word, token);
119
+ }
120
+ // Check against the replacement map for a direct word replacement.
121
+ if (Object.hasOwn(replaceMap, token)) {
122
+ return restoreCase(word, replaceMap[token]);
123
+ }
124
+ // Run all the rules against the word.
125
+ return sanitizeWord(token, word, rules);
126
+ };
148
127
  }
149
-
150
128
  /**
151
129
  * Check if a word is part of the map.
152
130
  */
153
- function checkWord(
154
- replaceMap: Record<string, string>,
155
- keepMap: Record<string, string>,
156
- rules: [RegExp, string][]
157
- ): (word: string) => boolean {
158
- return function (word) {
159
- const token = word.toLowerCase();
160
-
161
- if (Object.hasOwn(keepMap, token)) {
162
- return true;
163
- }
164
- if (Object.hasOwn(replaceMap, token)) {
165
- return false;
166
- }
167
-
168
- return sanitizeWord(token, token, rules) === token;
169
- };
131
+ function checkWord(replaceMap, keepMap, rules) {
132
+ return function (word) {
133
+ const token = word.toLowerCase();
134
+ if (Object.hasOwn(keepMap, token)) {
135
+ return true;
136
+ }
137
+ if (Object.hasOwn(replaceMap, token)) {
138
+ return false;
139
+ }
140
+ return sanitizeWord(token, token, rules) === token;
141
+ };
170
142
  }
171
-
172
143
  /**
173
144
  * Pluralize or singularize a word based on the passed in count.
174
145
  *
@@ -177,100 +148,87 @@ function checkWord(
177
148
  * @param {boolean} inclusive Whether to prefix with the number (e.g. 3 ducks)
178
149
  * @return {string}
179
150
  */
180
- export function pluralize(word: string, count?: number, inclusive?: boolean): string {
181
- if (count === undefined) {
182
- return pluralize.plural(word);
183
- }
184
- const pluralized = count === 1 ? pluralize.singular(word) : pluralize.plural(word);
185
-
186
- return (inclusive ? count + " " : "") + pluralized;
151
+ function pluralize(word, count, inclusive) {
152
+ if (count === undefined) {
153
+ return pluralize.plural(word);
154
+ }
155
+ const pluralized = count === 1 ? pluralize.singular(word) : pluralize.plural(word);
156
+ return (inclusive ? count + " " : "") + pluralized;
187
157
  }
188
-
189
158
  /**
190
159
  * Pluralize a word.
191
160
  *
192
161
  * @type {Function}
193
162
  */
194
163
  pluralize.plural = replaceWord(irregularSingles, irregularPlurals, pluralRules);
195
-
196
164
  /**
197
165
  * Check if a word is plural.
198
166
  *
199
167
  * @type {Function}
200
168
  */
201
169
  pluralize.isPlural = checkWord(irregularSingles, irregularPlurals, pluralRules);
202
-
203
170
  /**
204
171
  * Singularize a word.
205
172
  *
206
173
  * @type {Function}
207
174
  */
208
175
  pluralize.singular = replaceWord(irregularPlurals, irregularSingles, singularRules);
209
-
210
176
  /**
211
177
  * Check if a word is singular.
212
178
  *
213
179
  * @type {Function}
214
180
  */
215
181
  pluralize.isSingular = checkWord(irregularPlurals, irregularSingles, singularRules);
216
-
217
182
  /**
218
183
  * Add a pluralization rule to the collection.
219
184
  *
220
185
  * @param {(string|RegExp)} rule
221
186
  * @param {string} replacement
222
187
  */
223
- pluralize.addPluralRule = function (rule: string | RegExp, replacement: string) {
224
- pluralRules.push([sanitizeRule(rule), replacement]);
188
+ pluralize.addPluralRule = function (rule, replacement) {
189
+ pluralRules.push([sanitizeRule(rule), replacement]);
225
190
  };
226
-
227
191
  /**
228
192
  * Add a singularization rule to the collection.
229
193
  *
230
194
  * @param {(string|RegExp)} rule
231
195
  * @param {string} replacement
232
196
  */
233
- pluralize.addSingularRule = function (rule: string | RegExp, replacement: string) {
234
- singularRules.push([sanitizeRule(rule), replacement]);
197
+ pluralize.addSingularRule = function (rule, replacement) {
198
+ singularRules.push([sanitizeRule(rule), replacement]);
235
199
  };
236
-
237
200
  /**
238
201
  * Add an uncountable word rule.
239
202
  *
240
203
  * @param {(string|RegExp)} word
241
204
  */
242
- pluralize.addUncountableRule = function (word: string | RegExp) {
243
- if (typeof word === "string") {
244
- uncountables[word.toLowerCase()] = true;
245
- return;
246
- }
247
-
248
- // Set singular and plural references for the word.
249
- pluralize.addPluralRule(word, "$0");
250
- pluralize.addSingularRule(word, "$0");
205
+ pluralize.addUncountableRule = function (word) {
206
+ if (typeof word === "string") {
207
+ uncountables[word.toLowerCase()] = true;
208
+ return;
209
+ }
210
+ // Set singular and plural references for the word.
211
+ pluralize.addPluralRule(word, "$0");
212
+ pluralize.addSingularRule(word, "$0");
251
213
  };
252
-
253
214
  /**
254
215
  * Add an irregular word definition.
255
216
  *
256
217
  * @param {string} single
257
218
  * @param {string} plural
258
219
  */
259
- pluralize.addIrregularRule = function (single: string, plural: string) {
260
- // eslint-disable-next-line no-param-reassign
261
- plural = plural.toLowerCase();
262
- // eslint-disable-next-line no-param-reassign
263
- single = single.toLowerCase();
264
-
265
- irregularSingles[single] = plural;
266
- irregularPlurals[plural] = single;
220
+ pluralize.addIrregularRule = function (single, plural) {
221
+ // eslint-disable-next-line no-param-reassign
222
+ plural = plural.toLowerCase();
223
+ // eslint-disable-next-line no-param-reassign
224
+ single = single.toLowerCase();
225
+ irregularSingles[single] = plural;
226
+ irregularPlurals[plural] = single;
267
227
  };
268
-
269
228
  /**
270
229
  * Irregular rules.
271
230
  */
272
- (
273
- [
231
+ [
274
232
  // Pronouns.
275
233
  ["I", "we"],
276
234
  ["me", "us"],
@@ -328,16 +286,13 @@ pluralize.addIrregularRule = function (single: string, plural: string) {
328
286
  ["pickaxe", "pickaxes"],
329
287
  ["passerby", "passersby"],
330
288
  ["canvas", "canvases"],
331
- ] as const
332
- ).forEach(function (rule) {
333
- return pluralize.addIrregularRule(rule[0], rule[1]);
289
+ ].forEach(function (rule) {
290
+ return pluralize.addIrregularRule(rule[0], rule[1]);
334
291
  });
335
-
336
292
  /**
337
293
  * Pluralization rules.
338
294
  */
339
- (
340
- [
295
+ [
341
296
  [/s?$/i, "s"],
342
297
  // eslint-disable-next-line no-control-regex
343
298
  [/[^\u0000-\u007F]$/i, "$0"],
@@ -351,8 +306,8 @@ pluralize.addIrregularRule = function (single: string, plural: string) {
351
306
  [/(seraph|cherub)(?:im)?$/i, "$1im"],
352
307
  [/(her|at|gr)o$/i, "$1oes"],
353
308
  [
354
- /(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i,
355
- "$1a",
309
+ /(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i,
310
+ "$1a",
356
311
  ],
357
312
  [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i, "$1a"],
358
313
  [/sis$/i, "ses"],
@@ -367,16 +322,13 @@ pluralize.addIrregularRule = function (single: string, plural: string) {
367
322
  [/eaux$/i, "$0"],
368
323
  [/m[ae]n$/i, "men"],
369
324
  ["thou", "you"],
370
- ] as const
371
- ).forEach(function (rule) {
372
- return pluralize.addPluralRule(rule[0], rule[1]);
325
+ ].forEach(function (rule) {
326
+ return pluralize.addPluralRule(rule[0], rule[1]);
373
327
  });
374
-
375
328
  /**
376
329
  * Singularization rules.
377
330
  */
378
- (
379
- [
331
+ [
380
332
  [/s$/i, ""],
381
333
  [/(ss)$/i, "$1"],
382
334
  [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i, "$1fe"],
@@ -384,8 +336,8 @@ pluralize.addIrregularRule = function (single: string, plural: string) {
384
336
  [/ies$/i, "y"],
385
337
  [/(dg|ss|ois|lk|ok|wn|mb|th|ch|ec|oal|is|ck|ix|sser|ts|wb)ies$/i, "$1ie"],
386
338
  [
387
- /\b(l|(?:neck|cross|hog|aun)?t|coll|faer|food|gen|goon|group|hipp|junk|vegg|(?:pork)?p|charl|calor|cut)ies$/i,
388
- "$1ie",
339
+ /\b(l|(?:neck|cross|hog|aun)?t|coll|faer|food|gen|goon|group|hipp|junk|vegg|(?:pork)?p|charl|calor|cut)ies$/i,
340
+ "$1ie",
389
341
  ],
390
342
  [/\b(mon|smil)ies$/i, "$1ey"],
391
343
  [/\b((?:tit)?m|l)ice$/i, "$1ouse"],
@@ -404,116 +356,114 @@ pluralize.addIrregularRule = function (single: string, plural: string) {
404
356
  [/(child)ren$/i, "$1"],
405
357
  [/(eau)x?$/i, "$1"],
406
358
  [/men$/i, "man"],
407
- ] as const
408
- ).forEach(function (rule) {
409
- return pluralize.addSingularRule(rule[0], rule[1]);
359
+ ].forEach(function (rule) {
360
+ return pluralize.addSingularRule(rule[0], rule[1]);
410
361
  });
411
-
412
362
  /**
413
363
  * Uncountable rules.
414
364
  */
415
365
  [
416
- // Singular words with no plurals.
417
- "adulthood",
418
- "advice",
419
- "agenda",
420
- "aid",
421
- "aircraft",
422
- "alcohol",
423
- "ammo",
424
- "analytics",
425
- "anime",
426
- "athletics",
427
- "audio",
428
- "bison",
429
- "blood",
430
- "bream",
431
- "buffalo",
432
- "butter",
433
- "carp",
434
- "cash",
435
- "chassis",
436
- "chess",
437
- "clothing",
438
- "cod",
439
- "commerce",
440
- "cooperation",
441
- "corps",
442
- "debris",
443
- "diabetes",
444
- "digestion",
445
- "elk",
446
- "energy",
447
- "equipment",
448
- "excretion",
449
- "expertise",
450
- "firmware",
451
- "flounder",
452
- "fun",
453
- "gallows",
454
- "garbage",
455
- "graffiti",
456
- "hardware",
457
- "headquarters",
458
- "health",
459
- "herpes",
460
- "highjinks",
461
- "homework",
462
- "housework",
463
- "information",
464
- "jeans",
465
- "justice",
466
- "kudos",
467
- "labour",
468
- "literature",
469
- "machinery",
470
- "mackerel",
471
- "mail",
472
- "media",
473
- "mews",
474
- "moose",
475
- "music",
476
- "mud",
477
- "manga",
478
- "news",
479
- "only",
480
- "personnel",
481
- "pike",
482
- "plankton",
483
- "pliers",
484
- "police",
485
- "pollution",
486
- "premises",
487
- "rain",
488
- "research",
489
- "rice",
490
- "salmon",
491
- "scissors",
492
- "series",
493
- "sewage",
494
- "shambles",
495
- "shrimp",
496
- "software",
497
- "staff",
498
- "swine",
499
- "tennis",
500
- "traffic",
501
- "transportation",
502
- "trout",
503
- "tuna",
504
- "wealth",
505
- "welfare",
506
- "whiting",
507
- "wildebeest",
508
- "wildlife",
509
- "you",
510
- /pok[eé]mon$/i,
511
- // Regexes.
512
- /[^aeiou]ese$/i, // "chinese", "japanese"
513
- /deer$/i, // "deer", "reindeer"
514
- /fish$/i, // "fish", "blowfish", "angelfish"
515
- /measles$/i,
516
- /o[iu]s$/i, // "carnivorous"
517
- /pox$/i, // "chickpox", "smallpox"
518
- /sheep$/i,
366
+ // Singular words with no plurals.
367
+ "adulthood",
368
+ "advice",
369
+ "agenda",
370
+ "aid",
371
+ "aircraft",
372
+ "alcohol",
373
+ "ammo",
374
+ "analytics",
375
+ "anime",
376
+ "athletics",
377
+ "audio",
378
+ "bison",
379
+ "blood",
380
+ "bream",
381
+ "buffalo",
382
+ "butter",
383
+ "carp",
384
+ "cash",
385
+ "chassis",
386
+ "chess",
387
+ "clothing",
388
+ "cod",
389
+ "commerce",
390
+ "cooperation",
391
+ "corps",
392
+ "debris",
393
+ "diabetes",
394
+ "digestion",
395
+ "elk",
396
+ "energy",
397
+ "equipment",
398
+ "excretion",
399
+ "expertise",
400
+ "firmware",
401
+ "flounder",
402
+ "fun",
403
+ "gallows",
404
+ "garbage",
405
+ "graffiti",
406
+ "hardware",
407
+ "headquarters",
408
+ "health",
409
+ "herpes",
410
+ "highjinks",
411
+ "homework",
412
+ "housework",
413
+ "information",
414
+ "jeans",
415
+ "justice",
416
+ "kudos",
417
+ "labour",
418
+ "literature",
419
+ "machinery",
420
+ "mackerel",
421
+ "mail",
422
+ "media",
423
+ "mews",
424
+ "moose",
425
+ "music",
426
+ "mud",
427
+ "manga",
428
+ "news",
429
+ "only",
430
+ "personnel",
431
+ "pike",
432
+ "plankton",
433
+ "pliers",
434
+ "police",
435
+ "pollution",
436
+ "premises",
437
+ "rain",
438
+ "research",
439
+ "rice",
440
+ "salmon",
441
+ "scissors",
442
+ "series",
443
+ "sewage",
444
+ "shambles",
445
+ "shrimp",
446
+ "software",
447
+ "staff",
448
+ "swine",
449
+ "tennis",
450
+ "traffic",
451
+ "transportation",
452
+ "trout",
453
+ "tuna",
454
+ "wealth",
455
+ "welfare",
456
+ "whiting",
457
+ "wildebeest",
458
+ "wildlife",
459
+ "you",
460
+ /pok[eé]mon$/i,
461
+ // Regexes.
462
+ /[^aeiou]ese$/i, // "chinese", "japanese"
463
+ /deer$/i, // "deer", "reindeer"
464
+ /fish$/i, // "fish", "blowfish", "angelfish"
465
+ /measles$/i,
466
+ /o[iu]s$/i, // "carnivorous"
467
+ /pox$/i, // "chickpox", "smallpox"
468
+ /sheep$/i,
519
469
  ].forEach(pluralize.addUncountableRule);