@cjser/camelcase 9.0.0-cjser.2 → 10.0.0-cjser.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.
@@ -16,36 +16,40 @@ var __copyProps = (to, from, except, desc) => {
16
16
  };
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
 
19
- // packages/@cjser/camelcase/index.js
19
+ // packages/@cjser/camelcase.tmp-26-1791822176987/index.js
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  default: () => camelCase
23
23
  });
24
24
  module.exports = __toCommonJS(index_exports);
25
- var UPPERCASE = /[\p{Lu}]/u;
26
- var LOWERCASE = /[\p{Ll}]/u;
27
- var LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/u;
28
- var SEPARATORS = /[_.\- ]+/;
29
- var IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
30
- var LEADING_SEPARATORS = new RegExp("^" + SEPARATORS.source);
31
- var SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, "gu");
32
- var NUMBERS_AND_IDENTIFIER = new RegExp(String.raw`\d+` + IDENTIFIER.source, "gu");
25
+ var UPPERCASE = new RegExp("\\p{Uppercase_Letter}", "v");
26
+ var LOWERCASE = new RegExp("\\p{Lowercase_Letter}", "v");
27
+ var ALPHABETIC = new RegExp("\\p{Alphabetic}", "v");
28
+ var DIGIT = new RegExp("\\p{Decimal_Number}", "v");
29
+ var LEADING_CAPITAL = new RegExp("^\\p{Uppercase_Letter}(?!\\p{Uppercase_Letter})", "v");
30
+ var SEPARATORS = new RegExp("[ \\-._]+", "v");
31
+ var LEADING_PREFIX = new RegExp("^[$_]*", "v");
32
+ var IDENTIFIER = new RegExp("(?<identifier>[\\p{Alphabetic}\\p{Number}_]|$)", "v");
33
+ var LEADING_SEPARATORS = new RegExp("^" + SEPARATORS.source, "v");
34
+ var SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, "gv");
35
+ var NUMBERS_AND_IDENTIFIER = new RegExp(String.raw`\p{Decimal_Number}+` + IDENTIFIER.source, "gv");
33
36
  var preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUppercase2) => {
37
+ const characters = [...string];
34
38
  let isLastCharLower = false;
35
39
  let isLastCharUpper = false;
36
40
  let isLastLastCharUpper = false;
37
41
  let isLastLastCharPreserved = false;
38
- for (let index = 0; index < string.length; index++) {
39
- const character = string[index];
40
- isLastLastCharPreserved = index > 2 ? string[index - 3] === "-" : true;
42
+ for (let index = 0; index < characters.length; index++) {
43
+ const character = characters[index];
44
+ isLastLastCharPreserved = index > 2 ? characters[index - 3] === "-" : true;
41
45
  if (isLastCharLower && UPPERCASE.test(character)) {
42
- string = string.slice(0, index) + "-" + string.slice(index);
46
+ characters.splice(index, 0, "-");
43
47
  isLastCharLower = false;
44
48
  isLastLastCharUpper = isLastCharUpper;
45
49
  isLastCharUpper = true;
46
50
  index++;
47
51
  } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character) && (!isLastLastCharPreserved || preserveConsecutiveUppercase2)) {
48
- string = string.slice(0, index - 1) + "-" + string.slice(index - 1);
52
+ characters.splice(index - 1, 0, "-");
49
53
  isLastLastCharUpper = isLastCharUpper;
50
54
  isLastCharUpper = false;
51
55
  isLastCharLower = true;
@@ -55,49 +59,44 @@ var preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUp
55
59
  isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
56
60
  }
57
61
  }
58
- return string;
62
+ return characters.join("");
59
63
  };
60
64
  var preserveConsecutiveUppercase = (input, toLowerCase) => input.replace(LEADING_CAPITAL, (match) => toLowerCase(match));
