@karmaniverous/jeeves-watcher 0.9.5 → 0.9.7
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/config.schema.json +1 -1
- package/dist/cli/jeeves-watcher/index.js +78 -6
- package/dist/index.js +79 -7
- package/package.json +1 -1
package/config.schema.json
CHANGED
|
@@ -807,7 +807,7 @@
|
|
|
807
807
|
"items": {
|
|
808
808
|
"$ref": "#/definitions/__schema66"
|
|
809
809
|
},
|
|
810
|
-
"description": "Keys
|
|
810
|
+
"description": "Keys or glob patterns to include as YAML frontmatter. Supports picomatch globs (e.g. \"*\") and \"!\"-prefixed exclusion patterns (e.g. \"!_*\"). Explicit names preserve declaration order; glob-matched keys are sorted alphabetically."
|
|
811
811
|
},
|
|
812
812
|
"body": {
|
|
813
813
|
"type": "array",
|
|
@@ -1057,6 +1057,72 @@ function rebaseHeadings(markdown, baseHeading) {
|
|
|
1057
1057
|
return rebased.join('\n');
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
|
+
/**
|
|
1061
|
+
* @module templates/resolveFrontmatterKeys
|
|
1062
|
+
* Resolves frontmatter key patterns (with glob/negation support) against
|
|
1063
|
+
* available context keys. Patterns prefixed with `!` are exclusions.
|
|
1064
|
+
* Supports picomatch glob syntax (e.g. `*`, `_*`, `chunk_*`).
|
|
1065
|
+
*/
|
|
1066
|
+
/**
|
|
1067
|
+
* Resolve frontmatter patterns against available keys.
|
|
1068
|
+
*
|
|
1069
|
+
* @param patterns - Array of key names or glob patterns. `!`-prefixed patterns exclude.
|
|
1070
|
+
* @param allKeys - All available keys from the rendering context.
|
|
1071
|
+
* @returns Ordered array of resolved keys: explicit names in declaration order,
|
|
1072
|
+
* then glob-matched names sorted alphabetically, minus exclusions.
|
|
1073
|
+
*/
|
|
1074
|
+
function resolveFrontmatterKeys(patterns, allKeys) {
|
|
1075
|
+
const includes = [];
|
|
1076
|
+
const excludePatterns = [];
|
|
1077
|
+
for (const p of patterns) {
|
|
1078
|
+
if (p.startsWith('!')) {
|
|
1079
|
+
excludePatterns.push(p.slice(1));
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
includes.push(p);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const isExcluded = excludePatterns.length
|
|
1086
|
+
? picomatch(excludePatterns)
|
|
1087
|
+
: () => false;
|
|
1088
|
+
// Collect keys: explicit names first (in order), then glob-expanded (sorted).
|
|
1089
|
+
const result = [];
|
|
1090
|
+
const seen = new Set();
|
|
1091
|
+
// Pass 1: explicit (non-glob) names — preserved in declaration order.
|
|
1092
|
+
const explicitNames = [];
|
|
1093
|
+
const globPatterns = [];
|
|
1094
|
+
for (const p of includes) {
|
|
1095
|
+
if (isGlob(p)) {
|
|
1096
|
+
globPatterns.push(p);
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
explicitNames.push(p);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
for (const name of explicitNames) {
|
|
1103
|
+
if (!isExcluded(name) && allKeys.includes(name) && !seen.has(name)) {
|
|
1104
|
+
result.push(name);
|
|
1105
|
+
seen.add(name);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
// Pass 2: glob patterns — matched keys sorted alphabetically.
|
|
1109
|
+
if (globPatterns.length) {
|
|
1110
|
+
const isIncluded = picomatch(globPatterns);
|
|
1111
|
+
const matched = allKeys.filter((k) => isIncluded(k)).sort();
|
|
1112
|
+
for (const key of matched) {
|
|
1113
|
+
if (!isExcluded(key) && !seen.has(key)) {
|
|
1114
|
+
result.push(key);
|
|
1115
|
+
seen.add(key);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return result;
|
|
1120
|
+
}
|
|
1121
|
+
/** Check whether a pattern contains glob characters. */
|
|
1122
|
+
function isGlob(pattern) {
|
|
1123
|
+
return /[*?[\]{}]/.test(pattern);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1060
1126
|
/**
|
|
1061
1127
|
* @module templates/renderDoc
|
|
1062
1128
|
* Declarative renderer for YAML frontmatter + structured Markdown body.
|
|
@@ -1085,8 +1151,11 @@ function renderValueAsMarkdown(hbs, section, value) {
|
|
|
1085
1151
|
const md = callFormatHelper(hbs, section.format, value, section.formatArgs);
|
|
1086
1152
|
return rebaseHeadings(md, section.heading);
|
|
1087
1153
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1154
|
+
// Return string values as-is — renderDoc produces markdown, not HTML.
|
|
1155
|
+
// HTML escaping (if needed) happens downstream when the markdown is rendered.
|
|
1156
|
+
if (typeof value === 'string')
|
|
1157
|
+
return value;
|
|
1158
|
+
return JSON.stringify(value, null, 2);
|
|
1090
1159
|
}
|
|
1091
1160
|
function renderEach(hbs, section, value) {
|
|
1092
1161
|
if (!Array.isArray(value))
|
|
@@ -1127,9 +1196,10 @@ function renderEach(hbs, section, value) {
|
|
|
1127
1196
|
*/
|
|
1128
1197
|
function renderDoc(context, config, hbs) {
|
|
1129
1198
|
const parts = [];
|
|
1130
|
-
// Frontmatter
|
|
1199
|
+
// Frontmatter — resolve patterns (globs, negations) against context keys.
|
|
1200
|
+
const resolvedKeys = resolveFrontmatterKeys(config.frontmatter, Object.keys(context));
|
|
1131
1201
|
const fmObj = {};
|
|
1132
|
-
for (const key of
|
|
1202
|
+
for (const key of resolvedKeys) {
|
|
1133
1203
|
const v = get(context, key);
|
|
1134
1204
|
if (v !== undefined) {
|
|
1135
1205
|
fmObj[key] = v;
|
|
@@ -1923,10 +1993,12 @@ const renderBodySectionSchema = z.object({
|
|
|
1923
1993
|
});
|
|
1924
1994
|
/** Render config: YAML frontmatter + ordered body sections. */
|
|
1925
1995
|
const renderConfigSchema = z.object({
|
|
1926
|
-
/** Keys to extract from context and include as YAML frontmatter. */
|
|
1996
|
+
/** Keys or glob patterns to extract from context and include as YAML frontmatter. */
|
|
1927
1997
|
frontmatter: z
|
|
1928
1998
|
.array(z.string().min(1))
|
|
1929
|
-
.describe('Keys
|
|
1999
|
+
.describe('Keys or glob patterns to include as YAML frontmatter. ' +
|
|
2000
|
+
'Supports picomatch globs (e.g. "*") and "!"-prefixed exclusion patterns (e.g. "!_*"). ' +
|
|
2001
|
+
'Explicit names preserve declaration order; glob-matched keys are sorted alphabetically.'),
|
|
1930
2002
|
/** Ordered markdown body sections. */
|
|
1931
2003
|
body: z
|
|
1932
2004
|
.array(renderBodySectionSchema)
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,9 @@ import { capitalize, title, camel, snake, dash, isEqual, get, parallel, omit } f
|
|
|
12
12
|
import rehypeParse from 'rehype-parse';
|
|
13
13
|
import { unified } from 'unified';
|
|
14
14
|
import yaml from 'js-yaml';
|
|
15
|
+
import picomatch from 'picomatch';
|
|
15
16
|
import Ajv from 'ajv';
|
|
16
17
|
import addFormats from 'ajv-formats';
|
|
17
|
-
import picomatch from 'picomatch';
|
|
18
18
|
import { readdir, stat, writeFile, rm, readFile, mkdir } from 'node:fs/promises';
|
|
19
19
|
import { z, ZodError } from 'zod';
|
|
20
20
|
import { JSONPath } from 'jsonpath-plus';
|
|
@@ -374,6 +374,72 @@ function rebaseHeadings(markdown, baseHeading) {
|
|
|
374
374
|
return rebased.join('\n');
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/**
|
|
378
|
+
* @module templates/resolveFrontmatterKeys
|
|
379
|
+
* Resolves frontmatter key patterns (with glob/negation support) against
|
|
380
|
+
* available context keys. Patterns prefixed with `!` are exclusions.
|
|
381
|
+
* Supports picomatch glob syntax (e.g. `*`, `_*`, `chunk_*`).
|
|
382
|
+
*/
|
|
383
|
+
/**
|
|
384
|
+
* Resolve frontmatter patterns against available keys.
|
|
385
|
+
*
|
|
386
|
+
* @param patterns - Array of key names or glob patterns. `!`-prefixed patterns exclude.
|
|
387
|
+
* @param allKeys - All available keys from the rendering context.
|
|
388
|
+
* @returns Ordered array of resolved keys: explicit names in declaration order,
|
|
389
|
+
* then glob-matched names sorted alphabetically, minus exclusions.
|
|
390
|
+
*/
|
|
391
|
+
function resolveFrontmatterKeys(patterns, allKeys) {
|
|
392
|
+
const includes = [];
|
|
393
|
+
const excludePatterns = [];
|
|
394
|
+
for (const p of patterns) {
|
|
395
|
+
if (p.startsWith('!')) {
|
|
396
|
+
excludePatterns.push(p.slice(1));
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
includes.push(p);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const isExcluded = excludePatterns.length
|
|
403
|
+
? picomatch(excludePatterns)
|
|
404
|
+
: () => false;
|
|
405
|
+
// Collect keys: explicit names first (in order), then glob-expanded (sorted).
|
|
406
|
+
const result = [];
|
|
407
|
+
const seen = new Set();
|
|
408
|
+
// Pass 1: explicit (non-glob) names — preserved in declaration order.
|
|
409
|
+
const explicitNames = [];
|
|
410
|
+
const globPatterns = [];
|
|
411
|
+
for (const p of includes) {
|
|
412
|
+
if (isGlob(p)) {
|
|
413
|
+
globPatterns.push(p);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
explicitNames.push(p);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const name of explicitNames) {
|
|
420
|
+
if (!isExcluded(name) && allKeys.includes(name) && !seen.has(name)) {
|
|
421
|
+
result.push(name);
|
|
422
|
+
seen.add(name);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Pass 2: glob patterns — matched keys sorted alphabetically.
|
|
426
|
+
if (globPatterns.length) {
|
|
427
|
+
const isIncluded = picomatch(globPatterns);
|
|
428
|
+
const matched = allKeys.filter((k) => isIncluded(k)).sort();
|
|
429
|
+
for (const key of matched) {
|
|
430
|
+
if (!isExcluded(key) && !seen.has(key)) {
|
|
431
|
+
result.push(key);
|
|
432
|
+
seen.add(key);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
/** Check whether a pattern contains glob characters. */
|
|
439
|
+
function isGlob(pattern) {
|
|
440
|
+
return /[*?[\]{}]/.test(pattern);
|
|
441
|
+
}
|
|
442
|
+
|
|
377
443
|
/**
|
|
378
444
|
* @module templates/renderDoc
|
|
379
445
|
* Declarative renderer for YAML frontmatter + structured Markdown body.
|
|
@@ -402,8 +468,11 @@ function renderValueAsMarkdown(hbs, section, value) {
|
|
|
402
468
|
const md = callFormatHelper(hbs, section.format, value, section.formatArgs);
|
|
403
469
|
return rebaseHeadings(md, section.heading);
|
|
404
470
|
}
|
|
405
|
-
|
|
406
|
-
|
|
471
|
+
// Return string values as-is — renderDoc produces markdown, not HTML.
|
|
472
|
+
// HTML escaping (if needed) happens downstream when the markdown is rendered.
|
|
473
|
+
if (typeof value === 'string')
|
|
474
|
+
return value;
|
|
475
|
+
return JSON.stringify(value, null, 2);
|
|
407
476
|
}
|
|
408
477
|
function renderEach(hbs, section, value) {
|
|
409
478
|
if (!Array.isArray(value))
|
|
@@ -444,9 +513,10 @@ function renderEach(hbs, section, value) {
|
|
|
444
513
|
*/
|
|
445
514
|
function renderDoc(context, config, hbs) {
|
|
446
515
|
const parts = [];
|
|
447
|
-
// Frontmatter
|
|
516
|
+
// Frontmatter — resolve patterns (globs, negations) against context keys.
|
|
517
|
+
const resolvedKeys = resolveFrontmatterKeys(config.frontmatter, Object.keys(context));
|
|
448
518
|
const fmObj = {};
|
|
449
|
-
for (const key of
|
|
519
|
+
for (const key of resolvedKeys) {
|
|
450
520
|
const v = get(context, key);
|
|
451
521
|
if (v !== undefined) {
|
|
452
522
|
fmObj[key] = v;
|
|
@@ -1609,10 +1679,12 @@ const renderBodySectionSchema = z.object({
|
|
|
1609
1679
|
});
|
|
1610
1680
|
/** Render config: YAML frontmatter + ordered body sections. */
|
|
1611
1681
|
const renderConfigSchema = z.object({
|
|
1612
|
-
/** Keys to extract from context and include as YAML frontmatter. */
|
|
1682
|
+
/** Keys or glob patterns to extract from context and include as YAML frontmatter. */
|
|
1613
1683
|
frontmatter: z
|
|
1614
1684
|
.array(z.string().min(1))
|
|
1615
|
-
.describe('Keys
|
|
1685
|
+
.describe('Keys or glob patterns to include as YAML frontmatter. ' +
|
|
1686
|
+
'Supports picomatch globs (e.g. "*") and "!"-prefixed exclusion patterns (e.g. "!_*"). ' +
|
|
1687
|
+
'Explicit names preserve declaration order; glob-matched keys are sorted alphabetically.'),
|
|
1616
1688
|
/** Ordered markdown body sections. */
|
|
1617
1689
|
body: z
|
|
1618
1690
|
.array(renderBodySectionSchema)
|
package/package.json
CHANGED