@limaformat/lima 0.3.1 → 0.5.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/dist/scalars.js CHANGED
@@ -13,13 +13,12 @@ export const hasActiveReferences2 = (value) => value.kind === 'string' ? (value.
13
13
  : false;
14
14
  /** The `ValueBuilder<PositionedValue>` — reconstructs today's annotated tree exactly, for References. */
15
15
  export const positionedBuilder = {
16
- tracksStringSourcePositions: true,
17
16
  null: (line) => ({ kind: 'null', line }),
18
17
  bool: (value, line) => ({ kind: 'bool', value, line }),
19
18
  int: (value, line) => ({ kind: 'int', value, line }),
20
19
  float: (value, line) => ({ kind: 'float', value, line }),
21
- string: (value, line, quoted, sourceSpans) => {
22
- const references2 = quoted ? undefined : scanReferenceTokens2(value, line, sourceSpans);
20
+ string: (value, line, quoted, source) => {
21
+ const references2 = quoted ? undefined : scanReferenceTokens2(value, source);
23
22
  return {
24
23
  kind: 'string', value, line, quoted,
25
24
  ...(references2 && references2.length > 0 ? { references2 } : {}),
@@ -239,7 +238,7 @@ export const parseSimpleScalarSpan = (source, start, end, line, strict, builder)
239
238
  * an intermediate tagged `LimaValue` keeps both builders on one grammar while
240
239
  * eliminating an allocate-then-switch conversion for every scalar.
241
240
  */
242
- const buildTyped = (str, strict, line, builder) => {
241
+ const buildTyped = (str, strict, line, builder, source) => {
243
242
  if (str === '' || str === 'null' || str === '~')
244
243
  return builder.null(line);
245
244
  if (str === 'true')
@@ -253,7 +252,7 @@ const buildTyped = (str, strict, line, builder) => {
253
252
  // the email/date prechecks for ordinary words, URLs and identifiers.
254
253
  if (!((first >= 48 && first <= 57) || first === 45 || first === 46)) {
255
254
  checkStringLimit(str, line);
256
- return builder.string(str, line, false);
255
+ return builder.string(str, line, false, source);
257
256
  }
258
257
  // Hex (0x/0X), octal (0o/0O), binary (0b/0B) — kept as strings (YAML 1.2 compatible).
259
258
  if (str.length > 2 && str.charCodeAt(0) === 48 &&
@@ -261,7 +260,7 @@ const buildTyped = (str, strict, line, builder) => {
261
260
  str.charCodeAt(1) === 111 || str.charCodeAt(1) === 79 ||
262
261
  str.charCodeAt(1) === 98 || str.charCodeAt(1) === 66)) {
263
262
  checkStringLimit(str, line);
264
- return builder.string(str, line, false);
263
+ return builder.string(str, line, false, source);
265
264
  }
266
265
  const exactIsoShape = (str.length === 10 && str.charCodeAt(4) === 45 && str.charCodeAt(7) === 45) ||
267
266
  (str.length === 20 && str.charCodeAt(4) === 45 && str.charCodeAt(7) === 45 &&
@@ -272,7 +271,7 @@ const buildTyped = (str, strict, line, builder) => {
272
271
  if (date !== null)
273
272
  return builder.instant(date, line);
274
273
  checkStringLimit(str, line);
275
- return builder.string(str, line, false);
274
+ return builder.string(str, line, false, source);
276
275
  }
277
276
  if (NUMBER_RE.test(str)) {
278
277
  const n = Number(str);
@@ -307,10 +306,82 @@ const buildTyped = (str, strict, line, builder) => {
307
306
  return builder.instant(date, line);
308
307
  }
309
308
  checkStringLimit(str, line);
310
- return builder.string(str, line, false);
309
+ return builder.string(str, line, false, source);
311
310
  };
312
311
  // ─── Scalar / quoting ──────────────────────────────────────────────────────
313
- const ESCAPED_HASH_RE = /\\#/g;
312
+ /**
313
+ * Index of the quote that closes a quoted scalar opening at `s[0]`, or -1
314
+ * if it is never closed. Escape-aware, so `"a\""` closes at its last
315
+ * character rather than the escaped inner `"`:
316
+ * - double quotes: a backslash escapes the next character (§6.1.2);
317
+ * - single-quoted *values* (§6.1.3 "Backslash pairing"): scanning left
318
+ * to right, `\\` (two literal backslashes) and `\'` (an escaped quote)
319
+ * are each consumed as a two-character unit, so a lone `\` is literal
320
+ * and a closing `'` after an even backslash run closes the string.
321
+ * Pass `singleQuoteEscape = false` for single-quoted *keys*, which are
322
+ * fully literal (§5.2), where the first `'` closes.
323
+ * `s[0]` is assumed to be `'` or `"`.
324
+ */
325
+ export const closingQuoteIndex = (s, singleQuoteEscape = true) => {
326
+ const q = s.charCodeAt(0);
327
+ for (let i = 1; i < s.length; i++) {
328
+ const c = s.charCodeAt(i);
329
+ if (q === 34) {
330
+ if (c === 92) {
331
+ i++;
332
+ continue;
333
+ }
334
+ if (c === 34)
335
+ return i;
336
+ }
337
+ else {
338
+ if (singleQuoteEscape && c === 92) {
339
+ const n = s.charCodeAt(i + 1);
340
+ if (n === 92 || n === 39) {
341
+ i++;
342
+ continue;
343
+ }
344
+ }
345
+ if (c === 39)
346
+ return i;
347
+ }
348
+ }
349
+ return -1;
350
+ };
351
+ /**
352
+ * The unquoted-key grammar of Core §5.1: `[a-zA-Z0-9_][a-zA-Z0-9_:\-]*`.
353
+ * Kept byte-for-byte identical to `scanner.ts`'s `isKeyStartChar` /
354
+ * `isKeyContinueChar`, which enforce the same grammar at the top level.
355
+ */
356
+ const isUnquotedKeyStart = (c) => (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95;
357
+ const isUnquotedKeyContinue = (c) => isUnquotedKeyStart(c) || c === 58 || c === 45;
358
+ /**
359
+ * Whether `raw` (a key candidate as written, before quote stripping) is a
360
+ * usable Lima key:
361
+ * - a quoted string that is properly closed at its final character, or
362
+ * - an unquoted token matching the Core §5.1 grammar.
363
+ *
364
+ * §5.1 is a closed pattern: a key with a space, a dot, a slash, a paren, or
365
+ * any other character outside `[a-zA-Z0-9_:\-]` (a surviving NBSP included)
366
+ * is not an unquoted key. Per §4, such a line/item is unrecognised and
367
+ * skipped in *both* modes — §10's strict error list stays closed. The three
368
+ * mapping contexts (top level, block-nested, flow) now share this one
369
+ * check; `docs/decisions/structural-indentation-unicode-whitespace.md`
370
+ * governs only whether Unicode whitespace counts as *indentation*, not
371
+ * whether it may appear in a key.
372
+ */
373
+ export const isValidKey = (raw) => {
374
+ const f = raw.charCodeAt(0);
375
+ if (f === 34 || f === 39)
376
+ return closingQuoteIndex(raw, false) === raw.length - 1;
377
+ if (!isUnquotedKeyStart(f))
378
+ return false;
379
+ for (let i = 1; i < raw.length; i++) {
380
+ if (!isUnquotedKeyContinue(raw.charCodeAt(i)))
381
+ return false;
382
+ }
383
+ return true;
384
+ };
314
385
  const ANY_ESCAPE_RE = /\\(u[0-9a-fA-F]{0,4}|U[0-9a-fA-F]{0,8}|x[0-9a-fA-F]{0,2}|.)/gs;
315
386
  const SINGLE_CHAR_ESCAPES = '"\\/bfnrt'; // deliberately excludes '0' — Core Appendix A: \0 is unknown, not a null shorthand.
316
387
  const U_ESCAPE_RE = /^u([0-9a-fA-F]{4})$/;
@@ -359,8 +430,54 @@ export const unescapeDQ = (s, strict = false, line = 0) => {
359
430
  }
360
431
  });
361
432
  };
433
+ /**
434
+ * The value text with a trailing `#` comment removed, but with `\#`
435
+ * escapes left as written. This is the *physical* form a References 2.0
436
+ * token's column is measured against (§2.4): the comment is not part of
437
+ * the value (so its `${…}`-shaped text must not be scanned as a token),
438
+ * but a `\#` before a token still occupies its two source columns.
439
+ *
440
+ * `trimEnd()` is exactly the project whitespace set (ECMAScript WhiteSpace
441
+ * + LineTerminator = `isTrimWhitespace`) — a U+00A0 / FEFF between the
442
+ * value and the `#` is not part of the value. The Rust and Go ports must
443
+ * use `is_trim_whitespace` here, not a host `trim_end` or `" \t"`.
444
+ */
445
+ export const stripCommentKeepEscapes = (val) => {
446
+ let quote = 0;
447
+ for (let i = 0; i < val.length; i++) {
448
+ const cc = val.charCodeAt(i);
449
+ if (quote) {
450
+ if (cc === 92)
451
+ i++;
452
+ else if (cc === quote)
453
+ quote = 0;
454
+ }
455
+ else if (cc === 34 || cc === 39) {
456
+ quote = cc;
457
+ }
458
+ else if (cc === 92 && val.charCodeAt(i + 1) === 35) {
459
+ i++;
460
+ }
461
+ else if (cc === 35) {
462
+ return val.slice(0, i).trimEnd();
463
+ }
464
+ }
465
+ return val;
466
+ };
467
+ /**
468
+ * The value text with a trailing `#` comment removed and each `\#` *outside*
469
+ * a quoted string collapsed to `#` (Core §6.1.4 — the escaped-hash rule is
470
+ * defined for unquoted values only). Inside `"..."` / `'...'` the backslash
471
+ * is left intact: `\#` there is an unknown double-quoted escape (§6.1.2:
472
+ * strict throws, non-strict keeps it) or a literal backslash in a
473
+ * single-quoted string (§6.1.3). Finds the same comment boundary as
474
+ * `stripCommentKeepEscapes` — the two must agree, they annotate the same
475
+ * value.
476
+ */
362
477
  export const stripComment = (val) => {
363
478
  let quote = 0;
479
+ let out = '';
480
+ let seg = 0;
364
481
  for (let i = 0; i < val.length; i++) {
365
482
  const cc = val.charCodeAt(i);
366
483
  if (quote) {
@@ -373,52 +490,64 @@ export const stripComment = (val) => {
373
490
  quote = cc;
374
491
  }
375
492
  else if (cc === 92 && val.charCodeAt(i + 1) === 35) {
493
+ out += val.slice(seg, i) + '#';
376
494
  i++;
495
+ seg = i + 1;
377
496
  }
378
497
  else if (cc === 35) {
379
- return val.slice(0, i).trimEnd().replace(ESCAPED_HASH_RE, '#');
498
+ return (out + val.slice(seg, i)).trimEnd();
380
499
  }
381
500
  }
382
- return val.replace(ESCAPED_HASH_RE, '#');
501
+ return out + val.slice(seg);
383
502
  };
384
- /** Strips a key's surrounding quotes (unescaping double-quoted keys), or returns it unchanged. */
385
- export const stripKeyQuotes = (s) => {
503
+ /**
504
+ * Strips a key's surrounding quotes — unescaping a double-quoted key
505
+ * (§6.1.2 escapes, strict-checked), taking a single-quoted key literally
506
+ * (§5.2). Returns `s` unchanged when it is not a properly-closed quoted
507
+ * string. Callers gate on `isValidKey` first, so an unterminated or
508
+ * trailing-content key never reaches here as a real key.
509
+ */
510
+ export const stripKeyQuotes = (s, strict = false, line = 0) => {
386
511
  const f = s.charCodeAt(0);
387
- if (f === 39 && s.charCodeAt(s.length - 1) === 39)
388
- return s.slice(1, -1);
389
- if (f === 34 && s.charCodeAt(s.length - 1) === 34)
390
- return unescapeDQ(s.slice(1, -1));
512
+ if ((f === 34 || f === 39) && closingQuoteIndex(s, false) === s.length - 1) {
513
+ return f === 34 ? unescapeDQ(s.slice(1, -1), strict, line) : s.slice(1, -1);
514
+ }
391
515
  return s;
392
516
  };
393
517
  /**
394
518
  * Quoted-or-typed scalar, shared by every value position (top-level inline
395
519
  * values, flow-sequence/flow-mapping items, block-array scalar items).
396
- * `topLevel` gates two checks that only apply at the outermost resolveValue
397
- * call site in the legacy parser and are deliberately not extended to flow
398
- * items here, to keep this a faithful behavioral port: the "unclosed flow
399
- * bracket" throw and the "non-whitespace after closing quote" strict throw.
520
+ * §10.1's two quoted-string strict checks — "unterminated quoted string"
521
+ * and "non-whitespace content after closing quote in an inline value" —
522
+ * apply in every one of those positions, so they are enforced here rather
523
+ * than gated to the top level.
400
524
  */
401
- export const parseQuotedOrTyped = (raw, ctx, line, topLevel, builder) => {
525
+ export const parseQuotedOrTyped = (raw, ctx, line, builder, source) => {
402
526
  const first = raw.charCodeAt(0);
403
527
  if (first === 34 || first === 39) {
404
- if (raw.charCodeAt(raw.length - 1) === first) {
528
+ const close = closingQuoteIndex(raw);
529
+ if (close === raw.length - 1) {
405
530
  const unquoted = raw.slice(1, -1);
406
531
  const value = first === 34 ? unescapeDQ(unquoted, ctx.strict, line) : unquoted.replace(/\\'/g, "'");
407
532
  checkStringLimit(value, line);
408
533
  return builder.string(value, line, true);
409
534
  }
410
- if (topLevel && ctx.strict) {
411
- throw new LimaError({ code: 'INVALID_QUOTE', line, message: `Lima: non-whitespace content after closing quote at line ${line}` });
535
+ if (ctx.strict) {
536
+ throw close === -1
537
+ ? new LimaError({ code: 'INVALID_QUOTE', line, message: `Lima: unterminated quoted string at line ${line}` })
538
+ : new LimaError({ code: 'INVALID_QUOTE', line, message: `Lima: non-whitespace content after closing quote at line ${line}` });
412
539
  }
540
+ // Non-strict: §6.1.2/§6.1.3 + §10.1 — the entire value falls back to
541
+ // a literal string (opening quote included), handled below.
413
542
  }
414
543
  if (raw !== '' && raw !== 'null' && raw !== '~' && raw !== 'true' && raw !== 'false' &&
415
544
  !((first >= 48 && first <= 57) || first === 45 || first === 46)) {
416
545
  checkStringLimit(raw, line);
417
- return builder.string(raw, line, false);
546
+ return builder.string(raw, line, false, source);
418
547
  }
419
- return buildTyped(raw, ctx.strict, line, builder);
548
+ return buildTyped(raw, ctx.strict, line, builder, source);
420
549
  };
421
- export const parseScalarValue = (raw, ctx, line, builder) => {
550
+ export const parseScalarValue = (raw, ctx, line, builder, source) => {
422
551
  const first = raw.charCodeAt(0);
423
552
  if (ctx.strict && (first === 91 || first === 123)) {
424
553
  throw new LimaError({
@@ -426,5 +555,5 @@ export const parseScalarValue = (raw, ctx, line, builder) => {
426
555
  message: `Lima: unclosed flow ${first === 91 ? 'sequence' : 'mapping'} at line ${line}`,
427
556
  });
428
557
  }
429
- return parseQuotedOrTyped(raw, ctx, line, true, builder);
558
+ return parseQuotedOrTyped(raw, ctx, line, builder, source);
430
559
  };
package/dist/scanner.d.ts CHANGED
@@ -9,15 +9,23 @@
9
9
  * standard `regex` crate is itself RE2-derived and cannot express
10
10
  * backtracking-dependent patterns at all.
11
11
  *
12
- * Reproduces (verified by extensive differential testing against the
13
- * regex it replaced — over 100,000 fuzzed and structured cases, the full
14
- * conformance corpus, and hand-built adversarial edge cases, zero
15
- * divergences) the exact matching behavior of:
12
+ * Equivalent to this regex:
16
13
  *
17
- * /^(?:([a-zA-Z\d_][a-zA-Z\d_:-]*)|'([^']*)'|"((?:[^"\\]|\\.)*)"):( *\n| )/gm
14
+ * /^(?:([a-zA-Z\d_][a-zA-Z\d_:-]*)|'([^'\n]*)'|"((?:[^"\\\n]|\\[^\n])*)"):( *\n| )/gm
18
15
  *
19
- * including subtle backtracking-dependent cases that are easy to get
20
- * wrong without empirical verification against the real parser:
16
+ * The scanner replaced an actual regex of this shape; the differential
17
+ * testing that verified the port — over 100,000 fuzzed and structured
18
+ * cases, the full conformance corpus, hand-built adversarial edge cases,
19
+ * zero divergences — was against that original. The quoted-key character
20
+ * classes have since been tightened to exclude only U+000A (Core errata
21
+ * 1.0.8 / 1.0.9), reflected above: `[^'\n]`, `[^"\\\n]`, and `\\[^\n]` (a
22
+ * backslash escapes any character except U+000A; the resulting sequence is
23
+ * then validated as an escape at decode time — an unknown escape like
24
+ * `\` + U+2028 throws in strict, is kept literally in non-strict, exactly
25
+ * like `\q`).
26
+ *
27
+ * Subtle backtracking-dependent cases that are easy to get wrong without
28
+ * empirical verification against the real parser:
21
29
  * - `a:b: value` → key "a:b" (colon is a legal mid-key character; the
22
30
  * mandatory separator-introducing `:` is whichever colon within the
23
31
  * greedily-matched run is the RIGHTMOST one for which a valid
@@ -30,12 +38,17 @@
30
38
  * at all (the separator alternative `( *\n| )` requires an actual
31
39
  * trailing newline or a literal space; end-of-string alone satisfies
32
40
  * neither).
33
- * - Quoted keys (`'...'`, `"..."`) may contain a literal `\n` — the
34
- * character classes inside quotes don't exclude it.
35
- * - A backslash directly followed by a line terminator inside a
36
- * double-quoted key is NOT a valid `\\.` escape — the source regex's
37
- * `.` never matches a line terminator without the `s` flag (not set),
38
- * so this fails the match entirely rather than consuming the pair.
41
+ * - A raw U+000A in a quoted key aborts the match — and only U+000A:
42
+ * Core §15.6's single- and double-quoted-key character classes exclude
43
+ * U+000A alone, so U+2028 / U+2029 (and, on already-§3-normalised
44
+ * input, U+000D) are ordinary quoted-key characters here, matching the
45
+ * nested and flow key paths and the Rust/Go scanners. The `\n` escape
46
+ * is unaffected — it decodes to a key containing U+000A.
47
+ * - A backslash directly followed by U+000A inside a double-quoted key
48
+ * aborts the match (`\\[^\n]` — the backslash needs a non-U+000A
49
+ * character after it). A backslash followed by any other character,
50
+ * U+2028 / U+2029 included, is consumed as an escape pair here and
51
+ * resolved at decode time.
39
52
  */
40
53
  export interface KeyMatch {
41
54
  /** One-based source line of the key. */
package/dist/scanner.js CHANGED
@@ -9,15 +9,23 @@
9
9
  * standard `regex` crate is itself RE2-derived and cannot express
10
10
  * backtracking-dependent patterns at all.
11
11
  *
12
- * Reproduces (verified by extensive differential testing against the
13
- * regex it replaced — over 100,000 fuzzed and structured cases, the full
14
- * conformance corpus, and hand-built adversarial edge cases, zero
15
- * divergences) the exact matching behavior of:
12
+ * Equivalent to this regex:
16
13
  *
17
- * /^(?:([a-zA-Z\d_][a-zA-Z\d_:-]*)|'([^']*)'|"((?:[^"\\]|\\.)*)"):( *\n| )/gm
14
+ * /^(?:([a-zA-Z\d_][a-zA-Z\d_:-]*)|'([^'\n]*)'|"((?:[^"\\\n]|\\[^\n])*)"):( *\n| )/gm
18
15
  *
19
- * including subtle backtracking-dependent cases that are easy to get
20
- * wrong without empirical verification against the real parser:
16
+ * The scanner replaced an actual regex of this shape; the differential
17
+ * testing that verified the port — over 100,000 fuzzed and structured
18
+ * cases, the full conformance corpus, hand-built adversarial edge cases,
19
+ * zero divergences — was against that original. The quoted-key character
20
+ * classes have since been tightened to exclude only U+000A (Core errata
21
+ * 1.0.8 / 1.0.9), reflected above: `[^'\n]`, `[^"\\\n]`, and `\\[^\n]` (a
22
+ * backslash escapes any character except U+000A; the resulting sequence is
23
+ * then validated as an escape at decode time — an unknown escape like
24
+ * `\` + U+2028 throws in strict, is kept literally in non-strict, exactly
25
+ * like `\q`).
26
+ *
27
+ * Subtle backtracking-dependent cases that are easy to get wrong without
28
+ * empirical verification against the real parser:
21
29
  * - `a:b: value` → key "a:b" (colon is a legal mid-key character; the
22
30
  * mandatory separator-introducing `:` is whichever colon within the
23
31
  * greedily-matched run is the RIGHTMOST one for which a valid
@@ -30,12 +38,17 @@
30
38
  * at all (the separator alternative `( *\n| )` requires an actual
31
39
  * trailing newline or a literal space; end-of-string alone satisfies
32
40
  * neither).
33
- * - Quoted keys (`'...'`, `"..."`) may contain a literal `\n` — the
34
- * character classes inside quotes don't exclude it.
35
- * - A backslash directly followed by a line terminator inside a
36
- * double-quoted key is NOT a valid `\\.` escape — the source regex's
37
- * `.` never matches a line terminator without the `s` flag (not set),
38
- * so this fails the match entirely rather than consuming the pair.
41
+ * - A raw U+000A in a quoted key aborts the match — and only U+000A:
42
+ * Core §15.6's single- and double-quoted-key character classes exclude
43
+ * U+000A alone, so U+2028 / U+2029 (and, on already-§3-normalised
44
+ * input, U+000D) are ordinary quoted-key characters here, matching the
45
+ * nested and flow key paths and the Rust/Go scanners. The `\n` escape
46
+ * is unaffected — it decodes to a key containing U+000A.
47
+ * - A backslash directly followed by U+000A inside a double-quoted key
48
+ * aborts the match (`\\[^\n]` — the backslash needs a non-U+000A
49
+ * character after it). A backslash followed by any other character,
50
+ * U+2028 / U+2029 included, is consumed as an escape pair here and
51
+ * resolved at decode time.
39
52
  */
40
53
  const isKeyStartChar = (c) => (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95; // a-z A-Z 0-9 _
41
54
  const isKeyContinueChar = (c) => isKeyStartChar(c) || c === 58 || c === 45; // + : -
@@ -62,6 +75,10 @@ const matchAt = (s, pos, line, out) => {
62
75
  const end = s.indexOf("'", pos + 1);
63
76
  if (end === -1 || s.charCodeAt(end + 1) !== 58)
64
77
  return false;
78
+ for (let i = pos + 1; i < end; i++) {
79
+ if (s.charCodeAt(i) === 10)
80
+ return false; // §15.6 excludes U+000A only
81
+ }
65
82
  sepStart = end + 2;
66
83
  separator = matchSeparator(s, sepStart);
67
84
  if (separator === 0)
@@ -77,8 +94,7 @@ const matchAt = (s, pos, line, out) => {
77
94
  const cc = s.charCodeAt(i);
78
95
  if (cc === 92) {
79
96
  // `\\.` in the source regex — see the module doc comment.
80
- const next = s.charCodeAt(i + 1);
81
- if (i + 1 >= s.length || next === 10 || next === 13 || next === 0x2028 || next === 0x2029)
97
+ if (i + 1 >= s.length || s.charCodeAt(i + 1) === 10)
82
98
  return false;
83
99
  i += 2;
84
100
  continue;
@@ -87,6 +103,8 @@ const matchAt = (s, pos, line, out) => {
87
103
  closed = true;
88
104
  break;
89
105
  }
106
+ if (cc === 10)
107
+ return false; // §15.6 excludes U+000A only
90
108
  i++;
91
109
  }
92
110
  if (!closed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limaformat/lima",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "Lima frontmatter parser — LIMA Is Metadata Annotation",
5
5
  "keywords": ["frontmatter", "parser", "yaml", "markdown", "metadata"],
6
6
  "homepage": "https://limaformat.dev",
@@ -20,27 +20,27 @@
20
20
  }
21
21
  },
22
22
  "files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
23
- "engines": {
24
- "node": ">=18"
25
- },
26
- "scripts": {
27
- "build": "tsc -p tsconfig.build.json",
28
- "pretest": "bun run build",
29
- "test": "bun test",
30
- "typecheck": "tsc --noEmit",
31
- "prepublishOnly": "bun run typecheck && bun test",
32
- "bench": "bun run bench/index.ts",
33
- "bench:vs-yaml": "bun run bench/vs-yaml.ts",
34
- "bench:vs-yaml:node": "node bench/vs-yaml-node.mjs",
35
- "bench:cross-language": "bun run bench/cross-language.ts",
36
- "bench:differential": "bun run bench/differential.ts",
37
- "wordcount": "bun run scripts/wordcount.ts"
38
- },
39
- "devDependencies": {
40
- "@types/bun": "^1.3.14",
41
- "@types/js-yaml": "^4.0.9",
42
- "js-yaml": "^5.2.3",
43
- "typescript": "^7.0.2",
44
- "typescript-classic": "npm:typescript@^5.9.3"
45
- }
23
+ "engines": { "node": ">=18" },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.build.json",
26
+ "prepack": "bun run build",
27
+ "pretest": "bun run build",
28
+ "test": "bun test",
29
+ "pretypecheck": "bun run build",
30
+ "typecheck": "tsc --noEmit",
31
+ "prepublishOnly": "bun run typecheck && bun test",
32
+ "bench": "bun run bench/index.ts",
33
+ "bench:vs-yaml": "bun run bench/vs-yaml.ts",
34
+ "bench:vs-yaml:node": "node bench/vs-yaml-node.mjs",
35
+ "bench:cross-language": "bun run bench/cross-language.ts",
36
+ "bench:differential": "bun run bench/differential.ts",
37
+ "wordcount": "bun run scripts/wordcount.ts"
38
+ },
39
+ "devDependencies": {
40
+ "@types/bun": "^1.3.14",
41
+ "@types/js-yaml": "^4.0.9",
42
+ "js-yaml": "^5.2.3",
43
+ "typescript": "^7.0.2",
44
+ "typescript-classic": "npm:typescript@^5.9.3"
45
+ }
46
46
  }