eslint-plugin-perfectionist 4.1.2 → 4.3.0

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.
Files changed (44) hide show
  1. package/dist/alphabet.d.ts +176 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +9 -1
  4. package/dist/rules/sort-array-includes.js +6 -1
  5. package/dist/rules/sort-classes-utils.js +0 -23
  6. package/dist/rules/sort-classes.js +19 -43
  7. package/dist/rules/sort-classes.types.js +5 -37
  8. package/dist/rules/sort-decorators.js +9 -1
  9. package/dist/rules/sort-enums.js +11 -1
  10. package/dist/rules/sort-exports.js +8 -1
  11. package/dist/rules/sort-heritage-clauses.js +4 -0
  12. package/dist/rules/sort-imports.js +8 -3
  13. package/dist/rules/sort-interfaces.js +17 -260
  14. package/dist/rules/sort-intersection-types.js +9 -1
  15. package/dist/rules/sort-jsx-props.js +4 -0
  16. package/dist/rules/sort-maps.js +8 -1
  17. package/dist/rules/sort-modules-utils.js +0 -23
  18. package/dist/rules/sort-modules.js +20 -44
  19. package/dist/rules/sort-modules.types.js +5 -41
  20. package/dist/rules/sort-named-exports.js +8 -1
  21. package/dist/rules/sort-named-imports.js +8 -1
  22. package/dist/rules/sort-object-types-utils.js +37 -0
  23. package/dist/rules/sort-object-types.js +345 -223
  24. package/dist/rules/sort-object-types.types.js +20 -0
  25. package/dist/rules/sort-objects.js +36 -10
  26. package/dist/rules/sort-switch-case.js +4 -0
  27. package/dist/rules/sort-union-types.js +9 -1
  28. package/dist/rules/sort-variable-declarations.js +8 -1
  29. package/dist/utils/alphabet.js +385 -0
  30. package/dist/utils/common-json-schemas.js +94 -1
  31. package/dist/utils/compare.js +88 -39
  32. package/dist/utils/convert-boolean-to-sign.js +4 -0
  33. package/dist/utils/get-comments-before.js +4 -4
  34. package/dist/utils/get-custom-groups-compare-options.js +29 -0
  35. package/dist/utils/get-matching-context-options.js +18 -0
  36. package/dist/utils/get-node-range.js +18 -7
  37. package/dist/utils/get-settings.js +1 -0
  38. package/dist/utils/is-node-function-type.js +15 -0
  39. package/dist/utils/make-fixes.js +24 -3
  40. package/dist/utils/make-newlines-fixes.js +7 -4
  41. package/dist/utils/validate-custom-sort-configuration.js +11 -0
  42. package/dist/{rules → utils}/validate-generated-groups-configuration.js +4 -2
  43. package/package.json +9 -5
  44. package/readme.md +4 -3
@@ -2,7 +2,9 @@
2
2
  const commonJsonSchemas = require('../utils/common-json-schemas.js')
3
3
  const sortNodesByDependencies = require('../utils/sort-nodes-by-dependencies.js')
4
4
  const validateNewlinesAndPartitionConfiguration = require('../utils/validate-newlines-and-partition-configuration.js')
5
+ const validateCustomSortConfiguration = require('../utils/validate-custom-sort-configuration.js')
5
6
  const validateGroupsConfiguration = require('../utils/validate-groups-configuration.js')
7
+ const getMatchingContextOptions = require('../utils/get-matching-context-options.js')
6
8
  const getEslintDisabledLines = require('../utils/get-eslint-disabled-lines.js')
7
9
  const isNodeEslintDisabled = require('../utils/is-node-eslint-disabled.js')
8
10
  const isPartitionComment = require('../utils/is-partition-comment.js')
