@jxsuite/parser 4.2.0 → 4.3.1

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/markdown.js CHANGED
@@ -80,6 +80,9 @@ function isRef(value) {
80
80
  function isNodeObject(value) {
81
81
  return isJsonObject(value);
82
82
  }
83
+ function isTemplateString(value) {
84
+ return typeof value === "string" && value.includes("${");
85
+ }
83
86
  function isPlainTagName(tagName) {
84
87
  return typeof tagName === "string";
85
88
  }
@@ -129,6 +132,195 @@ var init_guards = __esm(() => {
129
132
  ]);
130
133
  });
131
134
 
135
+ // ../../packages/schema/src/content-query.ts
136
+ function isPlainObject(value) {
137
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
138
+ }
139
+ function comparable(value) {
140
+ if (value instanceof Date) {
141
+ if (Number.isNaN(value.getTime())) {
142
+ return;
143
+ }
144
+ const iso = value.toISOString();
145
+ return iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso.replace(/\.\d{3}Z$/, "Z");
146
+ }
147
+ return value;
148
+ }
149
+ function deepEqual(a, b) {
150
+ if (a === b) {
151
+ return true;
152
+ }
153
+ if (Array.isArray(a) && Array.isArray(b)) {
154
+ return a.length === b.length && a.every((item, i) => deepEqual(comparable(item), comparable(b[i])));
155
+ }
156
+ if (isPlainObject(a) && isPlainObject(b)) {
157
+ const keys = Object.keys(a);
158
+ return keys.length === Object.keys(b).length && keys.every((key) => (key in b) && deepEqual(comparable(a[key]), comparable(b[key])));
159
+ }
160
+ return false;
161
+ }
162
+ function sameValue(actual, expected) {
163
+ const a = comparable(actual);
164
+ if (Array.isArray(expected) || isPlainObject(expected)) {
165
+ return deepEqual(a, expected);
166
+ }
167
+ if (Array.isArray(a)) {
168
+ return a.some((item) => comparable(item) === expected);
169
+ }
170
+ return a === expected;
171
+ }
172
+ function readField(data, key) {
173
+ if (Object.hasOwn(data, key)) {
174
+ return data[key];
175
+ }
176
+ if (!key.includes(".")) {
177
+ return;
178
+ }
179
+ return readPath(data, key.split("."));
180
+ }
181
+ function readPath(holder, [part, ...rest]) {
182
+ if (part === undefined || !isPlainObject(holder) || !Object.hasOwn(holder, part)) {
183
+ return;
184
+ }
185
+ const value = holder[part];
186
+ return rest.length === 0 ? value : readPath(value, rest);
187
+ }
188
+ function order(actual, operand) {
189
+ const a = comparable(actual);
190
+ if (isNumber(a) && isNumber(operand)) {
191
+ return a - operand;
192
+ }
193
+ if (isString(a) && isString(operand)) {
194
+ return a < operand ? -1 : a > operand ? 1 : 0;
195
+ }
196
+ return;
197
+ }
198
+ function isNowOperand(operand) {
199
+ return isPlainObject(operand) && operand.$now === true && Object.keys(operand).length === 1;
200
+ }
201
+ function timeOf(value) {
202
+ const time = value instanceof Date ? value.getTime() : isString(value) ? Date.parse(value) : Number.NaN;
203
+ return Number.isNaN(time) ? undefined : time;
204
+ }
205
+ function operatorTest(where, op, operand, ctx) {
206
+ switch (op) {
207
+ case "$eq": {
208
+ return (actual) => sameValue(actual, operand);
209
+ }
210
+ case "$ne": {
211
+ return (actual) => !sameValue(actual, operand);
212
+ }
213
+ case "$in":
214
+ case "$nin": {
215
+ if (!Array.isArray(operand)) {
216
+ throw new TypeError(`${where}.${op}: expected an array, got ${JSON.stringify(operand)}`);
217
+ }
218
+ const hit = (actual) => operand.some((candidate) => sameValue(actual, candidate));
219
+ return op === "$in" ? hit : (actual) => !hit(actual);
220
+ }
221
+ case "$exists": {
222
+ if (!isBoolean(operand)) {
223
+ throw new TypeError(`${where}.$exists: expected true or false, got ${JSON.stringify(operand)}`);
224
+ }
225
+ return (actual) => (actual !== undefined && actual !== null) === operand;
226
+ }
227
+ default: {
228
+ const test = (diff) => op === "$gt" ? diff > 0 : op === "$gte" ? diff >= 0 : op === "$lt" ? diff < 0 : diff <= 0;
229
+ if (isNowOperand(operand)) {
230
+ return (actual) => {
231
+ if (actual === undefined || actual === null) {
232
+ return false;
233
+ }
234
+ const time = timeOf(actual);
235
+ return time === undefined ? ctx.onBadDate : test(time - ctx.now().getTime());
236
+ };
237
+ }
238
+ if (!isString(operand) && !isNumber(operand)) {
239
+ throw new TypeError(`${where}.${op}: expected a string or number, got ${JSON.stringify(operand)}`);
240
+ }
241
+ return (actual) => {
242
+ const diff = order(actual, operand);
243
+ return diff !== undefined && test(diff);
244
+ };
245
+ }
246
+ }
247
+ }
248
+ function operatorKeys(condition) {
249
+ const keys = Object.keys(condition);
250
+ const dollars = keys.filter((key) => key.startsWith("$"));
251
+ if (dollars.length === 0) {
252
+ return null;
253
+ }
254
+ return dollars.length === keys.length ? keys : [];
255
+ }
256
+ function compileCondition(key, condition, ctx) {
257
+ const where = `${ctx.name}.${key}`;
258
+ if (!isPlainObject(condition)) {
259
+ return (actual) => sameValue(actual, condition);
260
+ }
261
+ const ops = operatorKeys(condition);
262
+ if (ops === null) {
263
+ return (actual) => deepEqual(comparable(actual), condition);
264
+ }
265
+ if (ops.length === 0) {
266
+ throw new TypeError(`${where}: cannot mix $operators with plain keys in one condition`);
267
+ }
268
+ const tests = ops.map((op) => {
269
+ if (!OPERATORS.has(op)) {
270
+ throw new TypeError(`${where}: unknown operator "${op}" (use ${[...OPERATORS].join(", ")})`);
271
+ }
272
+ return operatorTest(where, op, condition[op], ctx);
273
+ });
274
+ return (actual) => tests.every((test) => test(actual));
275
+ }
276
+ function compileFields(query, ctx) {
277
+ return Object.entries(query).map(([key, condition]) => {
278
+ if (key.startsWith("$")) {
279
+ throw new TypeError(`${ctx.name}.${key}: top-level operators are not supported, conditions on different fields already combine with AND`);
280
+ }
281
+ return { key, test: compileCondition(key, condition, ctx) };
282
+ });
283
+ }
284
+ function compileWhere(where, options = {}) {
285
+ if (where === undefined) {
286
+ return Object.assign(() => true, { why: nothing });
287
+ }
288
+ if (!isPlainObject(where)) {
289
+ throw new TypeError(`"where" must be an object mapping frontmatter fields to conditions`);
290
+ }
291
+ const ctx = { name: "where", now: options.now ?? realClock, onBadDate: false };
292
+ const tests = compileFields(where, ctx);
293
+ return Object.assign((data) => tests.every(({ key, test }) => test(readField(data, key))), {
294
+ why: (data) => tests.find(({ key, test }) => !test(readField(data, key)))?.key
295
+ });
296
+ }
297
+ function compileHold(hold, options = {}) {
298
+ if (hold === undefined) {
299
+ return nothing;
300
+ }
301
+ if (!Array.isArray(hold)) {
302
+ throw new TypeError(`"hold" must be an array of { "label", "when" } rules`);
303
+ }
304
+ const now = options.now ?? realClock;
305
+ const rules = hold.map((rule, index) => {
306
+ const at = `hold[${index}]`;
307
+ if (!isPlainObject(rule) || !isString(rule.label) || rule.label.trim() === "") {
308
+ throw new TypeError(`${at}: expected a non-empty "label"`);
309
+ }
310
+ if (!isPlainObject(rule.when) || Object.keys(rule.when).length === 0) {
311
+ throw new TypeError(`${at}: expected "when" to name at least one frontmatter field`);
312
+ }
313
+ const ctx = { name: `${at}.when`, now, onBadDate: true };
314
+ return { label: rule.label, tests: compileFields(rule.when, ctx) };
315
+ });
316
+ return (data) => rules.find(({ tests }) => tests.every(({ key, test }) => test(readField(data, key))))?.label;
317
+ }
318
+ var OPERATORS, nothing = () => {}, realClock = () => new Date;
319
+ var init_content_query = __esm(() => {
320
+ init_guards();
321
+ OPERATORS = new Set(["$eq", "$ne", "$in", "$nin", "$exists", "$gt", "$gte", "$lt", "$lte"]);
322
+ });
323
+
132
324
  // src/content-rules.ts
