jostraca 0.26.0 → 0.28.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.
package/src/util/basic.ts CHANGED
@@ -115,6 +115,16 @@ function select(key: any, map: Record<string, Function>) {
115
115
  */
116
116
 
117
117
 
118
+ // Hoisted from getx() to avoid recompilation on every call.
119
+ // A B C D E
120
+ const GETX_TOKEN_RE = /\s*("(\\.|[^"\\])*"|[\w\d_]+|\s+|[^\w\d_]+)\s*/g
121
+ // A: prefixing space and/or comma
122
+ // B: quoted string
123
+ // C: atom
124
+ // D: space
125
+ // E: operator
126
+
127
+
118
128
  function getx(root: any, path: string | string[]): any {
119
129
  if (null == root || 'object' !== typeof root) {
120
130
  return undefined
@@ -127,13 +137,8 @@ function getx(root: any, path: string | string[]): any {
127
137
  }
128
138
  else if ('string' === typeof path) {
129
139
 
130
- // A B C D E
131
- let tre = /\s*("(\\.|[^"\\])*"|[\w\d_]+|\s+|[^\w\d_]+)\s*/g
132
- // A: prefixing space and/or comma
133
- // B: quoted string
134
- // C: atom
135
- // D: space
136
- // E: operator
140
+ GETX_TOKEN_RE.lastIndex = 0
141
+ let tre = GETX_TOKEN_RE
137
142
 
138
143
  tokens = []
139
144
  let t = null
@@ -341,6 +346,15 @@ function escre(s: string) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') }
341
346
  function idenstr(s: string) { return s.replace(/[^\w\d]/g, '_') }
342
347
 
343
348
 
349
+ // Cache for compiled template RegExps to avoid recompilation on repeated calls.
350
+ // Stores regex and the mapping from original keys to normalized canon keys.
351
+ const templateRECache = new Map<string, { re: RegExp, canonKeys: [string, string][] }>()
352
+ const TEMPLATE_RE_CACHE_MAX = 100
353
+
354
+ // Cache for eject RegExps.
355
+ const ejectRECache = new Map<string, RegExp>()
356
+
357
+
344
358
  // NOTE: $$foo.bar$$ format used as explicit start and end markers mean regex can be used
345
359
  // unambiguously ($fooa would not match `foo`)
346
360
  function template(
@@ -364,10 +378,10 @@ function template(
364
378
  if (null != eject) {
365
379
  const ejectStart: RegExp | null = null == eject[0] ? null :
366
380
  eject[0] instanceof RegExp ? eject[0] :
367
- new RegExp('[ \t]*' + escre('' + eject[0]) + '[ \t]*\\n?')
381
+ getCachedEjectRE('' + eject[0])
368
382
  const ejectEnd: RegExp | null = null == eject[1] ? null :
369
383
  eject[1] instanceof RegExp ? eject[1] :
370
- new RegExp('[ \t]*' + escre('' + eject[1]) + '[ \t]*\\n?')
384
+ getCachedEjectRE('' + eject[1])
371
385
 
372
386
  if (null != ejectStart && null != ejectEnd) {
373
387
  let startIndex = 0
@@ -392,62 +406,89 @@ function template(
392
406
  let ref = null == spec?.ref ? '[^$]+' : spec.ref
393
407
  let specReplaceMap = spec?.replace || {}
394
408
  let specReplaceCanon: any = {}
395
- let ngI = 1
396
- let insertRE = null == spec?.insert ?
397
- new RegExp(
398
-
399
- // Match alternate for `$$foo.bar$$` model replacements.
400
- '(?<J_O>' + open + ')' +
401
- '(?<J_R>' + ref + ')' +
402
- '(?<J_C>' + close + ')' +
403
-
404
- // Template replace entries.
405
- ((Object.keys(specReplaceMap))
406
- .sort((a, b) => a.startsWith('#') ?
407
- (a.includes('-') ? b.includes('-') ? b.length - a.length : -1 : b.length - a.length) :
408
- b.length - a.length)
409
- .map((k: string, _: any) => (
410
-
411
- // Normalize key for use as group name as key could be a regexp ('/foo/' format).
412
- _ = idenstr(k).replace(/_+/g, '_'),
413
- specReplaceCanon[_] = specReplaceMap[k],
414
-
415
- // match alternate per key.
416
- `|(?<J_K${ngI++}_${_}>` +
417
-
418
- // Custom regexp.
419
- (k.match(/^\/.+\/$/) ? k.substring(1, k.length - 1)
420
-
421
- // Prepend a counter to custom group names to ensure they are unique.
422
- .replace(/\(\?<([\w\d_]+)>/g, (_, p1) => `(?<J_N${ngI++}_${p1}>`) :
423
-
424
- // Tags: #Name matches <indent><comment><space>#Name<space><newline>
425
- // #Name-Tag matches same, but inner: #<Identifer>-Tag, and
426
- // provides {Tag:<identifer>}
427
- // See template utility unit test!
428
- (_ = k.match(/^#([A-Za-z0-9]+)(-[A-Z][a-z0-9]+)?$/)) ?
429
- (`(?<J_N${ngI++}_indent>[ \t]*)` +
430
- '\\/\\/' +
431
- '[ \t]*#' +
432
- (_[1] ?
433
- `(?<J_T${ngI++}_${_[2]?.substring(1) || 'TAG'}>` +
434
- (_[2] ? '[A-Za-z0-9]+' : _[1]) + ')' : '') +
435
- (_[2] ? `-(?<J_N${ngI++}_TAG>${_[2].substring(1)})` : '') +
436
- '[ \t]*\\n?') :
437
-
438
- // Just a key string.
439
- escre(k)) + ')'
440
- ))
441
- .join(''))) :
442
- spec.insert
409
+
410
+ let insertRE: RegExp
411
+ if (null != spec?.insert) {
412
+ insertRE = spec.insert
413
+ }
414
+ else {
415
+ const cacheKey = open + '\0' + close + '\0' + ref + '\0' +
416
+ Object.keys(specReplaceMap).sort().join('\0')
417
+ const cached = templateRECache.get(cacheKey)
418
+ if (cached) {
419
+ insertRE = cached.re
420
+ // Rebuild specReplaceCanon from current values using cached key mapping.
421
+ for (const [origKey, canonKey] of cached.canonKeys) {
422
+ specReplaceCanon[canonKey] = specReplaceMap[origKey]
423
+ }
424
+ }
425
+ else {
426
+ let ngI = 1
427
+ const canonKeys: [string, string][] = []
428
+ insertRE = new RegExp(
429
+
430
+ // Match alternate for `$$foo.bar$$` model replacements.
431
+ '(?<J_O>' + open + ')' +
432
+ '(?<J_R>' + ref + ')' +
433
+ '(?<J_C>' + close + ')' +
434
+
435
+ // Template replace entries.
436
+ ((Object.keys(specReplaceMap))
437
+ .sort((a, b) => a.startsWith('#') ?
438
+ (a.includes('-') ? b.includes('-') ? b.length - a.length : -1 : b.length - a.length) :
439
+ b.length - a.length)
440
+ .map((k: string, _: any) => (
441
+
442
+ // Normalize key for use as group name as key could be a regexp ('/foo/' format).
443
+ _ = idenstr(k).replace(/_+/g, '_'),
444
+ specReplaceCanon[_] = specReplaceMap[k],
445
+ canonKeys.push([k, _]),
446
+
447
+ // match alternate per key.
448
+ `|(?<J_K${ngI++}_${_}>` +
449
+
450
+ // Custom regexp.
451
+ (k.match(/^\/.+\/$/) ? k.substring(1, k.length - 1)
452
+
453
+ // Prepend a counter to custom group names to ensure they are unique.
454
+ .replace(/\(\?<([\w\d_]+)>/g, (_, p1) => `(?<J_N${ngI++}_${p1}>`) :
455
+
456
+ // Tags: #Name matches <indent><comment><space>#Name<space><newline>
457
+ // #Name-Tag matches same, but inner: #<Identifer>-Tag, and
458
+ // provides {Tag:<identifer>}
459
+ // See template utility unit test!
460
+ (_ = k.match(/^#([A-Za-z0-9]+)(-[A-Z][a-z0-9]+)?$/)) ?
461
+ (`(?<J_N${ngI++}_indent>[ \t]*)` +
462
+ '\\/\\/' +
463
+ '[ \t]*#' +
464
+ (_[1] ?
465
+ `(?<J_T${ngI++}_${_[2]?.substring(1) || 'TAG'}>` +
466
+ (_[2] ? '[A-Za-z0-9]+' : _[1]) + ')' : '') +
467
+ (_[2] ? `-(?<J_N${ngI++}_TAG>${_[2].substring(1)})` : '') +
468
+ '[ \t]*\\n?') :
469
+
470
+ // Just a key string.
471
+ escre(k)) + ')'
472
+ ))
473
+ .join('')))
474
+
475
+ if (templateRECache.size >= TEMPLATE_RE_CACHE_MAX) {
476
+ templateRECache.clear()
477
+ }
478
+ templateRECache.set(cacheKey, { re: insertRE, canonKeys })
479
+ }
480
+ }
443
481
 
444
482
  let remain = src
445
483
  let nextm = true
446
484
 
485
+ const hasCustomHandle = null != spec?.handle
447
486
  let out = ''
487
+ let parts: string[] = hasCustomHandle ? [] : []
448
488
 
449
- // By default, just append to a string, but allow for custom handling.
450
- let handle = spec?.handle || ((s: string) => out += (null == s ? '' : s))
489
+ // By default, collect into array (O(n) join), but allow for custom handling.
490
+ let handle = hasCustomHandle ? spec!.handle! :
491
+ ((s: string) => parts.push(null == s ? '' : s))
451
492
 
452
493
  while (nextm) {
453
494
  let m = remain.match(insertRE)
@@ -536,7 +577,17 @@ function template(
536
577
  }
537
578
 
538
579
 
539
- return out
580
+ return hasCustomHandle ? out : parts.join('')
581
+ }
582
+
583
+
584
+ function getCachedEjectRE(s: string): RegExp {
585
+ let re = ejectRECache.get(s)
586
+ if (!re) {
587
+ re = new RegExp('[ \t]*' + escre(s) + '[ \t]*\\n?')
588
+ ejectRECache.set(s, re)
589
+ }
590
+ return re
540
591
  }
541
592
 
542
593
 
package/src/util/point.ts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { Gubu, Skip, Any } from 'gubu'
2
+ import { Shape, Skip, Any } from 'shape'
3
3
 
4
4
  import { getx } from './basic'
5
5
 
@@ -204,7 +204,7 @@ class PrintPoint extends Point {
204
204
  }
205
205
 
206
206
 
207
- const PointDefShape = Gubu({
207
+ const PointDefShape = Shape({
208
208
  k: Skip(String),
209
209
  n: Skip(String),
210
210
  p: Skip([]),