@@ -33,11 +35,13 @@ let defaultOptions = {
33
35
  objectDeclarations: true,
34
36
  styledComponents: true,
35
37
  destructureOnly: false,
38
+ useConfigurationIf: {},
36
39
  type: 'alphabetical',
37
40
  ignorePattern: [],
38
41
  ignoreCase: true,
39
42
  customGroups: {},
40
43
  locales: 'en-US',
44
+ alphabet: '',
41
45
  order: 'asc',
42
46
  groups: [],
43
47
  }
@@ -48,11 +52,20 @@ const sortObjects = createEslintRule.createEslintRule({
48
52
  return
49
53
  }
50
54
  let settings = getSettings.getSettings(context.settings)
55
+ let sourceCode = getSourceCode.getSourceCode(context)
56
+ let matchedContextOptions =
57
+ getMatchingContextOptions.getMatchingContextOptions({
58
+ nodeNames: nodeObject.properties
59
+ .map(property => getNodeName({ sourceCode, property }))
60
+ .filter(nodeName => nodeName !== null),
61
+ contextOptions: context.options,
62
+ })
51
63
  let options = complete.complete(
52
- context.options.at(0),
64
+ matchedContextOptions,
53
65
  settings,
54
66
  defaultOptions,
55
67
  )
68
+ validateCustomSortConfiguration.validateCustomSortConfiguration(options)
56
69
  validateGroupsConfiguration.validateGroupsConfiguration(
57
70
  options.groups,
58
71
  ['multiline', 'method', 'unknown'],
@@ -130,7 +143,6 @@ const sortObjects = createEslintRule.createEslintRule({
130
143
  ) {
131
144
  return
132
145
  }
133
- let sourceCode = getSourceCode.getSourceCode(context)
134
146
  let eslintDisabledLines = getEslintDisabledLines.getEslintDisabledLines({
135
147
  ruleName: context.id,
136
148
  sourceCode,
@@ -218,10 +230,6 @@ const sortObjects = createEslintRule.createEslintRule({
218
230
  accumulator.push([])
219
231
  return accumulator
220
232
  }
221
- let comments = getCommentsBefore.getCommentsBefore(
222
- property,
223
- sourceCode,
224
- )
225
233
  let lastProperty =
226
234
  (_a = accumulator.at(-1)) == null ? void 0 : _a.at(-1)
227
235
  let name
@@ -270,7 +278,10 @@ const sortObjects = createEslintRule.createEslintRule({
270
278
  (options.partitionByComment &&
271
279
  isPartitionComment.hasPartitionComment(
272
280
  options.partitionByComment,
273
- comments,
281
+ getCommentsBefore.getCommentsBefore({
282
+ node: property,
283
+ sourceCode,
284
+ }),
274
285
  ))
275
286
  ) {
276
287
  accumulator.push([])
@@ -383,8 +394,8 @@ const sortObjects = createEslintRule.createEslintRule({
383
394
  }
384
395
  },
385
396
  meta: {
386
- schema: [
387
- {
397
+ schema: {
398
+ items: {
388
399
  properties: {
389
400
  destructuredObjects: {
390
401
  oneOf: [
@@ -431,10 +442,12 @@ const sortObjects = createEslintRule.createEslintRule({
431
442
  type: 'boolean',
432
443
  },
433
444
  partitionByNewLine: commonJsonSchemas.partitionByNewLineJsonSchema,
445
+ useConfigurationIf: commonJsonSchemas.useConfigurationIfJsonSchema,
434
446
  specialCharacters: commonJsonSchemas.specialCharactersJsonSchema,
435
447
  newlinesBetween: commonJsonSchemas.newlinesBetweenJsonSchema,
436
448
  customGroups: commonJsonSchemas.customGroupsJsonSchema,
437
449
  ignoreCase: commonJsonSchemas.ignoreCaseJsonSchema,
450
+ alphabet: commonJsonSchemas.alphabetJsonSchema,
438
451
  locales: commonJsonSchemas.localesJsonSchema,
439
452
  groups: commonJsonSchemas.groupsJsonSchema,
440
453
  order: commonJsonSchemas.orderJsonSchema,
@@ -443,7 +456,9 @@ const sortObjects = createEslintRule.createEslintRule({
443
456
  additionalProperties: false,
444
457
  type: 'object',
445
458
  },
446
- ],
459
+ uniqueItems: true,
460
+ type: 'array',
461
+ },
447
462
  messages: {
448
463
  unexpectedObjectsGroupOrder:
449
464
  'Expected "{{right}}" ({{rightGroup}}) to come before "{{left}}" ({{leftGroup}}).',
@@ -466,4 +481,15 @@ const sortObjects = createEslintRule.createEslintRule({
466
481
  defaultOptions: [defaultOptions],
467
482
  name: 'sort-objects',
468
483
  })
484
+ let getNodeName = ({ sourceCode, property }) => {
485
+ if (property.type === 'SpreadElement' || property.type === 'RestElement') {
486
+ return null
487
+ }
488
+ if (property.key.type === 'Identifier') {
489
+ return property.key.name
490
+ } else if (property.key.type === 'Literal') {
491
+ return `${property.key.value}`
492
+ }
493
+ return sourceCode.getText(property.key)
494
+ }
469
495
  module.exports = sortObjects
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
  const commonJsonSchemas = require('../utils/common-json-schemas.js')
3
+ const validateCustomSortConfiguration = require('../utils/validate-custom-sort-configuration.js')
3
4
  const makeCommentAfterFixes = require('../utils/make-comment-after-fixes.js')
4
5
  const createEslintRule = require('../utils/create-eslint-rule.js')
5
6
  const getSourceCode = require('../utils/get-source-code.js')
@@ -16,6 +17,7 @@ let defaultOptions = {
16
17
  type: 'alphabetical',
17
18
  ignoreCase: true,
18
19
  locales: 'en-US',
20
+ alphabet: '',
19
21
  order: 'asc',
20
22
  }
21
23
  const sortSwitchCase = createEslintRule.createEslintRule({
@@ -30,6 +32,7 @@ const sortSwitchCase = createEslintRule.createEslintRule({
30
32
  settings,
31
33
  defaultOptions,
32
34
  )
35
+ validateCustomSortConfiguration.validateCustomSortConfiguration(options)
33
36
  let sourceCode = getSourceCode.getSourceCode(context)
34
37
  let isDiscriminantTrue =
35
38
  switchNode.discriminant.type === 'Literal' &&
@@ -218,6 +221,7 @@ const sortSwitchCase = createEslintRule.createEslintRule({
218
221
  properties: {
219
222
  specialCharacters: commonJsonSchemas.specialCharactersJsonSchema,
220
223
  ignoreCase: commonJsonSchemas.ignoreCaseJsonSchema,
224
+ alphabet: commonJsonSchemas.alphabetJsonSchema,
221
225
  locales: commonJsonSchemas.localesJsonSchema,
222
226
  order: commonJsonSchemas.orderJsonSchema,
223
227
  type: commonJsonSchemas.typeJsonSchema,
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
  const commonJsonSchemas = require('../utils/common-json-schemas.js')
3
3
  const validateNewlinesAndPartitionConfiguration = require('../utils/validate-newlines-and-partition-configuration.js')
4
+ const validateCustomSortConfiguration = require('../utils/validate-custom-sort-configuration.js')
4
5
  const validateGroupsConfiguration = require('../utils/validate-groups-configuration.js')
5
6
  const getEslintDisabledLines = require('../utils/get-eslint-disabled-lines.js')
6
7
  const isNodeEslintDisabled = require('../utils/is-node-eslint-disabled.js')
@@ -28,6 +29,7 @@ let defaultOptions = {
28
29
  type: 'alphabetical',
29
30
  ignoreCase: true,
30
31
  locales: 'en-US',
32
+ alphabet: '',
31
33
  order: 'asc',
32
34
  groups: [],
33
35
  }
@@ -40,6 +42,7 @@ const sortUnionTypes = createEslintRule.createEslintRule({
40
42
  settings,
41
43
  defaultOptions,
42
44
  )
45
+ validateCustomSortConfiguration.validateCustomSortConfiguration(options)
43
46
  validateGroupsConfiguration.validateGroupsConfiguration(
44
47
  options.groups,
45
48
  [
@@ -144,7 +147,11 @@ const sortUnionTypes = createEslintRule.createEslintRule({
144
147
  (options.partitionByComment &&
145
148
  isPartitionComment.hasPartitionComment(
146
149
  options.partitionByComment,
147
- getCommentsBefore.getCommentsBefore(type, sourceCode, '|'),
150
+ getCommentsBefore.getCommentsBefore({
151
+ tokenValueToIgnoreBefore: '|',
152
+ node: type,
153
+ sourceCode,
154
+ }),
148
155
  )) ||
149
156
  (options.partitionByNewLine &&
150
157
  lastSortingNode &&
@@ -245,6 +252,7 @@ const sortUnionTypes = createEslintRule.createEslintRule({
245
252
  specialCharacters: commonJsonSchemas.specialCharactersJsonSchema,
246
253
  newlinesBetween: commonJsonSchemas.newlinesBetweenJsonSchema,
247
254
  ignoreCase: commonJsonSchemas.ignoreCaseJsonSchema,
255
+ alphabet: commonJsonSchemas.alphabetJsonSchema,
248
256
  locales: commonJsonSchemas.localesJsonSchema,
249
257
  groups: commonJsonSchemas.groupsJsonSchema,
250
258
  order: commonJsonSchemas.orderJsonSchema,
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
  const commonJsonSchemas = require('../utils/common-json-schemas.js')
3
3
  const sortNodesByDependencies = require('../utils/sort-nodes-by-dependencies.js')
4
+ const validateCustomSortConfiguration = require('../utils/validate-custom-sort-configuration.js')
4
5
  const getEslintDisabledLines = require('../utils/get-eslint-disabled-lines.js')
5
6
  const isNodeEslintDisabled = require('../utils/is-node-eslint-disabled.js')
6
7
  const isPartitionComment = require('../utils/is-partition-comment.js')
@@ -23,6 +24,7 @@ let defaultOptions = {
23
24
  type: 'alphabetical',
24
25
  ignoreCase: true,
25
26
  locales: 'en-US',
27
+ alphabet: '',
26
28
  order: 'asc',
27
29
  }
28
30
  const sortVariableDeclarations = createEslintRule.createEslintRule({
@@ -37,6 +39,7 @@ const sortVariableDeclarations = createEslintRule.createEslintRule({
37
39
  settings,
38
40
  defaultOptions,
39
41
  )
42
+ validateCustomSortConfiguration.validateCustomSortConfiguration(options)
40
43
  let sourceCode = getSourceCode.getSourceCode(context)
41
44
  let eslintDisabledLines = getEslintDisabledLines.getEslintDisabledLines({
42
45
  ruleName: context.id,
@@ -146,7 +149,10 @@ const sortVariableDeclarations = createEslintRule.createEslintRule({
146
149
  (options.partitionByComment &&
147
150
  isPartitionComment.hasPartitionComment(
148
151
  options.partitionByComment,
149
- getCommentsBefore.getCommentsBefore(declaration, sourceCode),
152
+ getCommentsBefore.getCommentsBefore({
153
+ node: declaration,
154
+ sourceCode,
155
+ }),
150
156
  )) ||
151
157
  (options.partitionByNewLine &&
152
158
  lastSortingNode &&
@@ -228,6 +234,7 @@ const sortVariableDeclarations = createEslintRule.createEslintRule({
228
234
  partitionByNewLine: commonJsonSchemas.partitionByNewLineJsonSchema,
229
235
  specialCharacters: commonJsonSchemas.specialCharactersJsonSchema,
230
236
  ignoreCase: commonJsonSchemas.ignoreCaseJsonSchema,
237
+ alphabet: commonJsonSchemas.alphabetJsonSchema,
231
238
  locales: commonJsonSchemas.localesJsonSchema,
232
239
  order: commonJsonSchemas.orderJsonSchema,
233
240
  type: commonJsonSchemas.typeJsonSchema,
@@ -0,0 +1,385 @@
1
+ 'use strict'
2
+ var __defProp = Object.defineProperty
3
+ var __defNormalProp = (obj, key, value) =>
4
+ key in obj
5
+ ? __defProp(obj, key, {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value,
10
+ })
11
+ : (obj[key] = value)
12
+ var __publicField = (obj, key, value) =>
13
+ __defNormalProp(obj, typeof key !== 'symbol' ? key + '' : key, value)
14
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
15
+ const naturalOrderby = require('natural-orderby')
16
+ const convertBooleanToSign = require('./convert-boolean-to-sign.js')
17
+ class Alphabet {
18
+ constructor(characters) {
19
+ __publicField(this, '_characters', [])
20
+ this._characters = characters
21
+ }
22
+ /**
23
+ * Generates an alphabet from the given characters.
24
+ * @param {string|string[]} values - The characters to generate the alphabet from
25
+ * @returns {Alphabet} - The wrapped alphabet
26
+ */
27
+ static generateFrom(values) {
28
+ let arrayValues = typeof values === 'string' ? [...values] : values
29
+ if (arrayValues.length !== new Set(arrayValues).size) {
30
+ throw new Error('The alphabet must not contain repeated characters')
31
+ }
32
+ if (arrayValues.some(value => value.length !== 1)) {
33
+ throw new Error('The alphabet must contain single characters')
34
+ }
35
+ return new Alphabet(
36
+ arrayValues.map(value =>
37
+ Alphabet._convertCodepointToCharacter(value.codePointAt(0)),
38
+ ),
39
+ )
40
+ }
41
+ /**
42
+ * Generates an alphabet containing relevant characters from the Unicode
43
+ * standard. Contains the Unicode planes 0 and 1.
44
+ * @returns {Alphabet} - The generated alphabet
45
+ */
46
+ static generateRecommendedAlphabet() {
47
+ return Alphabet._generateAlphabetToRange(131071 + 1)
48
+ }
49
+ /**
50
+ * Generates an alphabet containing all characters from the Unicode standard
51
+ * except for irrelevant Unicode planes.
52
+ * Contains the Unicode planes 0, 1, 2 and 3.
53
+ * @returns {Alphabet} - The generated alphabet
54
+ */
55
+ static generateCompleteAlphabet() {
56
+ return Alphabet._generateAlphabetToRange(262143 + 1)
57
+ }
58
+ static _convertCodepointToCharacter(codePoint) {
59
+ let character = String.fromCodePoint(codePoint)
60
+ let lowercaseCharacter = character.toLowerCase()
61
+ let uppercaseCharacter = character.toUpperCase()
62
+ return {
63
+ value: character,
64
+ codePoint,
65
+ ...(lowercaseCharacter === character
66
+ ? null
67
+ : {
68
+ lowercaseCharacterCodePoint: lowercaseCharacter.codePointAt(0),
69
+ }),
70
+ ...(uppercaseCharacter === character
71
+ ? null
72
+ : {
73
+ uppercaseCharacterCodePoint: uppercaseCharacter.codePointAt(0),
74
+ }),
75
+ }
76
+ }
77
+ /**
78
+ * Generates an alphabet containing relevant characters from the Unicode
79
+ * standard.
80
+ * @param {number} maxCodePoint - The maximum code point to generate the
81
+ * alphabet to
82
+ * @returns {Alphabet} - The generated alphabet
83
+ */
84
+ static _generateAlphabetToRange(maxCodePoint) {
85
+ let totalCharacters = Array.from({ length: maxCodePoint }, (_, i) =>
86
+ Alphabet._convertCodepointToCharacter(i),
87
+ )
88
+ return new Alphabet(totalCharacters)
89
+ }
90
+ /**
91
+ * For each character with a lower and upper case, permutes the two cases
92
+ * so that the alphabet is ordered by the case priority entered.
93
+ * @param {string} casePriority - The case to prioritize
94
+ * @returns {Alphabet} - The same alphabet instance with the cases prioritized
95
+ * @example
96
+ * Alphabet.generateFrom('aAbBcdCD')
97
+ * .prioritizeCase('uppercase')
98
+ * // Returns 'AaBbCDcd'
99
+ */
100
+ prioritizeCase(casePriority) {
101
+ let charactersWithCase = this._getCharactersWithCase()
102
+ let parsedIndexes = /* @__PURE__ */ new Set()
103
+ let indexByCodePoints = this._characters.reduce(
104
+ (indexByCodePoint, character, index) => {
105
+ indexByCodePoint[character.codePoint] = index
106
+ return indexByCodePoint
107
+ },
108
+ {},
109
+ )
110
+ for (let { character, index } of charactersWithCase) {
111
+ if (parsedIndexes.has(index)) {
112
+ continue
113
+ }
114
+ parsedIndexes.add(index)
115
+ let otherCharacterIndex =
116
+ indexByCodePoints[
117
+ character.uppercaseCharacterCodePoint ??
118
+ character.lowercaseCharacterCodePoint
119
+ ]
120
+ if (otherCharacterIndex === void 0) {
121
+ continue
122
+ }
123
+ parsedIndexes.add(otherCharacterIndex)
124
+ let isCharacterUppercase = !character.uppercaseCharacterCodePoint
125
+ if (isCharacterUppercase) {
126
+ if (
127
+ (casePriority === 'uppercase' && index < otherCharacterIndex) ||
128
+ (casePriority === 'lowercase' && index > otherCharacterIndex)
129
+ ) {
130
+ continue
131
+ }
132
+ } else {
133
+ if (
134
+ (casePriority === 'uppercase' && index > otherCharacterIndex) ||
135
+ (casePriority === 'lowercase' && index < otherCharacterIndex)
136
+ ) {
137
+ continue
138
+ }
139
+ }
140
+ this._characters[index] = this._characters[otherCharacterIndex]
141
+ this._characters[otherCharacterIndex] = character
142
+ }
143
+ return this
144
+ }
145
+ /**
146
+ * Adds specific characters to the end of the alphabet.
147
+ * @param {string|string[]} values - The characters to push to the alphabet
148
+ * @returns {Alphabet} - The same alphabet instance without the specified
149
+ * characters
150
+ * @example
151
+ * Alphabet.generateFrom('ab')
152
+ * .pushCharacters('cd')
153
+ * // Returns 'abcd'
154
+ */
155
+ pushCharacters(values) {
156
+ let arrayValues = typeof values === 'string' ? [...values] : values
157
+ let valuesSet = new Set(arrayValues)
158
+ let valuesAlreadyExisting = this._characters.filter(({ value }) =>
159
+ valuesSet.has(value),
160
+ )
161
+ if (valuesAlreadyExisting.length > 0) {
162
+ throw new Error(
163
+ `The alphabet already contains the characters ${valuesAlreadyExisting
164
+ .slice(0, 5)
165
+ .map(({ value }) => value)
166
+ .join(', ')}`,
167
+ )
168
+ }
169
+ if (arrayValues.some(value => value.length !== 1)) {
170
+ throw new Error('Only single characters may be pushed')
171
+ }
172
+ this._characters.push(
173
+ ...[...valuesSet].map(value =>
174
+ Alphabet._convertCodepointToCharacter(value.codePointAt(0)),
175
+ ),
176
+ )
177
+ return this
178
+ }
179
+ /**
180
+ * Permutes characters with cases so that all characters with the entered case
181
+ * are put before the other characters.
182
+ * @param {string} caseToComeFirst - The case to put before the other
183
+ * characters
184
+ * @returns {Alphabet} - The same alphabet instance with all characters with
185
+ * case before all the characters with the other case
186
+ */
187
+ placeAllWithCaseBeforeAllWithOtherCase(caseToComeFirst) {
188
+ let charactersWithCase = this._getCharactersWithCase()
189
+ let orderedCharacters = [
190
+ ...charactersWithCase.filter(character =>
191
+ caseToComeFirst === 'uppercase'
192
+ ? !character.character.uppercaseCharacterCodePoint
193
+ : character.character.uppercaseCharacterCodePoint,
194
+ ),
195
+ ...charactersWithCase.filter(character =>
196
+ caseToComeFirst === 'uppercase'
197
+ ? character.character.uppercaseCharacterCodePoint
198
+ : !character.character.uppercaseCharacterCodePoint,
199
+ ),
200
+ ]
201
+ for (let [i, element] of charactersWithCase.entries()) {
202
+ this._characters[element.index] = orderedCharacters[i].character
203
+ }
204
+ return this
205
+ }
206
+ /**
207
+ * Places a specific character right before another character in the alphabet.
208
+ * @param {object} params - The parameters for the operation
209
+ * @param {string} params.characterBefore - The character to come before
210
+ * characterAfter
211
+ * @param {string} params.characterAfter - The target character
212
+ * @returns {Alphabet} - The same alphabet instance with the specific
213
+ * character prioritized
214
+ * @example
215
+ * Alphabet.generateFrom('ab-cd/')
216
+ * .placeCharacterBefore({ characterBefore: '/', characterAfter: '-' })
217
+ * // Returns 'ab/-cd'
218
+ */
219
+ placeCharacterBefore({ characterBefore, characterAfter }) {
220
+ return this._placeCharacterBeforeOrAfter({
221
+ characterBefore,
222
+ characterAfter,
223
+ type: 'before',
224
+ })
225
+ }
226
+ /**
227
+ * Places a specific character right after another character in the alphabet.
228
+ * @param {object} params - The parameters for the operation
229
+ * @param {string} params.characterBefore - The target character
230
+ * @param {string} params.characterAfter - The character to come after
231
+ * characterBefore
232
+ * @returns {Alphabet} - The same alphabet instance with the specific
233
+ * character prioritized
234
+ * @example
235
+ * Alphabet.generateFrom('ab-cd/')
236
+ * .placeCharacterAfter({ characterBefore: '/', characterAfter: '-' })
237
+ * // Returns 'abcd/-'
238
+ */
239
+ placeCharacterAfter({ characterBefore, characterAfter }) {
240
+ return this._placeCharacterBeforeOrAfter({
241
+ characterBefore,
242
+ characterAfter,
243
+ type: 'after',
244
+ })
245
+ }
246
+ /**
247
+ * Removes specific characters from the alphabet by their range.
248
+ * @param {object} range - The Unicode range to remove characters from
249
+ * @param {number} range.start - The starting Unicode codepoint
250
+ * @param {number} range.end - The ending Unicode codepoint
251
+ * @returns {Alphabet} - The same alphabet instance without the characters
252
+ * from the specified range
253
+ */
254
+ removeUnicodeRange({ start, end }) {
255
+ this._characters = this._characters.filter(
256
+ ({ codePoint }) => codePoint < start || codePoint > end,
257
+ )
258
+ return this
259
+ }
260
+ /**
261
+ * Sorts the alphabet by the sorting function provided.
262
+ * @param {Function} sortingFunction - The sorting function to use
263
+ * @returns {Alphabet} - The same alphabet instance sorted by the sorting function provided
264
+ */
265
+ sortBy(sortingFunction) {
266
+ this._characters.sort((a, b) => sortingFunction(a.value, b.value))
267
+ return this
268
+ }
269
+ /**
270
+ * Removes specific characters from the alphabet.
271
+ * @param {string|string[]} values - The characters to remove from the
272
+ * alphabet
273
+ * @returns {Alphabet} - The same alphabet instance without the specified
274
+ * characters
275
+ * @example
276
+ * Alphabet.generateFrom('abcd')
277
+ * .removeCharacters('dcc')
278
+ * // Returns 'ab'
279
+ */
280
+ removeCharacters(values) {
281
+ this._characters = this._characters.filter(
282
+ ({ value }) => !values.includes(value),
283
+ )
284
+ return this
285
+ }
286
+ /**
287
+ * Sorts the alphabet by the natural order of the characters using
288
+ * `natural-orderby`.
289
+ * @param {string} locale - The locale to use for sorting
290
+ * @returns {Alphabet} - The same alphabet instance sorted by the natural
291
+ * order of the characters
292
+ */
293
+ sortByNaturalSort(locale) {
294
+ let naturalCompare = naturalOrderby.compare({
295
+ locale,
296
+ })
297
+ return this.sortBy((a, b) => naturalCompare(a, b))
298
+ }
299
+ /**
300
+ * Sorts the alphabet by the character code point.
301
+ * @returns {Alphabet} - The same alphabet instance sorted by the character
302
+ * code point
303
+ */
304
+ sortByCharCodeAt() {
305
+ return this.sortBy((a, b) =>
306
+ convertBooleanToSign.convertBooleanToSign(
307
+ a.charCodeAt(0) > b.charCodeAt(0),
308
+ ),
309
+ )
310
+ }
311
+ /**
312
+ * Sorts the alphabet by the locale order of the characters.
313
+ * @param {Intl.LocalesArgument} locales - The locales to use for sorting
314
+ * @returns {Alphabet} - The same alphabet instance sorted by the locale
315
+ * order of the characters
316
+ */
317
+ sortByLocaleCompare(locales) {
318
+ return this.sortBy((a, b) => a.localeCompare(b, locales))
319
+ }
320
+ /**
321
+ * Retrieves the characters from the alphabet.
322
+ * @returns {string} The characters from the alphabet
323
+ */
324
+ getCharacters() {
325
+ return this._characters.map(({ value }) => value).join('')
326
+ }
327
+ /**
328
+ * Reverses the alphabet.
329
+ * @returns {Alphabet} - The same alphabet instance reversed
330
+ * @example
331
+ * Alphabet.generateFrom('ab')
332
+ * .reverse()
333
+ * // Returns 'ba'
334
+ */
335
+ reverse() {
336
+ this._characters.reverse()
337
+ return this
338
+ }
339
+ _placeCharacterBeforeOrAfter({ characterBefore, characterAfter, type }) {
340
+ let indexOfCharacterAfter = this._characters.findIndex(
341
+ ({ value }) => value === characterAfter,
342
+ )
343
+ let indexOfCharacterBefore = this._characters.findIndex(
344
+ ({ value }) => value === characterBefore,
345
+ )
346
+ if (indexOfCharacterAfter === -1) {
347
+ throw new Error(`Character ${characterAfter} not found in alphabet`)
348
+ }
349
+ if (indexOfCharacterBefore === -1) {
350
+ throw new Error(`Character ${characterBefore} not found in alphabet`)
351
+ }
352
+ if (indexOfCharacterBefore <= indexOfCharacterAfter) {
353
+ return this
354
+ }
355
+ this._characters.splice(
356
+ type === 'before' ? indexOfCharacterAfter : indexOfCharacterBefore + 1,
357
+ 0,
358
+ this._characters[
359
+ type === 'before' ? indexOfCharacterBefore : indexOfCharacterAfter
360
+ ],
361
+ )
362
+ this._characters.splice(
363
+ type === 'before' ? indexOfCharacterBefore + 1 : indexOfCharacterAfter,
364
+ 1,
365
+ )
366
+ return this
367
+ }
368
+ _getCharactersWithCase() {
369
+ return this._characters
370
+ .map((character, index) => {
371
+ if (
372
+ !character.uppercaseCharacterCodePoint &&
373
+ !character.lowercaseCharacterCodePoint
374
+ ) {
375
+ return null
376
+ }
377
+ return {
378
+ character,
379
+ index,
380
+ }
381
+ })
382
+ .filter(element => element !== null)
383
+ }
384
+ }
385
+ exports.Alphabet = Alphabet