61
- var processWithCasePreservation = (input, toLowerCase, preserveConsecutiveUppercase2) => {
65
+ var processWithCasePreservation = (input, toLowerCase, shouldPreserveConsecutiveUppercase) => {
62
66
  let result = "";
63
- let previousWasNumber = false;
64
- let previousWasUppercase = false;
67
+ let wasNumberBefore = false;
65
68
  const characters = [...input];
66
69
  for (let index = 0; index < characters.length; index++) {
67
70
  const character = characters[index];
68
71
  const isUpperCase = UPPERCASE.test(character);
69
- const nextCharIsUpperCase = index + 1 < characters.length && UPPERCASE.test(characters[index + 1]);
70
- if (previousWasNumber && /[\p{Alpha}]/u.test(character)) {
72
+ const isLoneLeadingCapital = index === 0 && isUpperCase && !(index + 1 < characters.length && UPPERCASE.test(characters[index + 1]));
73
+ if (wasNumberBefore && ALPHABETIC.test(character)) {
71
74
  result += character;
72
- previousWasNumber = false;
73
- previousWasUppercase = isUpperCase;
74
- } else if (preserveConsecutiveUppercase2 && isUpperCase && (previousWasUppercase || nextCharIsUpperCase)) {
75
+ wasNumberBefore = false;
76
+ } else if (shouldPreserveConsecutiveUppercase && isUpperCase && !isLoneLeadingCapital) {
75
77
  result += character;
76
- previousWasUppercase = true;
77
- } else if (/\d/.test(character)) {
78
+ } else if (DIGIT.test(character)) {
78
79
  result += character;
79
- previousWasNumber = true;
80
- previousWasUppercase = false;
80
+ wasNumberBefore = true;
81
81
  } else if (SEPARATORS.test(character)) {
82
82
  result += character;
83
- previousWasUppercase = false;
83
+ wasNumberBefore = false;
84
84
  } else {
85
85
  result += toLowerCase(character);
86
- previousWasNumber = false;
87
- previousWasUppercase = false;
86
+ wasNumberBefore = false;
88
87
  }
89
88
  }
90
89
  return result;
91
90
  };
92
91
  var postProcess = (input, toUpperCase, { capitalizeAfterNumber }) => {
93
- const transformNumericIdentifier = capitalizeAfterNumber ? (match, identifier, offset, string) => {
92
+ const withNumbers = capitalizeAfterNumber ? input.replaceAll(NUMBERS_AND_IDENTIFIER, (match, identifier, offset, string) => {
94
93
  const nextCharacter = string.charAt(offset + match.length);
95
94
  if (SEPARATORS.test(nextCharacter)) {
96
95
  return match;
97
96
  }
98
97
  return identifier ? match.slice(0, -identifier.length) + toUpperCase(identifier) : match;
99
- } : (match) => match;
100
- return input.replaceAll(NUMBERS_AND_IDENTIFIER, transformNumericIdentifier).replaceAll(
98
+ }) : input;
99
+ return withNumbers.replaceAll(
101
100
  SEPARATORS_AND_IDENTIFIER,
102
101
  (_, identifier) => toUpperCase(identifier)
103
102
  );
@@ -120,7 +119,7 @@ function camelCase(input, options) {
120
119
  if (input.length === 0) {
121
120
  return "";
122
121
  }
123
- const leadingPrefix = input.match(/^[_$]*/)[0];
122
+ const leadingPrefix = input.match(LEADING_PREFIX)[0];
124
123
  input = input.slice(leadingPrefix.length);
125
124
  if (input.length === 0) {
126
125
  return leadingPrefix;
@@ -149,7 +148,8 @@ function camelCase(input, options) {
149
148
  input = processWithCasePreservation(input, toLowerCase, options.preserveConsecutiveUppercase);
150
149
  }
151
150
  if (options.pascalCase && input.length > 0) {
152
- input = toUpperCase(input[0]) + input.slice(1);
151
+ const firstCharacter = String.fromCodePoint(input.codePointAt(0));
152
+ input = toUpperCase(firstCharacter) + input.slice(firstCharacter.length);
153
153
  }
154
154
  return leadingPrefix + postProcess(input, toUpperCase, options);
155
155
  }
package/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export type Options = {
18
18
  readonly pascalCase?: boolean;
19
19
 
20
20
  /**
21
- Preserve consecutive uppercase characters: `foo-BAR` → `FooBAR`
21
+ Preserve consecutive uppercase characters: `foo-BAR` → `fooBAR`
22
22
 
23
23
  @default false
24
24
 
@@ -31,17 +31,19 @@ export type Options = {
31
31
 
32
32
  camelCase('foo-BAR', {preserveConsecutiveUppercase: false});
33
33
  //=> 'fooBar'
34
- ````
34
+ ```
35
35
  */
36
36
  readonly preserveConsecutiveUppercase?: boolean;
37
37
 
38
38
  /**
39
39
  Controls whether letters immediately following digits are treated as word boundaries.
40
40
 
41
- **When `true` (default):**
42
- Capitalize the first letter immediately following a numeric sequence, unless a separator intervenes. This aligns with most utilities, such as Lodash.
41
+ When `true` (default):
42
+ Capitalize the first letter immediately following a numeric sequence, unless that letter is itself immediately followed by a separator. This is similar to Lodash, which also capitalizes after a number, but without the separator condition.
43
+
44
+ The extra condition keeps a token like `b2b` intact, so `b2b_registration` becomes `b2bRegistration` rather than `b2BRegistration`. It also means a trailing separator can stop the capitalization, as in `1a` → `1A` but `1a.` → `1a`.
43
45
 
44
- **When `false`:**
46
+ When `false`:
45
47
  Do not treat digits as word boundaries. Preserve the original case of letters immediately following digits. Separators still create word boundaries. This follows the Google Java Style Guide which treats numbers as non-boundary characters.
46
48
 
47
49
  @default true
@@ -98,7 +100,7 @@ export type Options = {
98
100
  ```
99
101
  import camelCase from '@cjser/camelcase';
100
102
 
101
- // On a platform with `tr-TR`.`
103
+ // On a platform with `tr-TR`.
102
104
 
103
105
  camelCase('lorem-ipsum');
104
106
  //=> 'loremİpsum'
@@ -150,5 +152,5 @@ camelCase(['__foo__', '--bar']);
150
152
  */
151
153
  export default function camelcase(
152
154
  input: string | readonly string[],
153
- options?: Options
155
+ options?: Options,
154
156
  ): string;
package/index.js CHANGED
@@ -1,34 +1,46 @@
1
- const UPPERCASE = /[\p{Lu}]/u;
2
- const LOWERCASE = /[\p{Ll}]/u;
3
- const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/u;
4
- const SEPARATORS = /[_.\- ]+/;
1
+ const UPPERCASE = /\p{Uppercase_Letter}/v;
2
+ const LOWERCASE = /\p{Lowercase_Letter}/v;
3
+ const ALPHABETIC = /\p{Alphabetic}/v;
4
+ // A decimal digit in any script, not only ASCII like `\d`. Other \p{Number} characters, such as superscripts and Roman numerals, are not digit runs.
5
+ const DIGIT = /\p{Decimal_Number}/v;
6
+ const LEADING_CAPITAL = /^\p{Uppercase_Letter}(?!\p{Uppercase_Letter})/v;
7
+ const SEPARATORS = /[ \-._]+/v;
8
+ const LEADING_PREFIX = /^[$_]*/v;
5
9
 
6
10
  // The |$ alternative allows matching at end-of-string, capturing empty string
7
11
  // This enables NUMBERS_AND_IDENTIFIER to match digits at string end (e.g., "test123")
8
- const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
9
-
10
- const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
11
- const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
12
- const NUMBERS_AND_IDENTIFIER = new RegExp(String.raw`\d+` + IDENTIFIER.source, 'gu');
12
+ //
13
+ // A combining mark with no base letter is not an identifier, so `a-́b` comes back unchanged. A mark after a base letter is fine, so decomposed `café-bar` still gives `caféBar`.
14
+ const IDENTIFIER = /(?<identifier>[\p{Alphabetic}\p{Number}_]|$)/v;
15
+
16
+ const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source, 'v');
17
+ // A long separator run followed by a rejecting character backtracks quadratically (see the input length note in the readme). An atomic run would change the result for inputs like `a-_-!`, where the trailing `_` is the identifier.
18
+ // eslint-disable-next-line regexp/no-super-linear-move
19
+ const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gv');
20
+ // False positive: `\p{Decimal_Number}` is a subset of the identifier class, so a rejecting suffix costs one backtrack, not one per digit. A run of millions of non-ASCII digits can overflow V8's regex stack and throw a RangeError (see the input length note in the readme).
21
+ // eslint-disable-next-line regexp/no-super-linear-move
22
+ const NUMBERS_AND_IDENTIFIER = new RegExp(String.raw`\p{Decimal_Number}+` + IDENTIFIER.source, 'gv');
13
23
 
14
24
  const preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUppercase) => {
25
+ // Iterate code points, not code units. Letters outside the basic multilingual plane need a surrogate pair, so indexing the string directly would see two meaningless halves per letter.
26
+ const characters = [...string];
15
27
  let isLastCharLower = false;
16
28
  let isLastCharUpper = false;
17
29
  let isLastLastCharUpper = false;
18
30
  let isLastLastCharPreserved = false;
19
31
 
20
- for (let index = 0; index < string.length; index++) {
21
- const character = string[index];
32
+ for (let index = 0; index < characters.length; index++) {
33
+ const character = characters[index];
22
34
 
23
35
  // Was the character 3 positions back inserted as a separator?
24
36
  // Prevents excessive separators by checking if we recently inserted one
25
37
  // index - 3 accounts for: current character, inserted separator, previous character
26
38
  // Default true for early positions activates the preserveConsecutiveUppercase guard
27
- isLastLastCharPreserved = index > 2 ? string[index - 3] === '-' : true;
39
+ isLastLastCharPreserved = index > 2 ? characters[index - 3] === '-' : true;
28
40
 
29
41
  if (isLastCharLower && UPPERCASE.test(character)) {
30
42
  // FooBar → Foo-Bar (insert separator before uppercase)
31
- string = string.slice(0, index) + '-' + string.slice(index);
43
+ characters.splice(index, 0, '-');
32
44
  isLastCharLower = false;
33
45
  isLastLastCharUpper = isLastCharUpper;
34
46
  isLastCharUpper = true;
@@ -40,7 +52,7 @@ const preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutive
40
52
  && (!isLastLastCharPreserved || preserveConsecutiveUppercase)
41
53
  ) {
42
54
  // FOOBar → FOO-Bar
43
- string = string.slice(0, index - 1) + '-' + string.slice(index - 1);
55
+ characters.splice(index - 1, 0, '-');
44
56
  isLastLastCharUpper = isLastCharUpper;
45
57
  isLastCharUpper = false;
46
58
  isLastCharLower = true;
@@ -55,54 +67,54 @@ const preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutive
55
67
  }
56
68
  }
57
69
 
58
- return string;
70
+ return characters.join('');
59
71
  };
60
72
 
73
+ // Only a lone leading capital is lowercased. Cased characters that are not Uppercase_Letter (titlecase letters, Roman numerals) survive here, while the capitalizeAfterNumber: false path lowercases them.
61
74
  const preserveConsecutiveUppercase = (input, toLowerCase) => input.replace(LEADING_CAPITAL, match => toLowerCase(match));
62
75
 
63
- const processWithCasePreservation = (input, toLowerCase, preserveConsecutiveUppercase) => {
76
+ const processWithCasePreservation = (input, toLowerCase, shouldPreserveConsecutiveUppercase) => {
64
77
  let result = '';
65
- let previousWasNumber = false;
66
- let previousWasUppercase = false;
78
+ let wasNumberBefore = false;
67
79
 
68
- // Convert input to array for lookahead capability
80
+ // Iterate code points, not code units (see preserveCamelCase)
69
81
  const characters = [...input];
70
82
 
71
83
  for (let index = 0; index < characters.length; index++) {
72
84
  const character = characters[index];
73
85
  const isUpperCase = UPPERCASE.test(character);
74
- const nextCharIsUpperCase = index + 1 < characters.length && UPPERCASE.test(characters[index + 1]);
75
86
 
76
- if (previousWasNumber && /[\p{Alpha}]/u.test(character)) {
87
+ // Mirrors LEADING_CAPITAL: a lone capital at the very start of the string is lowercased, even when preserveConsecutiveUppercase is on.
88
+ const isLoneLeadingCapital = index === 0
89
+ && isUpperCase
90
+ && !(index + 1 < characters.length && UPPERCASE.test(characters[index + 1]));
91
+
92
+ if (wasNumberBefore && ALPHABETIC.test(character)) {
77
93
  // Letter after number - preserve original case
78
94
  result += character;
79
- previousWasNumber = false;
80
- previousWasUppercase = isUpperCase;
81
- } else if (preserveConsecutiveUppercase && isUpperCase && (previousWasUppercase || nextCharIsUpperCase)) {
82
- // Part of consecutive uppercase sequence when preserveConsecutiveUppercase is true - keep it
95
+ wasNumberBefore = false;
96
+ } else if (shouldPreserveConsecutiveUppercase && isUpperCase && !isLoneLeadingCapital) {
97
+ // Mirror the capitalizeAfterNumber: true path, which keeps every capital except a lone leading one.
83
98
  result += character;
84
- previousWasUppercase = true;
85
- } else if (/\d/.test(character)) {
99
+ } else if (DIGIT.test(character)) {
86
100
  // Number - keep as-is and track it
87
101
  result += character;
88
- previousWasNumber = true;
89
- previousWasUppercase = false;
102
+ wasNumberBefore = true;
90
103
  } else if (SEPARATORS.test(character)) {
91
- // Separator - keep as-is and maintain previousWasNumber state
104
+ // Separator - keep as-is, and end any digit run, so the next letter is lowercased like any other.
92
105
  result += character;
93
- previousWasUppercase = false;
106
+ wasNumberBefore = false;
94
107
  } else {
95
- // Regular character - lowercase it
108
+ // Regular character - lowercase it. This goes one character at a time, so U+03A3 never becomes the final sigma, which depends on the surrounding characters.
96
109
  result += toLowerCase(character);
97
- previousWasNumber = false;
98
- previousWasUppercase = false;
110
+ wasNumberBefore = false;
99
111
  }
100
112
  }
101
113
 
102
114
  return result;
103
115
  };
104
116
 
105
- /**
117
+ /*
106
118
  Core post-processing:
107
119
  - Collapses separators and uppercases the following identifier character.
108
120
  - Optionally uppercases the identifier following a numeric sequence.
@@ -116,8 +128,9 @@ Example: "b2b_registration" with capitalizeAfterNumber: true
116
128
  - Pass 2: "_r" matches, replace with "R" → "b2bRegistration"
117
129
  */
118
130
  const postProcess = (input, toUpperCase, {capitalizeAfterNumber}) => {
119
- const transformNumericIdentifier = capitalizeAfterNumber
120
- ? (match, identifier, offset, string) => {
131
+ // When `capitalizeAfterNumber` is false, numbers do not create a word boundary, so pass 1 is skipped entirely.
132
+ const withNumbers = capitalizeAfterNumber
133
+ ? input.replaceAll(NUMBERS_AND_IDENTIFIER, (match, identifier, offset, string) => {
121
134
  const nextCharacter = string.charAt(offset + match.length);
122
135
 
123
136
  // If the numeric+identifier run is immediately followed by a separator,
@@ -128,16 +141,13 @@ const postProcess = (input, toUpperCase, {capitalizeAfterNumber}) => {
128
141
 
129
142
  // Only uppercase the identifier part (not the digits) for efficiency
130
143
  return identifier ? match.slice(0, -identifier.length) + toUpperCase(identifier) : match;
131
- }
132
- // When false: numbers do not create a word boundary.
133
- : match => match;
134
-
135
- return input
136
- .replaceAll(NUMBERS_AND_IDENTIFIER, transformNumericIdentifier)
137
- .replaceAll(
138
- SEPARATORS_AND_IDENTIFIER,
139
- (_, identifier) => toUpperCase(identifier),
140
- );
144
+ })
145
+ : input;
146
+
147
+ return withNumbers.replaceAll(
148
+ SEPARATORS_AND_IDENTIFIER,
149
+ (_, identifier) => toUpperCase(identifier),
150
+ );
141
151
  };
142
152
 
143
153
  export default function camelCase(input, options) {
@@ -166,7 +176,7 @@ export default function camelCase(input, options) {
166
176
  }
167
177
 
168
178
  // Preserve leading _ and $ as they have semantic meaning
169
- const leadingPrefix = input.match(/^[_$]*/)[0];
179
+ const leadingPrefix = input.match(LEADING_PREFIX)[0];
170
180
  input = input.slice(leadingPrefix.length);
171
181
 
172
182
  if (input.length === 0) {
@@ -217,7 +227,9 @@ export default function camelCase(input, options) {
217
227
  }
218
228
 
219
229
  if (options.pascalCase && input.length > 0) {
220
- input = toUpperCase(input[0]) + input.slice(1);
230
+ // Read the first code point, not the first code unit, so astral letters get uppercased.
231
+ const firstCharacter = String.fromCodePoint(input.codePointAt(0));
232
+ input = toUpperCase(firstCharacter) + input.slice(firstCharacter.length);
221
233
  }
222
234
 
223
235
  return leadingPrefix + postProcess(input, toUpperCase, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjser/camelcase",
3
- "version": "9.0.0-cjser.2",
3
+ "version": "10.0.0-cjser.2",
4
4
  "description": "Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -8,11 +8,7 @@
8
8
  "url": "https://code.moenext.com/3rdeye/cjser.git"
9
9
  },
10
10
  "funding": "https://github.com/sponsors/sindresorhus",
11
- "author": {
12
- "name": "Sindre Sorhus",
13
- "email": "sindresorhus@gmail.com",
14
- "url": "https://sindresorhus.com"
15
- },
11
+ "author": "Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)",
16
12
  "type": "module",
17
13
  "exports": {
18
14
  "types": "./index.d.ts",
@@ -21,10 +17,10 @@
21
17
  },
22
18
  "sideEffects": false,
23
19
  "engines": {
24
- "node": ">=20"
20
+ "node": ">=22"
25
21
  },
26
22
  "scripts": {
27
- "test": "xo && ava && tsd"
23
+ "test": "xo && node --test && nz"
28
24
  },
29
25
  "files": [
30
26
  "index.js",
@@ -32,7 +28,6 @@
32
28
  "dist-cjser"
33
29
  ],
34
30
  "keywords": [
35
- "camelcase",
36
31
  "camel-case",
37
32
  "camel",
38
33
  "case",
@@ -48,18 +43,18 @@
48
43
  "pascal-case"
49
44
  ],
50
45
  "devDependencies": {
51
- "ava": "^6.4.1",
52
- "tsd": "^0.33.0",
53
- "xo": "^1.2.3"
46
+ "@typescript/native": "npm:typescript@^7.0.2",
47
+ "nz": "^0.1.0",
48
+ "xo": "^5.0.1"
54
49
  },
55
50
  "types": "./index.d.ts",
56
51
  "main": "./dist-cjser/index.cjs",
57
52
  "cjser": {
58
- "sourceVersion": "9.0.0",
53
+ "sourceVersion": "10.0.0",
59
54
  "cjserVersion": 2,
60
55
  "original": {
61
56
  "name": "camelcase",
62
- "version": "9.0.0",
57
+ "version": "10.0.0",
63
58
  "exports": {
64
59
  "types": "./index.d.ts",
65
60
  "default": "./index.js"
@@ -70,7 +65,7 @@
70
65
  "index.d.ts"
71
66
  ],
72
67
  "scripts": {
73
- "test": "xo && ava && tsd"
68
+ "test": "xo && node --test && nz"
74
69
  }
75
70
  }
76
71
  }
package/readme.md CHANGED
@@ -45,7 +45,9 @@ camelCase(['__foo__', '--bar']);
45
45
  ```
46
46
 
47
47
  > [!NOTE]
48
- > Not idempotent when special characters are present. `camelCase('{ A')` → `'{A}'`, but `camelCase('{A}')` → `'{a}'`.
48
+ > Not idempotent in general. `camelCase('{ A')` → `'{A'`, but `camelCase('{A')` → `'{a'`.
49
+ >
50
+ > Mixed case input is not idempotent either, because the word boundaries have to be guessed: `camelCase('Ac_c A')` → `'acCA'`, but `camelCase('acCA')` → `'acCa'`.
49
51
 
50
52
  ## API
51
53
 
@@ -53,7 +55,7 @@ camelCase(['__foo__', '--bar']);
53
55
 
54
56
  #### input
55
57
 
56
- Type: `string | string[]`
58
+ Type: `string | readonly string[]`
57
59
 
58
60
  The string to convert to camel case.
59
61
 
@@ -83,7 +85,7 @@ camelCase('foo-bar', {pascalCase: false});
83
85
  Type: `boolean`\
84
86
  Default: `false`
85
87
 
86
- Preserve consecutive uppercase characters: `foo-BAR` → `FooBAR`
88
+ Preserve consecutive uppercase characters: `foo-BAR` → `fooBAR`
87
89
 
88
90
  ```js
89
91
  import camelCase from 'camelcase';
@@ -93,7 +95,7 @@ camelCase('foo-BAR', {preserveConsecutiveUppercase: true});
93
95
 
94
96
  camelCase('foo-BAR', {preserveConsecutiveUppercase: false});
95
97
  //=> 'fooBar'
96
- ````
98
+ ```
97
99
 
98
100
  ##### capitalizeAfterNumber
99
101
 
@@ -102,10 +104,12 @@ Default: `true`
102
104
 
103
105
  Controls whether letters immediately following digits are treated as word boundaries.
104
106
 
105
- **When `true` (default):**
106
- Capitalize the first letter immediately following a numeric sequence, unless a separator intervenes. This aligns with most utilities, such as Lodash.
107
+ When `true` (default):
108
+ Capitalize the first letter immediately following a numeric sequence, unless that letter is itself immediately followed by a separator. This is similar to Lodash, which also capitalizes after a number, but without the separator condition.
109
+
110
+ The extra condition keeps a token like `b2b` intact, so `b2b_registration` becomes `b2bRegistration` rather than `b2BRegistration`. It also means a trailing separator can stop the capitalization, as in `1a` → `1A` but `1a.` → `1a`.
107
111
 
108
- **When `false`:**
112
+ When `false`:
109
113
  Do not treat digits as word boundaries. Preserve the original case of letters immediately following digits. Separators still create word boundaries. This follows the Google Java Style Guide which treats numbers as non-boundary characters.
110
114
 
111
115
  ```js
@@ -131,7 +135,7 @@ camelCase('foo_2_bar', {capitalizeAfterNumber: false});
131
135
 
132
136
  ##### locale
133
137
 
134
- Type: `false | string | string[]`\
138
+ Type: `false | string | readonly string[]`\
135
139
  Default: The host environment’s current locale.
136
140
 
137
141
  The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used.