133
325
  function escapeRegExp(text) {
134
326
  return text.replaceAll(REGEXP_SPECIAL, String.raw`\$&`);
@@ -254,7 +446,7 @@ function mayNameDirectory(pattern) {
254
446
  }
255
447
  function compileExclude(patterns) {
256
448
  if (patterns === undefined) {
257
- return { empty: true, excludedBy: nothing, excludesDir: () => false };
449
+ return { empty: true, excludedBy: nothing2, excludesDir: () => false };
258
450
  }
259
451
  if (!Array.isArray(patterns)) {
260
452
  throw new TypeError(`"exclude" must be an array of glob patterns`);
@@ -294,152 +486,11 @@ function compileExclude(patterns) {
294
486
  excludedBy: (path) => files.find((f) => f.re.test(path))?.pattern
295
487
  };
296
488
  }
297
- function isPlainObject(value) {
298
- return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
299
- }
300
- function comparable(value) {
301
- if (value instanceof Date) {
302
- if (Number.isNaN(value.getTime())) {
303
- return;
304
- }
305
- const iso = value.toISOString();
306
- return iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso.replace(/\.\d{3}Z$/, "Z");
307
- }
308
- return value;
309
- }
310
- function deepEqual(a, b) {
311
- if (a === b) {
312
- return true;
313
- }
314
- if (Array.isArray(a) && Array.isArray(b)) {
315
- return a.length === b.length && a.every((item, i) => deepEqual(comparable(item), comparable(b[i])));
316
- }
317
- if (isPlainObject(a) && isPlainObject(b)) {
318
- const keys = Object.keys(a);
319
- return keys.length === Object.keys(b).length && keys.every((key) => (key in b) && deepEqual(comparable(a[key]), comparable(b[key])));
320
- }
321
- return false;
322
- }
323
- function sameValue(actual, expected) {
324
- const a = comparable(actual);
325
- if (Array.isArray(expected) || isPlainObject(expected)) {
326
- return deepEqual(a, expected);
327
- }
328
- if (Array.isArray(a)) {
329
- return a.some((item) => comparable(item) === expected);
330
- }
331
- return a === expected;
332
- }
333
- function readField(data, key) {
334
- if (Object.hasOwn(data, key)) {
335
- return data[key];
336
- }
337
- if (!key.includes(".")) {
338
- return;
339
- }
340
- return readPath(data, key.split("."));
341
- }
342
- function readPath(holder, [part, ...rest]) {
343
- if (part === undefined || !isPlainObject(holder) || !Object.hasOwn(holder, part)) {
344
- return;
345
- }
346
- const value = holder[part];
347
- return rest.length === 0 ? value : readPath(value, rest);
348
- }
349
- function order(actual, operand) {
350
- const a = comparable(actual);
351
- if (isNumber(a) && isNumber(operand)) {
352
- return a - operand;
353
- }
354
- if (isString(a) && isString(operand)) {
355
- return a < operand ? -1 : a > operand ? 1 : 0;
356
- }
357
- return;
358
- }
359
- function operatorTest(where, op, operand) {
360
- switch (op) {
361
- case "$eq": {
362
- return (actual) => sameValue(actual, operand);
363
- }
364
- case "$ne": {
365
- return (actual) => !sameValue(actual, operand);
366
- }
367
- case "$in":
368
- case "$nin": {
369
- if (!Array.isArray(operand)) {
370
- throw new TypeError(`${where}.${op}: expected an array, got ${JSON.stringify(operand)}`);
371
- }
372
- const hit = (actual) => operand.some((candidate) => sameValue(actual, candidate));
373
- return op === "$in" ? hit : (actual) => !hit(actual);
374
- }
375
- case "$exists": {
376
- if (!isBoolean(operand)) {
377
- throw new TypeError(`${where}.$exists: expected true or false, got ${JSON.stringify(operand)}`);
378
- }
379
- return (actual) => (actual !== undefined && actual !== null) === operand;
380
- }
381
- default: {
382
- if (!isString(operand) && !isNumber(operand)) {
383
- throw new TypeError(`${where}.${op}: expected a string or number, got ${JSON.stringify(operand)}`);
384
- }
385
- const test = (diff) => op === "$gt" ? diff > 0 : op === "$gte" ? diff >= 0 : op === "$lt" ? diff < 0 : diff <= 0;
386
- return (actual) => {
387
- const diff = order(actual, operand);
388
- return diff !== undefined && test(diff);
389
- };
390
- }
391
- }
392
- }
393
- function operatorKeys(condition) {
394
- const keys = Object.keys(condition);
395
- const dollars = keys.filter((key) => key.startsWith("$"));
396
- if (dollars.length === 0) {
397
- return null;
398
- }
399
- return dollars.length === keys.length ? keys : [];
400
- }
401
- function compileCondition(key, condition) {
402
- const where = `where.${key}`;
403
- if (!isPlainObject(condition)) {
404
- return (actual) => sameValue(actual, condition);
405
- }
406
- const ops = operatorKeys(condition);
407
- if (ops === null) {
408
- return (actual) => deepEqual(comparable(actual), condition);
409
- }
410
- if (ops.length === 0) {
411
- throw new TypeError(`${where}: cannot mix $operators with plain keys in one condition`);
412
- }
413
- const tests = ops.map((op) => {
414
- if (!OPERATORS.has(op)) {
415
- throw new TypeError(`${where}: unknown operator "${op}" (use ${[...OPERATORS].join(", ")})`);
416
- }
417
- return operatorTest(where, op, condition[op]);
418
- });
419
- return (actual) => tests.every((test) => test(actual));
420
- }
421
- function compileWhere(where) {
422
- if (where === undefined) {
423
- return Object.assign(() => true, { why: nothing });
424
- }
425
- if (!isPlainObject(where)) {
426
- throw new TypeError(`"where" must be an object mapping frontmatter fields to conditions`);
427
- }
428
- const tests = Object.entries(where).map(([key, condition]) => {
429
- if (key.startsWith("$")) {
430
- throw new TypeError(`where.${key}: top-level operators are not supported, conditions on different fields already combine with AND`);
431
- }
432
- return { key, test: compileCondition(key, condition) };
433
- });
434
- return Object.assign((data) => tests.every(({ key, test }) => test(readField(data, key))), {
435
- why: (data) => tests.find(({ key, test }) => !test(readField(data, key)))?.key
436
- });
437
- }
438
- var nothing = () => {}, REGEXP_SPECIAL, MAX_STAR_RUNS = 4, OPERATORS;
489
+ var nothing2 = () => {}, REGEXP_SPECIAL, MAX_STAR_RUNS = 4;
439
490
  var init_content_rules = __esm(() => {
440
491
  init_guards();
492
+ init_content_query();
441
493
  REGEXP_SPECIAL = /[$()*+.?[\\\]^{|}]/g;
442
- OPERATORS = new Set(["$eq", "$ne", "$in", "$nin", "$exists", "$gt", "$gte", "$lt", "$lte"]);
443
494
  });
444
495
 
445
496
  // ../../node_modules/bail/index.js
@@ -41277,6 +41328,9 @@ function normalizeAllowlist(allowlist) {
41277
41328
  return allowlist instanceof Set ? allowlist : new Set(allowlist);
41278
41329
  }
41279
41330
  function hasJxProps(el) {
41331
+ if (isTemplateString(el.children)) {
41332
+ return true;
41333
+ }
41280
41334
  for (const key of Object.keys(el)) {
41281
41335
  if (key === "tagName" || key === "children" || key === "textContent" || key === "innerHTML" || key === "attributes") {
41282
41336
  continue;
@@ -41490,7 +41544,7 @@ function collectDirectiveAttrs(el) {
41490
41544
  continue;
41491
41545
  }
41492
41546
  if (key === "children") {
41493
- if (isJsonObject(value)) {
41547
+ if (isJsonObject(value) || isTemplateString(value)) {
41494
41548
  propsObj.children = value;
41495
41549
  }
41496
41550
  continue;
package/dist/serialize.js CHANGED
@@ -892,6 +892,9 @@ function isRef(value) {
892
892
  function isNodeObject(value) {
893
893
  return isJsonObject(value);
894
894
  }
895
+ function isTemplateString(value) {
896
+ return typeof value === "string" && value.includes("${");
897
+ }
895
898
  function isPlainTagName(tagName) {
896
899
  return typeof tagName === "string";
897
900
  }
@@ -40964,6 +40967,9 @@ function normalizeAllowlist(allowlist) {
40964
40967
  return allowlist instanceof Set ? allowlist : new Set(allowlist);
40965
40968
  }
40966
40969
  function hasJxProps(el) {
40970
+ if (isTemplateString(el.children)) {
40971
+ return true;
40972
+ }
40967
40973
  for (const key of Object.keys(el)) {
40968
40974
  if (key === "tagName" || key === "children" || key === "textContent" || key === "innerHTML" || key === "attributes") {
40969
40975
  continue;
@@ -41177,7 +41183,7 @@ function collectDirectiveAttrs(el) {
41177
41183
  continue;
41178
41184
  }
41179
41185
  if (key === "children") {
41180
- if (isJsonObject(value)) {
41186
+ if (isJsonObject(value) || isTemplateString(value)) {
41181
41187
  propsObj.children = value;
41182
41188
  }
41183
41189
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/parser",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "description": "Jx markdown parser and external class integration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,8 +48,8 @@
48
48
  "upgrade": "bun update --latest && bun install"
49
49
  },
50
50
  "dependencies": {
51
- "@jxsuite/markup": "^1.0.3",
52
- "@jxsuite/schema": "^5.1.0",
51
+ "@jxsuite/markup": "^1.0.5",
52
+ "@jxsuite/schema": "^5.3.0",
53
53
  "@shikijs/langs": "^4.5.0",
54
54
  "@shikijs/themes": "^4.5.0",
55
55
  "glob": "^14.0.3",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@happy-dom/global-registrator": "^21.0.4",
70
- "@jxsuite/runtime": "^6.0.2",
70
+ "@jxsuite/runtime": "^6.2.0",
71
71
  "@jxsuite/test-support": "^0.0.0",
72
72
  "@types/mdast": "^4.0.4",
73
73
  "ajv": "^8.20.0"
@@ -100,16 +100,76 @@
100
100
  "type": "boolean"
101
101
  },
102
102
  "$gt": {
103
- "type": ["string", "number"]
103
+ "anyOf": [
104
+ {
105
+ "type": ["string", "number"]
106
+ },
107
+ {
108
+ "type": "object",
109
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
110
+ "properties": {
111
+ "$now": {
112
+ "const": true
113
+ }
114
+ },
115
+ "required": ["$now"],
116
+ "additionalProperties": false
117
+ }
118
+ ]
104
119
  },
105
120
  "$gte": {
106
- "type": ["string", "number"]
121
+ "anyOf": [
122
+ {
123
+ "type": ["string", "number"]
124
+ },
125
+ {
126
+ "type": "object",
127
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
128
+ "properties": {
129
+ "$now": {
130
+ "const": true
131
+ }
132
+ },
133
+ "required": ["$now"],
134
+ "additionalProperties": false
135
+ }
136
+ ]
107
137
  },
108
138
  "$lt": {
109
- "type": ["string", "number"]
139
+ "anyOf": [
140
+ {
141
+ "type": ["string", "number"]
142
+ },
143
+ {
144
+ "type": "object",
145
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
146
+ "properties": {
147
+ "$now": {
148
+ "const": true
149
+ }
150
+ },
151
+ "required": ["$now"],
152
+ "additionalProperties": false
153
+ }
154
+ ]
110
155
  },
111
156
  "$lte": {
112
- "type": ["string", "number"]
157
+ "anyOf": [
158
+ {
159
+ "type": ["string", "number"]
160
+ },
161
+ {
162
+ "type": "object",
163
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
164
+ "properties": {
165
+ "$now": {
166
+ "const": true
167
+ }
168
+ },
169
+ "required": ["$now"],
170
+ "additionalProperties": false
171
+ }
172
+ ]
113
173
  }
114
174
  }
115
175
  },
@@ -133,6 +193,165 @@
133
193
  }
134
194
  ]
135
195
  },
196
+ "hold": {
197
+ "description": "What keeps an entry out of a production build while authors still see it. Any rule matching holds the entry, and the first names why. `jx build` leaves held entries out, `jx build --include-held`, `jx dev` and Studio keep them. Jx gives no field a meaning of its own: a project names its fields and says here what they imply.",
198
+ "type": "array",
199
+ "items": {
200
+ "type": "object",
201
+ "additionalProperties": false,
202
+ "required": ["label", "when"],
203
+ "properties": {
204
+ "label": {
205
+ "description": "The words every surface shows for an entry this rule holds.",
206
+ "type": "string",
207
+ "minLength": 1
208
+ },
209
+ "when": {
210
+ "type": "object",
211
+ "propertyNames": {
212
+ "pattern": "^[^$]"
213
+ },
214
+ "additionalProperties": {
215
+ "anyOf": [
216
+ {
217
+ "type": "object",
218
+ "propertyNames": {
219
+ "enum": [
220
+ "$eq",
221
+ "$ne",
222
+ "$in",
223
+ "$nin",
224
+ "$exists",
225
+ "$gt",
226
+ "$gte",
227
+ "$lt",
228
+ "$lte"
229
+ ]
230
+ },
231
+ "minProperties": 1,
232
+ "properties": {
233
+ "$eq": {},
234
+ "$ne": {},
235
+ "$in": {
236
+ "type": "array"
237
+ },
238
+ "$nin": {
239
+ "type": "array"
240
+ },
241
+ "$exists": {
242
+ "type": "boolean"
243
+ },
244
+ "$gt": {
245
+ "anyOf": [
246
+ {
247
+ "type": ["string", "number"]
248
+ },
249
+ {
250
+ "type": "object",
251
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
252
+ "properties": {
253
+ "$now": {
254
+ "const": true
255
+ }
256
+ },
257
+ "required": ["$now"],
258
+ "additionalProperties": false
259
+ }
260
+ ]
261
+ },
262
+ "$gte": {
263
+ "anyOf": [
264
+ {
265
+ "type": ["string", "number"]
266
+ },
267
+ {
268
+ "type": "object",
269
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
270
+ "properties": {
271
+ "$now": {
272
+ "const": true
273
+ }
274
+ },
275
+ "required": ["$now"],
276
+ "additionalProperties": false
277
+ }
278
+ ]
279
+ },
280
+ "$lt": {
281
+ "anyOf": [
282
+ {
283
+ "type": ["string", "number"]
284
+ },
285
+ {
286
+ "type": "object",
287
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
288
+ "properties": {
289
+ "$now": {
290
+ "const": true
291
+ }
292
+ },
293
+ "required": ["$now"],
294
+ "additionalProperties": false
295
+ }
296
+ ]
297
+ },
298
+ "$lte": {
299
+ "anyOf": [
300
+ {
301
+ "type": ["string", "number"]
302
+ },
303
+ {
304
+ "type": "object",
305
+ "description": "The moment the rule is evaluated: the build machine's real clock.",
306
+ "properties": {
307
+ "$now": {
308
+ "const": true
309
+ }
310
+ },
311
+ "required": ["$now"],
312
+ "additionalProperties": false
313
+ }
314
+ ]
315
+ }
316
+ }
317
+ },
318
+ {
319
+ "type": ["string", "number", "boolean", "null", "array"]
320
+ },
321
+ {
322
+ "type": "object",
323
+ "propertyNames": {
324
+ "pattern": "^[^$]"
325
+ }
326
+ }
327
+ ]
328
+ },
329
+ "description": "The frontmatter that matches this rule, with the grammar of `where`.",
330
+ "minProperties": 1
331
+ }
332
+ }
333
+ },
334
+ "examples": [
335
+ [
336
+ {
337
+ "label": "Draft",
338
+ "when": {
339
+ "draft": true
340
+ }
341
+ },
342
+ {
343
+ "label": "Scheduled",
344
+ "when": {
345
+ "publishDate": {
346
+ "$gt": {
347
+ "$now": true
348
+ }
349
+ }
350
+ }
351
+ }
352
+ ]
353
+ ]
354
+ },
136
355
  "idField": {
137
356
  "description": "Frontmatter field whose value is the entry id, instead of the path-derived id. An entry without the field keeps its path id and is reported.",
138
357
  "type": "string",