@nyaomaru/divider 2.0.3 → 2.0.5

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.
package/dist/index.cjs CHANGED
@@ -122,7 +122,10 @@ function isNoneMode(value) {
122
122
  function normalizeSeparators(separators) {
123
123
  return Array.from(new Set(separators)).filter(
124
124
  (separator) => !isEmptyString(separator)
125
- );
125
+ ).sort((left, right) => right.length - left.length);
126
+ }
127
+ function createCacheKey(normalizedSeparators) {
128
+ return normalizedSeparators.join(CACHE_KEY_SEPARATOR);
126
129
  }
127
130
  var RegexCache = class {
128
131
  cache = /* @__PURE__ */ new Map();
@@ -138,6 +141,15 @@ var RegexCache = class {
138
141
  */
139
142
  get(separators) {
140
143
  const key = this.createKey(separators);
144
+ return this.getByKey(key);
145
+ }
146
+ /**
147
+ * Retrieves a cached RegExp for a precomputed cache key.
148
+ *
149
+ * @param key - Cache key string
150
+ * @returns Cached RegExp or null if not found
151
+ */
152
+ getByKey(key) {
141
153
  const regex = this.cache.get(key);
142
154
  if (regex) {
143
155
  this.cache.delete(key);
@@ -153,6 +165,15 @@ var RegexCache = class {
153
165
  */
154
166
  set(separators, regex) {
155
167
  const key = this.createKey(separators);
168
+ this.setByKey(key, regex);
169
+ }
170
+ /**
171
+ * Stores a RegExp in the cache for a precomputed cache key.
172
+ *
173
+ * @param key - Cache key string
174
+ * @param regex - Compiled RegExp to cache
175
+ */
176
+ setByKey(key, regex) {
156
177
  if (this.cache.has(key)) {
157
178
  this.cache.delete(key);
158
179
  }
@@ -173,7 +194,7 @@ var RegexCache = class {
173
194
  */
174
195
  createKey(separators) {
175
196
  const normalizedSeparators = normalizeSeparators(separators);
176
- return normalizedSeparators.join(CACHE_KEY_SEPARATOR);
197
+ return createCacheKey(normalizedSeparators);
177
198
  }
178
199
  /**
179
200
  * Gets current cache size for debugging/monitoring.
@@ -194,15 +215,16 @@ var RegexCache = class {
194
215
  var regexCache = new RegexCache();
195
216
  function getRegex(separators) {
196
217
  if (isEmptyArray(separators)) return null;
197
- const cached = regexCache.get(separators);
198
- if (cached) return cached;
199
218
  const uniqueSeparators = normalizeSeparators(separators);
200
219
  if (uniqueSeparators.length === 0) {
201
220
  return null;
202
221
  }
222
+ const cacheKey = createCacheKey(uniqueSeparators);
223
+ const cached = regexCache.getByKey(cacheKey);
224
+ if (cached) return cached;
203
225
  const pattern = uniqueSeparators.map(escapeRegExp).join("|");
204
226
  const regex = new RegExp(`(?:${pattern})`, "g");
205
- regexCache.set(separators, regex);
227
+ regexCache.setByKey(cacheKey, regex);
206
228
  return regex;
207
229
  }
208
230
  function escapeRegExp(str) {
@@ -422,13 +444,41 @@ function dividePreserve(input, separator) {
422
444
  if (isEmptyString(input)) return [""];
423
445
  return divider(input, separator, { preserveEmpty: true });
424
446
  }
425
- function countUnescaped(text, quote) {
426
- const pair = quote + quote;
447
+ var countUnescapedSingleChar = (text, quote) => {
427
448
  let count = 0;
428
- for (const chunk of dividePreserve(text, pair)) {
429
- count += dividePreserve(chunk, quote).length - 1;
449
+ for (let index = 0; index < text.length; index++) {
450
+ if (text[index] !== quote) continue;
451
+ if (text[index + 1] === quote) {
452
+ index++;
453
+ continue;
454
+ }
455
+ count++;
430
456
  }
431
457
  return count;
458
+ };
459
+ var countUnescapedMultiChar = (text, quote) => {
460
+ const escapedPair = quote + quote;
461
+ const quoteLength = quote.length;
462
+ const escapedPairLength = escapedPair.length;
463
+ let count = 0;
464
+ for (let index = 0; index < text.length; ) {
465
+ if (text.startsWith(escapedPair, index)) {
466
+ index += escapedPairLength;
467
+ continue;
468
+ }
469
+ if (text.startsWith(quote, index)) {
470
+ count++;
471
+ index += quoteLength;
472
+ continue;
473
+ }
474
+ index++;
475
+ }
476
+ return count;
477
+ };
478
+ function countUnescaped(text, quote) {
479
+ if (isEmptyString(quote)) return 0;
480
+ if (quote.length === 1) return countUnescapedSingleChar(text, quote);
481
+ return countUnescapedMultiChar(text, quote);
432
482
  }
433
483
  function stripOuterQuotes(text, quoteChar, { lenient = true } = {}) {
434
484
  const escapedPair = quoteChar + quoteChar;
@@ -480,7 +530,8 @@ var buildQuotedFields = (line, delimiter, quote, trim, lenient) => {
480
530
  const pieces = dividePreserve(line, delimiter);
481
531
  const state = {
482
532
  fields: [],
483
- current: ""
533
+ current: "",
534
+ insideQuotes: false
484
535
  };
485
536
  for (const piece of pieces) {
486
537
  appendPiece(state, piece, delimiter, quote, trim, lenient);
@@ -490,10 +541,18 @@ var buildQuotedFields = (line, delimiter, quote, trim, lenient) => {
490
541
  }
491
542
  return state.fields;
492
543
  };
544
+ var advanceQuoteState = (insideQuotes, segment, quote) => {
545
+ let nextInsideQuotes = insideQuotes;
546
+ for (const char of segment) {
547
+ if (char === quote) nextInsideQuotes = !nextInsideQuotes;
548
+ }
549
+ return nextInsideQuotes;
550
+ };
493
551
  var appendPiece = (state, piece, delimiter, quote, trim, lenient) => {
494
- state.current = isEmptyString(state.current) ? piece : state.current + delimiter + piece;
495
- const insideQuotes = countUnescaped(state.current, quote) % 2 === 1;
496
- if (!insideQuotes) {
552
+ const segment = isEmptyString(state.current) ? piece : delimiter + piece;
553
+ state.current += segment;
554
+ state.insideQuotes = quote.length === 1 ? advanceQuoteState(state.insideQuotes, segment, quote) : countUnescaped(state.current, quote) % 2 === 1;
555
+ if (!state.insideQuotes) {
497
556
  flushField(state, quote, trim, lenient);
498
557
  }
499
558
  };
@@ -502,6 +561,7 @@ var flushField = (state, quote, trim, lenient) => {
502
561
  if (trim) fieldValue = fieldValue.trim();
503
562
  state.fields.push(fieldValue);
504
563
  state.current = "";
564
+ state.insideQuotes = false;
505
565
  };
506
566
 
507
567
  // src/presets/csv-divider.ts
package/dist/index.js CHANGED
@@ -88,7 +88,10 @@ function isNoneMode(value) {
88
88
  function normalizeSeparators(separators) {
89
89
  return Array.from(new Set(separators)).filter(
90
90
  (separator) => !isEmptyString(separator)
91
- );
91
+ ).sort((left, right) => right.length - left.length);
92
+ }
93
+ function createCacheKey(normalizedSeparators) {
94
+ return normalizedSeparators.join(CACHE_KEY_SEPARATOR);
92
95
  }
93
96
  var RegexCache = class {
94
97
  cache = /* @__PURE__ */ new Map();
@@ -104,6 +107,15 @@ var RegexCache = class {
104
107
  */
105
108
  get(separators) {
106
109
  const key = this.createKey(separators);
110
+ return this.getByKey(key);
111
+ }
112
+ /**
113
+ * Retrieves a cached RegExp for a precomputed cache key.
114
+ *
115
+ * @param key - Cache key string
116
+ * @returns Cached RegExp or null if not found
117
+ */
118
+ getByKey(key) {
107
119
  const regex = this.cache.get(key);
108
120
  if (regex) {
109
121
  this.cache.delete(key);
@@ -119,6 +131,15 @@ var RegexCache = class {
119
131
  */
120
132
  set(separators, regex) {
121
133
  const key = this.createKey(separators);
134
+ this.setByKey(key, regex);
135
+ }
136
+ /**
137
+ * Stores a RegExp in the cache for a precomputed cache key.
138
+ *
139
+ * @param key - Cache key string
140
+ * @param regex - Compiled RegExp to cache
141
+ */
142
+ setByKey(key, regex) {
122
143
  if (this.cache.has(key)) {
123
144
  this.cache.delete(key);
124
145
  }
@@ -139,7 +160,7 @@ var RegexCache = class {
139
160
  */
140
161
  createKey(separators) {
141
162
  const normalizedSeparators = normalizeSeparators(separators);
142
- return normalizedSeparators.join(CACHE_KEY_SEPARATOR);
163
+ return createCacheKey(normalizedSeparators);
143
164
  }
144
165
  /**
145
166
  * Gets current cache size for debugging/monitoring.
@@ -160,15 +181,16 @@ var RegexCache = class {
160
181
  var regexCache = new RegexCache();
161
182
  function getRegex(separators) {
162
183
  if (isEmptyArray(separators)) return null;
163
- const cached = regexCache.get(separators);
164
- if (cached) return cached;
165
184
  const uniqueSeparators = normalizeSeparators(separators);
166
185
  if (uniqueSeparators.length === 0) {
167
186
  return null;
168
187
  }
188
+ const cacheKey = createCacheKey(uniqueSeparators);
189
+ const cached = regexCache.getByKey(cacheKey);
190
+ if (cached) return cached;
169
191
  const pattern = uniqueSeparators.map(escapeRegExp).join("|");
170
192
  const regex = new RegExp(`(?:${pattern})`, "g");
171
- regexCache.set(separators, regex);
193
+ regexCache.setByKey(cacheKey, regex);
172
194
  return regex;
173
195
  }
174
196
  function escapeRegExp(str) {
@@ -388,13 +410,41 @@ function dividePreserve(input, separator) {
388
410
  if (isEmptyString(input)) return [""];
389
411
  return divider(input, separator, { preserveEmpty: true });
390
412
  }
391
- function countUnescaped(text, quote) {
392
- const pair = quote + quote;
413
+ var countUnescapedSingleChar = (text, quote) => {
393
414
  let count = 0;
394
- for (const chunk of dividePreserve(text, pair)) {
395
- count += dividePreserve(chunk, quote).length - 1;
415
+ for (let index = 0; index < text.length; index++) {
416
+ if (text[index] !== quote) continue;
417
+ if (text[index + 1] === quote) {
418
+ index++;
419
+ continue;
420
+ }
421
+ count++;
396
422
  }
397
423
  return count;
424
+ };
425
+ var countUnescapedMultiChar = (text, quote) => {
426
+ const escapedPair = quote + quote;
427
+ const quoteLength = quote.length;
428
+ const escapedPairLength = escapedPair.length;
429
+ let count = 0;
430
+ for (let index = 0; index < text.length; ) {
431
+ if (text.startsWith(escapedPair, index)) {
432
+ index += escapedPairLength;
433
+ continue;
434
+ }
435
+ if (text.startsWith(quote, index)) {
436
+ count++;
437
+ index += quoteLength;
438
+ continue;
439
+ }
440
+ index++;
441
+ }
442
+ return count;
443
+ };
444
+ function countUnescaped(text, quote) {
445
+ if (isEmptyString(quote)) return 0;
446
+ if (quote.length === 1) return countUnescapedSingleChar(text, quote);
447
+ return countUnescapedMultiChar(text, quote);
398
448
  }
399
449
  function stripOuterQuotes(text, quoteChar, { lenient = true } = {}) {
400
450
  const escapedPair = quoteChar + quoteChar;
@@ -446,7 +496,8 @@ var buildQuotedFields = (line, delimiter, quote, trim, lenient) => {
446
496
  const pieces = dividePreserve(line, delimiter);
447
497
  const state = {
448
498
  fields: [],
449
- current: ""
499
+ current: "",
500
+ insideQuotes: false
450
501
  };
451
502
  for (const piece of pieces) {
452
503
  appendPiece(state, piece, delimiter, quote, trim, lenient);
@@ -456,10 +507,18 @@ var buildQuotedFields = (line, delimiter, quote, trim, lenient) => {
456
507
  }
457
508
  return state.fields;
458
509
  };
510
+ var advanceQuoteState = (insideQuotes, segment, quote) => {
511
+ let nextInsideQuotes = insideQuotes;
512
+ for (const char of segment) {
513
+ if (char === quote) nextInsideQuotes = !nextInsideQuotes;
514
+ }
515
+ return nextInsideQuotes;
516
+ };
459
517
  var appendPiece = (state, piece, delimiter, quote, trim, lenient) => {
460
- state.current = isEmptyString(state.current) ? piece : state.current + delimiter + piece;
461
- const insideQuotes = countUnescaped(state.current, quote) % 2 === 1;
462
- if (!insideQuotes) {
518
+ const segment = isEmptyString(state.current) ? piece : delimiter + piece;
519
+ state.current += segment;
520
+ state.insideQuotes = quote.length === 1 ? advanceQuoteState(state.insideQuotes, segment, quote) : countUnescaped(state.current, quote) % 2 === 1;
521
+ if (!state.insideQuotes) {
463
522
  flushField(state, quote, trim, lenient);
464
523
  }
465
524
  };
@@ -468,6 +527,7 @@ var flushField = (state, quote, trim, lenient) => {
468
527
  if (trim) fieldValue = fieldValue.trim();
469
528
  state.fields.push(fieldValue);
470
529
  state.current = "";
530
+ state.insideQuotes = false;
471
531
  };
472
532
 
473
533
  // src/presets/csv-divider.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nyaomaru/divider",
3
3
  "type": "module",
4
- "version": "2.0.3",
4
+ "version": "2.0.5",
5
5
  "description": "To divide string or string[] with a given separator",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",