@atscript/typescript 0.0.27 → 0.0.29
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/cli.cjs +712 -33
- package/dist/index.cjs +599 -369
- package/dist/index.d.ts +11 -2
- package/dist/index.mjs +599 -370
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -29,7 +29,7 @@ const __atscript_core = __toESM(require("@atscript/core"));
|
|
|
29
29
|
const fs = __toESM(require("fs"));
|
|
30
30
|
|
|
31
31
|
//#region packages/typescript/src/codegen/code-printer.ts
|
|
32
|
-
function _define_property$
|
|
32
|
+
function _define_property$5(obj, key, value) {
|
|
33
33
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
34
34
|
value,
|
|
35
35
|
enumerable: true,
|
|
@@ -122,17 +122,17 @@ else this.write(closing);
|
|
|
122
122
|
return " ".repeat(this.indentLevel * this.indentSize);
|
|
123
123
|
}
|
|
124
124
|
constructor() {
|
|
125
|
-
_define_property$
|
|
126
|
-
_define_property$
|
|
127
|
-
_define_property$
|
|
128
|
-
_define_property$
|
|
129
|
-
_define_property$
|
|
125
|
+
_define_property$5(this, "lines", []);
|
|
126
|
+
_define_property$5(this, "currentLine", "");
|
|
127
|
+
_define_property$5(this, "indentLevel", 0);
|
|
128
|
+
_define_property$5(this, "indentSize", 2);
|
|
129
|
+
_define_property$5(this, "blockStack", []);
|
|
130
130
|
}
|
|
131
131
|
};
|
|
132
132
|
|
|
133
133
|
//#endregion
|
|
134
134
|
//#region packages/typescript/src/codegen/base-renderer.ts
|
|
135
|
-
function _define_property$
|
|
135
|
+
function _define_property$4(obj, key, value) {
|
|
136
136
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
137
137
|
value,
|
|
138
138
|
enumerable: true,
|
|
@@ -154,7 +154,7 @@ var BaseRenderer = class extends CodePrinter {
|
|
|
154
154
|
renderInterface(node) {}
|
|
155
155
|
renderType(node) {}
|
|
156
156
|
transformFromPath(path$3) {
|
|
157
|
-
return path$3
|
|
157
|
+
return `${path$3}.as`;
|
|
158
158
|
}
|
|
159
159
|
renderImport(node) {
|
|
160
160
|
const def = node.getDefinition();
|
|
@@ -191,7 +191,7 @@ var BaseRenderer = class extends CodePrinter {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
constructor(doc) {
|
|
194
|
-
super(), _define_property$
|
|
194
|
+
super(), _define_property$4(this, "doc", void 0), _define_property$4(this, "unused", void 0), this.doc = doc;
|
|
195
195
|
this.unused = new Set(this.doc.getUnusedTokens().map((t) => t.text));
|
|
196
196
|
}
|
|
197
197
|
};
|
|
@@ -209,7 +209,17 @@ function escapeQuotes(str) {
|
|
|
209
209
|
|
|
210
210
|
//#endregion
|
|
211
211
|
//#region packages/typescript/src/codegen/type-renderer.ts
|
|
212
|
-
|
|
212
|
+
function _define_property$3(obj, key, value) {
|
|
213
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
214
|
+
value,
|
|
215
|
+
enumerable: true,
|
|
216
|
+
configurable: true,
|
|
217
|
+
writable: true
|
|
218
|
+
});
|
|
219
|
+
else obj[key] = value;
|
|
220
|
+
return obj;
|
|
221
|
+
}
|
|
222
|
+
var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
213
223
|
pre() {
|
|
214
224
|
this.writeln("// prettier-ignore-start");
|
|
215
225
|
this.writeln("/* eslint-disable */");
|
|
@@ -224,6 +234,11 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
224
234
|
post() {
|
|
225
235
|
this.writeln("// prettier-ignore-end");
|
|
226
236
|
}
|
|
237
|
+
renderTypeDefString(def) {
|
|
238
|
+
const newThis = new TypeRenderer(this.doc, this.opts);
|
|
239
|
+
newThis.renderTypeDef(def);
|
|
240
|
+
return newThis.toString();
|
|
241
|
+
}
|
|
227
242
|
renderTypeDef(def) {
|
|
228
243
|
if (!def) {
|
|
229
244
|
this.write("unknown");
|
|
@@ -272,33 +287,33 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
272
287
|
renderStructure(struct, asClass) {
|
|
273
288
|
this.blockln("{}");
|
|
274
289
|
const patterns = [];
|
|
275
|
-
|
|
290
|
+
const propsDefs = new Set();
|
|
276
291
|
for (const prop of Array.from(struct.props.values())) {
|
|
277
292
|
if (prop.token("identifier")?.pattern) {
|
|
278
293
|
patterns.push(prop);
|
|
279
294
|
continue;
|
|
280
295
|
}
|
|
281
|
-
hasProp = true;
|
|
282
296
|
const optional = !!prop.token("optional");
|
|
283
297
|
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
284
|
-
this.
|
|
285
|
-
|
|
298
|
+
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
299
|
+
propsDefs.add(renderedDef);
|
|
300
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
286
301
|
}
|
|
287
302
|
if (patterns.length) {
|
|
288
303
|
this.write(`[key: string]: `);
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
304
|
+
if (patterns.length > 0) {
|
|
305
|
+
for (const prop of patterns) propsDefs.add(this.renderTypeDefString(prop.getDefinition()));
|
|
306
|
+
const defs = Array.from(propsDefs);
|
|
307
|
+
if (defs.length > 1) {
|
|
308
|
+
this.indent();
|
|
309
|
+
for (const def of defs) {
|
|
310
|
+
this.writeln();
|
|
311
|
+
this.write("| ");
|
|
312
|
+
def.split("\n").forEach((l) => this.write(l.trim()));
|
|
313
|
+
}
|
|
314
|
+
this.unindent();
|
|
296
315
|
this.writeln();
|
|
297
|
-
|
|
298
|
-
this.renderTypeDef(prop.getDefinition());
|
|
299
|
-
}
|
|
300
|
-
this.unindent();
|
|
301
|
-
this.writeln();
|
|
316
|
+
} else defs[0].split("\n").forEach((l) => this.writeln(l));
|
|
302
317
|
}
|
|
303
318
|
}
|
|
304
319
|
if (asClass) {
|
|
@@ -306,6 +321,7 @@ else if (patterns.length === 1) {
|
|
|
306
321
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
307
322
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
308
323
|
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
324
|
+
this.writeln("static toJsonSchema: () => any");
|
|
309
325
|
}
|
|
310
326
|
this.pop();
|
|
311
327
|
}
|
|
@@ -348,6 +364,7 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
|
|
|
348
364
|
this.writeln(`const type: ${typeDef}`);
|
|
349
365
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
350
366
|
this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
367
|
+
this.writeln("const toJsonSchema: () => any");
|
|
351
368
|
this.popln();
|
|
352
369
|
}
|
|
353
370
|
renderJsDoc(node) {
|
|
@@ -358,6 +375,9 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
|
|
|
358
375
|
this.writeln(` * @see {@link ./${this.doc.name}${rangeStr}}`);
|
|
359
376
|
this.writeln(` */`);
|
|
360
377
|
}
|
|
378
|
+
constructor(doc, opts) {
|
|
379
|
+
super(doc), _define_property$3(this, "opts", void 0), this.opts = opts;
|
|
380
|
+
}
|
|
361
381
|
};
|
|
362
382
|
function renderPrimitiveTypeDef(def) {
|
|
363
383
|
if (!def) return "unknown";
|
|
@@ -376,6 +396,537 @@ function renderPrimitiveTypeDef(def) {
|
|
|
376
396
|
}
|
|
377
397
|
}
|
|
378
398
|
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region packages/typescript/src/validator.ts
|
|
401
|
+
function _define_property$2(obj, key, value) {
|
|
402
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
403
|
+
value,
|
|
404
|
+
enumerable: true,
|
|
405
|
+
configurable: true,
|
|
406
|
+
writable: true
|
|
407
|
+
});
|
|
408
|
+
else obj[key] = value;
|
|
409
|
+
return obj;
|
|
410
|
+
}
|
|
411
|
+
const regexCache = new Map();
|
|
412
|
+
var Validator = class {
|
|
413
|
+
isLimitExceeded() {
|
|
414
|
+
if (this.stackErrors.length > 0) return this.stackErrors[this.stackErrors.length - 1].length >= this.opts.errorLimit;
|
|
415
|
+
return this.errors.length >= this.opts.errorLimit;
|
|
416
|
+
}
|
|
417
|
+
push(name) {
|
|
418
|
+
this.stackPath.push(name);
|
|
419
|
+
this.stackErrors.push([]);
|
|
420
|
+
}
|
|
421
|
+
pop(saveErrors) {
|
|
422
|
+
this.stackPath.pop();
|
|
423
|
+
const popped = this.stackErrors.pop();
|
|
424
|
+
if (saveErrors && popped?.length) popped.forEach((error) => {
|
|
425
|
+
this.error(error.message, error.path, error.details);
|
|
426
|
+
});
|
|
427
|
+
return popped;
|
|
428
|
+
}
|
|
429
|
+
clear() {
|
|
430
|
+
this.stackErrors[this.stackErrors.length - 1] = [];
|
|
431
|
+
}
|
|
432
|
+
error(message, path$3, details) {
|
|
433
|
+
const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
|
|
434
|
+
const error = {
|
|
435
|
+
path: path$3 || this.path,
|
|
436
|
+
message
|
|
437
|
+
};
|
|
438
|
+
if (details?.length) error.details = details;
|
|
439
|
+
errors.push(error);
|
|
440
|
+
}
|
|
441
|
+
throw() {
|
|
442
|
+
throw new ValidatorError(this.errors);
|
|
443
|
+
}
|
|
444
|
+
validate(value, safe) {
|
|
445
|
+
this.push("");
|
|
446
|
+
this.errors = [];
|
|
447
|
+
this.stackErrors = [];
|
|
448
|
+
const passed = this.validateSafe(this.def, value);
|
|
449
|
+
this.pop(!passed);
|
|
450
|
+
if (!passed) {
|
|
451
|
+
if (safe) return false;
|
|
452
|
+
this.throw();
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
validateSafe(def, value) {
|
|
457
|
+
if (this.isLimitExceeded()) return false;
|
|
458
|
+
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
459
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
|
|
460
|
+
if (def.optional && value === undefined) return true;
|
|
461
|
+
for (const plugin of this.opts.plugins) {
|
|
462
|
+
const result = plugin(this, def, value);
|
|
463
|
+
if (result === false || result === true) return result;
|
|
464
|
+
}
|
|
465
|
+
return this.validateAnnotatedType(def, value);
|
|
466
|
+
}
|
|
467
|
+
get path() {
|
|
468
|
+
return this.stackPath.slice(1).join(".");
|
|
469
|
+
}
|
|
470
|
+
validateAnnotatedType(def, value) {
|
|
471
|
+
switch (def.type.kind) {
|
|
472
|
+
case "object": return this.validateObject(def, value);
|
|
473
|
+
case "union": return this.validateUnion(def, value);
|
|
474
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
475
|
+
case "tuple": return this.validateTuple(def, value);
|
|
476
|
+
case "array": return this.validateArray(def, value);
|
|
477
|
+
case "": return this.validatePrimitive(def, value);
|
|
478
|
+
default: throw new Error(`Unknown type "${def.type.kind}"`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
validateUnion(def, value) {
|
|
482
|
+
let i = 0;
|
|
483
|
+
const popped = [];
|
|
484
|
+
for (const item of def.type.items) {
|
|
485
|
+
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
486
|
+
if (this.validateSafe(item, value)) {
|
|
487
|
+
this.pop(false);
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
const errors = this.pop(false);
|
|
491
|
+
if (errors) popped.push(...errors);
|
|
492
|
+
i++;
|
|
493
|
+
}
|
|
494
|
+
this.clear();
|
|
495
|
+
const expected = def.type.items.map((item, i$1) => `[${item.type.kind || item.type.designType}(${i$1})]`).join(", ");
|
|
496
|
+
this.error(`Value does not match any of the allowed types: ${expected}`, undefined, popped);
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
validateIntersection(def, value) {
|
|
500
|
+
for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
validateTuple(def, value) {
|
|
504
|
+
if (!Array.isArray(value) || value.length !== def.type.items.length) {
|
|
505
|
+
this.error("Expected array of length " + def.type.items.length);
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
let i = 0;
|
|
509
|
+
for (const item of def.type.items) {
|
|
510
|
+
this.push(`[${i}]`);
|
|
511
|
+
if (!this.validateSafe(item, value[i])) {
|
|
512
|
+
this.pop(true);
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
this.pop(false);
|
|
516
|
+
i++;
|
|
517
|
+
}
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
validateArray(def, value) {
|
|
521
|
+
if (!Array.isArray(value)) {
|
|
522
|
+
this.error("Expected array");
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
const minLength = def.metadata.get("expect.minLength");
|
|
526
|
+
if (typeof minLength === "number" && value.length < minLength) {
|
|
527
|
+
this.error(`Expected minimum length of ${minLength} items, got ${value.length} items`);
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
const maxLength = def.metadata.get("expect.maxLength");
|
|
531
|
+
if (typeof maxLength === "number" && value.length > maxLength) {
|
|
532
|
+
this.error(`Expected maximum length of ${maxLength} items, got ${value.length} items`);
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
let i = 0;
|
|
536
|
+
let passed = true;
|
|
537
|
+
for (const item of value) {
|
|
538
|
+
this.push(`[${i}]`);
|
|
539
|
+
if (!this.validateSafe(def.type.of, item)) {
|
|
540
|
+
passed = false;
|
|
541
|
+
this.pop(true);
|
|
542
|
+
if (this.isLimitExceeded()) return false;
|
|
543
|
+
} else this.pop(false);
|
|
544
|
+
i++;
|
|
545
|
+
}
|
|
546
|
+
return passed;
|
|
547
|
+
}
|
|
548
|
+
validateObject(def, value) {
|
|
549
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
550
|
+
this.error("Expected object");
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
let passed = true;
|
|
554
|
+
const valueKeys = new Set(Object.keys(value));
|
|
555
|
+
const typeKeys = new Set();
|
|
556
|
+
const skipList = new Set();
|
|
557
|
+
if (this.opts.skipList) {
|
|
558
|
+
const path$3 = this.stackPath.length > 1 ? this.path + "." : "";
|
|
559
|
+
this.opts.skipList.forEach((item) => {
|
|
560
|
+
if (item.startsWith(path$3)) {
|
|
561
|
+
const key = item.slice(path$3.length);
|
|
562
|
+
skipList.add(key);
|
|
563
|
+
valueKeys.delete(key);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
let partialFunctionMatched = false;
|
|
568
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
|
|
569
|
+
for (const [key, item] of def.type.props.entries()) {
|
|
570
|
+
if (skipList.has(key)) continue;
|
|
571
|
+
typeKeys.add(key);
|
|
572
|
+
if (value[key] === undefined) {
|
|
573
|
+
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
574
|
+
}
|
|
575
|
+
this.push(key);
|
|
576
|
+
if (this.validateSafe(item, value[key])) this.pop(false);
|
|
577
|
+
else {
|
|
578
|
+
passed = false;
|
|
579
|
+
this.pop(true);
|
|
580
|
+
if (this.isLimitExceeded()) return false;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const key of valueKeys)
|
|
584
|
+
/** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
|
|
585
|
+
const matched = [];
|
|
586
|
+
for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
|
|
587
|
+
pattern,
|
|
588
|
+
def: propDef
|
|
589
|
+
});
|
|
590
|
+
if (matched.length) {
|
|
591
|
+
let keyPassed = false;
|
|
592
|
+
for (const { def: def$1 } of matched) if (this.validateSafe(def$1, value[key])) {
|
|
593
|
+
this.pop(false);
|
|
594
|
+
keyPassed = true;
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
if (!keyPassed) {
|
|
598
|
+
this.push(key);
|
|
599
|
+
this.validateSafe(matched[0].def, value[key]);
|
|
600
|
+
this.pop(true);
|
|
601
|
+
passed = false;
|
|
602
|
+
if (this.isLimitExceeded()) return false;
|
|
603
|
+
}
|
|
604
|
+
} else if (this.opts.unknwonProps !== "ignore") {
|
|
605
|
+
if (this.opts.unknwonProps === "error") {
|
|
606
|
+
this.push(key);
|
|
607
|
+
this.error(`Unexpected property`);
|
|
608
|
+
this.pop(true);
|
|
609
|
+
if (this.isLimitExceeded()) return false;
|
|
610
|
+
passed = false;
|
|
611
|
+
} else if (this.opts.unknwonProps === "strip") delete value[key];
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return passed;
|
|
615
|
+
}
|
|
616
|
+
validatePrimitive(def, value) {
|
|
617
|
+
if (typeof def.type.value !== "undefined") {
|
|
618
|
+
if (value !== def.type.value) {
|
|
619
|
+
this.error(`Expected ${def.type.value}, got ${value}`);
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
const typeOfValue = Array.isArray(value) ? "array" : typeof value;
|
|
625
|
+
switch (def.type.designType) {
|
|
626
|
+
case "never":
|
|
627
|
+
this.error(`This type is impossible, must be an internal problem`);
|
|
628
|
+
return false;
|
|
629
|
+
case "any": return true;
|
|
630
|
+
case "string":
|
|
631
|
+
if (typeOfValue !== def.type.designType) {
|
|
632
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
return this.validateString(def, value);
|
|
636
|
+
case "number":
|
|
637
|
+
if (typeOfValue !== def.type.designType) {
|
|
638
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
return this.validateNumber(def, value);
|
|
642
|
+
case "boolean":
|
|
643
|
+
if (typeOfValue !== def.type.designType) {
|
|
644
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
case "undefined":
|
|
649
|
+
if (value !== undefined) {
|
|
650
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
return true;
|
|
654
|
+
case "null":
|
|
655
|
+
if (value !== null) {
|
|
656
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
return true;
|
|
660
|
+
default: throw new Error(`Unknown type "${def.type.designType}"`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
validateString(def, value) {
|
|
664
|
+
const minLength = def.metadata.get("expect.minLength");
|
|
665
|
+
if (typeof minLength === "number" && value.length < minLength) {
|
|
666
|
+
this.error(`Expected minimum length of ${minLength} characters, got ${value.length} characters`);
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
const maxLength = def.metadata.get("expect.maxLength");
|
|
670
|
+
if (typeof maxLength === "number" && value.length > maxLength) {
|
|
671
|
+
this.error(`Expected maximum length of ${maxLength} characters, got ${value.length} characters`);
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
const patterns = def.metadata.get("expect.pattern");
|
|
675
|
+
for (const { pattern, flags, message } of patterns || []) {
|
|
676
|
+
if (!pattern) continue;
|
|
677
|
+
const cacheKey = `${pattern}//${flags || ""}`;
|
|
678
|
+
let regex = regexCache.get(cacheKey);
|
|
679
|
+
if (!regex) {
|
|
680
|
+
regex = new RegExp(pattern, flags);
|
|
681
|
+
regexCache.set(cacheKey, regex);
|
|
682
|
+
}
|
|
683
|
+
if (!regex.test(value)) {
|
|
684
|
+
this.error(message || `Value is expected to match pattern "${pattern}"`);
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
validateNumber(def, value) {
|
|
691
|
+
const int = def.metadata.get("expect.int");
|
|
692
|
+
if (typeof int === "boolean" && int && value % 1 !== 0) {
|
|
693
|
+
this.error(`Expected integer, got ${value}`);
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
const min = def.metadata.get("expect.min");
|
|
697
|
+
if (typeof min === "number" && value < min) {
|
|
698
|
+
this.error(`Expected minimum ${min}, got ${value}`);
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
const max = def.metadata.get("expect.max");
|
|
702
|
+
if (typeof max === "number" && value > max) {
|
|
703
|
+
this.error(`Expected maximum ${max}, got ${value}`);
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
constructor(def, opts) {
|
|
709
|
+
_define_property$2(this, "def", void 0);
|
|
710
|
+
_define_property$2(this, "opts", void 0);
|
|
711
|
+
_define_property$2(this, "errors", void 0);
|
|
712
|
+
_define_property$2(this, "stackErrors", void 0);
|
|
713
|
+
_define_property$2(this, "stackPath", void 0);
|
|
714
|
+
this.def = def;
|
|
715
|
+
this.errors = [];
|
|
716
|
+
this.stackErrors = [];
|
|
717
|
+
this.stackPath = [];
|
|
718
|
+
this.opts = {
|
|
719
|
+
partial: false,
|
|
720
|
+
unknwonProps: "error",
|
|
721
|
+
errorLimit: 10,
|
|
722
|
+
...opts,
|
|
723
|
+
plugins: opts?.plugins || []
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
var ValidatorError = class extends Error {
|
|
728
|
+
constructor(errors) {
|
|
729
|
+
super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property$2(this, "errors", void 0), _define_property$2(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region packages/typescript/src/annotated-type.ts
|
|
735
|
+
function isAnnotatedType(type) {
|
|
736
|
+
return type && type.__is_atscript_annotated_type;
|
|
737
|
+
}
|
|
738
|
+
function defineAnnotatedType(_kind, base) {
|
|
739
|
+
const kind = _kind || "";
|
|
740
|
+
const type = base?.type || {};
|
|
741
|
+
type.kind = kind;
|
|
742
|
+
if ([
|
|
743
|
+
"union",
|
|
744
|
+
"intersection",
|
|
745
|
+
"tuple"
|
|
746
|
+
].includes(kind)) type.items = [];
|
|
747
|
+
if (kind === "object") {
|
|
748
|
+
type.props = new Map();
|
|
749
|
+
type.propsPatterns = [];
|
|
750
|
+
}
|
|
751
|
+
type.tags = new Set();
|
|
752
|
+
const metadata = base?.metadata || new Map();
|
|
753
|
+
if (base) Object.assign(base, {
|
|
754
|
+
__is_atscript_annotated_type: true,
|
|
755
|
+
metadata,
|
|
756
|
+
type,
|
|
757
|
+
validator(opts) {
|
|
758
|
+
return new Validator(this, opts);
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
else base = {
|
|
762
|
+
__is_atscript_annotated_type: true,
|
|
763
|
+
metadata,
|
|
764
|
+
type,
|
|
765
|
+
validator(opts) {
|
|
766
|
+
return new Validator(this, opts);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
const handle = {
|
|
770
|
+
$type: base,
|
|
771
|
+
$def: type,
|
|
772
|
+
$metadata: metadata,
|
|
773
|
+
_existingObject: undefined,
|
|
774
|
+
tags(...tags) {
|
|
775
|
+
for (const tag of tags) this.$def.tags.add(tag);
|
|
776
|
+
return this;
|
|
777
|
+
},
|
|
778
|
+
designType(value) {
|
|
779
|
+
this.$def.designType = value;
|
|
780
|
+
return this;
|
|
781
|
+
},
|
|
782
|
+
value(value) {
|
|
783
|
+
this.$def.value = value;
|
|
784
|
+
return this;
|
|
785
|
+
},
|
|
786
|
+
of(value) {
|
|
787
|
+
this.$def.of = value;
|
|
788
|
+
return this;
|
|
789
|
+
},
|
|
790
|
+
item(value) {
|
|
791
|
+
this.$def.items.push(value);
|
|
792
|
+
return this;
|
|
793
|
+
},
|
|
794
|
+
prop(name, value) {
|
|
795
|
+
this.$def.props.set(name, value);
|
|
796
|
+
return this;
|
|
797
|
+
},
|
|
798
|
+
propPattern(pattern, def) {
|
|
799
|
+
this.$def.propsPatterns.push({
|
|
800
|
+
pattern,
|
|
801
|
+
def
|
|
802
|
+
});
|
|
803
|
+
return this;
|
|
804
|
+
},
|
|
805
|
+
optional(value = true) {
|
|
806
|
+
this.$type.optional = value;
|
|
807
|
+
return this;
|
|
808
|
+
},
|
|
809
|
+
copyMetadata(fromMetadata, ignore) {
|
|
810
|
+
for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
|
|
811
|
+
return this;
|
|
812
|
+
},
|
|
813
|
+
refTo(type$1, chain) {
|
|
814
|
+
let newBase = type$1;
|
|
815
|
+
const typeName = type$1.name || "Unknown";
|
|
816
|
+
if (isAnnotatedType(newBase)) {
|
|
817
|
+
let keys = "";
|
|
818
|
+
for (const c of chain || []) {
|
|
819
|
+
keys += `["${c}"]`;
|
|
820
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
821
|
+
else throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
822
|
+
}
|
|
823
|
+
if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
824
|
+
else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
825
|
+
this.$type = {
|
|
826
|
+
__is_atscript_annotated_type: true,
|
|
827
|
+
type: newBase.type,
|
|
828
|
+
metadata,
|
|
829
|
+
validator(opts) {
|
|
830
|
+
return new Validator(this, opts);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
} else throw new Error(`${type$1} is not annotated type`);
|
|
834
|
+
return this;
|
|
835
|
+
},
|
|
836
|
+
annotate(key, value, asArray) {
|
|
837
|
+
if (asArray) if (this.$metadata.has(key)) {
|
|
838
|
+
const a = this.$metadata.get(key);
|
|
839
|
+
if (Array.isArray(a)) a.push(value);
|
|
840
|
+
else this.$metadata.set(key, [a, value]);
|
|
841
|
+
} else this.$metadata.set(key, [value]);
|
|
842
|
+
else this.$metadata.set(key, value);
|
|
843
|
+
return this;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
return handle;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region packages/typescript/src/json-schema.ts
|
|
851
|
+
function buildJsonSchema(type) {
|
|
852
|
+
const build$1 = (def) => {
|
|
853
|
+
const t = def.type;
|
|
854
|
+
const meta = def.metadata;
|
|
855
|
+
switch (t.kind) {
|
|
856
|
+
case "object": {
|
|
857
|
+
const obj = t;
|
|
858
|
+
const properties = {};
|
|
859
|
+
const required = [];
|
|
860
|
+
for (const [key, val] of obj.props.entries()) {
|
|
861
|
+
properties[key] = build$1(val);
|
|
862
|
+
if (!val.optional) required.push(key);
|
|
863
|
+
}
|
|
864
|
+
const schema = {
|
|
865
|
+
type: "object",
|
|
866
|
+
properties
|
|
867
|
+
};
|
|
868
|
+
if (required.length) schema.required = required;
|
|
869
|
+
return schema;
|
|
870
|
+
}
|
|
871
|
+
case "array": {
|
|
872
|
+
const arr = t;
|
|
873
|
+
const schema = {
|
|
874
|
+
type: "array",
|
|
875
|
+
items: build$1(arr.of)
|
|
876
|
+
};
|
|
877
|
+
const minLength = meta.get("expect.minLength");
|
|
878
|
+
if (typeof minLength === "number") schema.minItems = minLength;
|
|
879
|
+
const maxLength = meta.get("expect.maxLength");
|
|
880
|
+
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
881
|
+
return schema;
|
|
882
|
+
}
|
|
883
|
+
case "union": {
|
|
884
|
+
const grp = t;
|
|
885
|
+
return { anyOf: grp.items.map(build$1) };
|
|
886
|
+
}
|
|
887
|
+
case "intersection": {
|
|
888
|
+
const grp = t;
|
|
889
|
+
return { allOf: grp.items.map(build$1) };
|
|
890
|
+
}
|
|
891
|
+
case "tuple": {
|
|
892
|
+
const grp = t;
|
|
893
|
+
return {
|
|
894
|
+
type: "array",
|
|
895
|
+
items: grp.items.map(build$1),
|
|
896
|
+
additionalItems: false
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
case "": {
|
|
900
|
+
const fin = t;
|
|
901
|
+
const schema = {};
|
|
902
|
+
if (fin.value !== undefined) schema.const = fin.value;
|
|
903
|
+
if (fin.designType && fin.designType !== "any") {
|
|
904
|
+
schema.type = fin.designType === "undefined" ? "null" : fin.designType;
|
|
905
|
+
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
906
|
+
}
|
|
907
|
+
if (schema.type === "string") {
|
|
908
|
+
const minLength = meta.get("expect.minLength");
|
|
909
|
+
if (typeof minLength === "number") schema.minLength = minLength;
|
|
910
|
+
const maxLength = meta.get("expect.maxLength");
|
|
911
|
+
if (typeof maxLength === "number") schema.maxLength = maxLength;
|
|
912
|
+
const patterns = meta.get("expect.pattern");
|
|
913
|
+
if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
|
|
914
|
+
else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
915
|
+
}
|
|
916
|
+
if (schema.type === "number" || schema.type === "integer") {
|
|
917
|
+
const min = meta.get("expect.min");
|
|
918
|
+
if (typeof min === "number") schema.minimum = min;
|
|
919
|
+
const max = meta.get("expect.max");
|
|
920
|
+
if (typeof max === "number") schema.maximum = max;
|
|
921
|
+
}
|
|
922
|
+
return schema;
|
|
923
|
+
}
|
|
924
|
+
default: return {};
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
return build$1(type);
|
|
928
|
+
}
|
|
929
|
+
|
|
379
930
|
//#endregion
|
|
380
931
|
//#region packages/typescript/src/codegen/js-renderer.ts
|
|
381
932
|
function _define_property$1(obj, key, value) {
|
|
@@ -392,7 +943,9 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
392
943
|
pre() {
|
|
393
944
|
this.writeln("// prettier-ignore-start");
|
|
394
945
|
this.writeln("/* eslint-disable */");
|
|
395
|
-
|
|
946
|
+
const imports = ["defineAnnotatedType as $"];
|
|
947
|
+
if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
|
|
948
|
+
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript"`);
|
|
396
949
|
}
|
|
397
950
|
post() {
|
|
398
951
|
for (const node of this.postAnnotate) {
|
|
@@ -412,6 +965,18 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
412
965
|
this.writeln("static __is_atscript_annotated_type = true");
|
|
413
966
|
this.writeln("static type = {}");
|
|
414
967
|
this.writeln("static metadata = new Map()");
|
|
968
|
+
if (this.opts?.preRenderJsonSchema) {
|
|
969
|
+
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
970
|
+
this.writeln(`static _jsonSchema = ${schema}`);
|
|
971
|
+
this.writeln("static toJsonSchema() {");
|
|
972
|
+
this.indent().writeln("return this._jsonSchema").unindent();
|
|
973
|
+
this.writeln("}");
|
|
974
|
+
} else {
|
|
975
|
+
this.writeln("static _jsonSchema");
|
|
976
|
+
this.writeln("static toJsonSchema() {");
|
|
977
|
+
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
978
|
+
this.writeln("}");
|
|
979
|
+
}
|
|
415
980
|
this.popln();
|
|
416
981
|
this.postAnnotate.push(node);
|
|
417
982
|
this.writeln();
|
|
@@ -425,10 +990,125 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
425
990
|
this.writeln("static __is_atscript_annotated_type = true");
|
|
426
991
|
this.writeln("static type = {}");
|
|
427
992
|
this.writeln("static metadata = new Map()");
|
|
993
|
+
if (this.opts?.jsonSchema) if (typeof this.opts.jsonSchema === "object" && this.opts.jsonSchema.preRender) {
|
|
994
|
+
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
995
|
+
this.writeln(`static _jsonSchema = ${schema}`);
|
|
996
|
+
this.writeln("static toJsonSchema() {");
|
|
997
|
+
this.indent().writeln("return this._jsonSchema").unindent();
|
|
998
|
+
this.writeln("}");
|
|
999
|
+
} else {
|
|
1000
|
+
this.writeln("static _jsonSchema");
|
|
1001
|
+
this.writeln("static toJsonSchema() {");
|
|
1002
|
+
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1003
|
+
this.writeln("}");
|
|
1004
|
+
}
|
|
428
1005
|
this.popln();
|
|
429
1006
|
this.postAnnotate.push(node);
|
|
430
1007
|
this.writeln();
|
|
431
1008
|
}
|
|
1009
|
+
toAnnotatedType(node) {
|
|
1010
|
+
return this.toAnnotatedHandle(node).$type;
|
|
1011
|
+
}
|
|
1012
|
+
toAnnotatedHandle(node, skipAnnotations = false) {
|
|
1013
|
+
if (!node) return defineAnnotatedType();
|
|
1014
|
+
switch (node.entity) {
|
|
1015
|
+
case "interface":
|
|
1016
|
+
case "type": {
|
|
1017
|
+
const def = node.getDefinition();
|
|
1018
|
+
const handle = this.toAnnotatedHandle(def, true);
|
|
1019
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1020
|
+
}
|
|
1021
|
+
case "prop": {
|
|
1022
|
+
const prop = node;
|
|
1023
|
+
const def = prop.getDefinition();
|
|
1024
|
+
const handle = this.toAnnotatedHandle(def, true);
|
|
1025
|
+
if (!skipAnnotations) {
|
|
1026
|
+
this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(prop));
|
|
1027
|
+
if (prop.token("optional")) handle.optional();
|
|
1028
|
+
}
|
|
1029
|
+
return handle;
|
|
1030
|
+
}
|
|
1031
|
+
case "ref": {
|
|
1032
|
+
const ref = node;
|
|
1033
|
+
const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1034
|
+
const handle = this.toAnnotatedHandle(decl, true);
|
|
1035
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1036
|
+
}
|
|
1037
|
+
case "primitive": {
|
|
1038
|
+
const prim = node;
|
|
1039
|
+
const handle = defineAnnotatedType();
|
|
1040
|
+
handle.designType(prim.id === "never" ? "never" : prim.config.type);
|
|
1041
|
+
if (!skipAnnotations) this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1042
|
+
return handle;
|
|
1043
|
+
}
|
|
1044
|
+
case "const": {
|
|
1045
|
+
const c = node;
|
|
1046
|
+
const handle = defineAnnotatedType();
|
|
1047
|
+
const t = c.token("identifier")?.type;
|
|
1048
|
+
handle.designType(t === "number" ? "number" : "string");
|
|
1049
|
+
handle.value(t === "number" ? Number(c.id) : c.id);
|
|
1050
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1051
|
+
}
|
|
1052
|
+
case "structure": {
|
|
1053
|
+
const struct = node;
|
|
1054
|
+
const handle = defineAnnotatedType("object");
|
|
1055
|
+
for (const prop of Array.from(struct.props.values())) {
|
|
1056
|
+
const propHandle = this.toAnnotatedHandle(prop);
|
|
1057
|
+
const pattern = prop.token("identifier")?.pattern;
|
|
1058
|
+
if (pattern) handle.propPattern(pattern, propHandle.$type);
|
|
1059
|
+
else handle.prop(prop.id, propHandle.$type);
|
|
1060
|
+
}
|
|
1061
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1062
|
+
}
|
|
1063
|
+
case "group": {
|
|
1064
|
+
const group = node;
|
|
1065
|
+
const kind = group.op === "|" ? "union" : "intersection";
|
|
1066
|
+
const handle = defineAnnotatedType(kind);
|
|
1067
|
+
for (const item of group.unwrap()) handle.item(this.toAnnotatedHandle(item).$type);
|
|
1068
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1069
|
+
}
|
|
1070
|
+
case "tuple": {
|
|
1071
|
+
const group = node;
|
|
1072
|
+
const handle = defineAnnotatedType("tuple");
|
|
1073
|
+
for (const item of group.unwrap()) handle.item(this.toAnnotatedHandle(item).$type);
|
|
1074
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1075
|
+
}
|
|
1076
|
+
case "array": {
|
|
1077
|
+
const arr = node;
|
|
1078
|
+
const handle = defineAnnotatedType("array");
|
|
1079
|
+
handle.of(this.toAnnotatedHandle(arr.getDefinition()).$type);
|
|
1080
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1081
|
+
}
|
|
1082
|
+
default: {
|
|
1083
|
+
const handle = defineAnnotatedType();
|
|
1084
|
+
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
applyExpectAnnotations(handle, annotations) {
|
|
1089
|
+
annotations?.forEach((a) => {
|
|
1090
|
+
switch (a.name) {
|
|
1091
|
+
case "expect.minLength":
|
|
1092
|
+
case "expect.maxLength":
|
|
1093
|
+
case "expect.min":
|
|
1094
|
+
case "expect.max":
|
|
1095
|
+
if (a.args[0]) handle.annotate(a.name, Number(a.args[0].text));
|
|
1096
|
+
break;
|
|
1097
|
+
case "expect.pattern":
|
|
1098
|
+
handle.annotate(a.name, {
|
|
1099
|
+
pattern: a.args[0]?.text || "",
|
|
1100
|
+
flags: a.args[1]?.text,
|
|
1101
|
+
message: a.args[2]?.text
|
|
1102
|
+
}, true);
|
|
1103
|
+
break;
|
|
1104
|
+
case "expect.int":
|
|
1105
|
+
handle.annotate(a.name, true);
|
|
1106
|
+
break;
|
|
1107
|
+
default:
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
return handle;
|
|
1111
|
+
}
|
|
432
1112
|
annotateType(_node, name) {
|
|
433
1113
|
if (!_node) return this;
|
|
434
1114
|
const node = this.doc.mergeIntersection(_node);
|
|
@@ -479,7 +1159,6 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
479
1159
|
defineConst(node) {
|
|
480
1160
|
const t = node.token("identifier")?.type;
|
|
481
1161
|
const designType = t === "text" ? "string" : t === "number" ? "number" : "unknown";
|
|
482
|
-
const type = t === "text" ? "String" : t === "number" ? "Number" : "undefined";
|
|
483
1162
|
this.writeln(`.designType("${escapeQuotes(designType)}")`);
|
|
484
1163
|
this.writeln(`.value(${t === "text" ? `"${escapeQuotes(node.id)}"` : node.id})`);
|
|
485
1164
|
return this;
|
|
@@ -619,24 +1298,24 @@ else targetValue = "true";
|
|
|
619
1298
|
if (multiple) this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue}, true)`);
|
|
620
1299
|
else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue})`);
|
|
621
1300
|
}
|
|
622
|
-
constructor(
|
|
623
|
-
super(
|
|
1301
|
+
constructor(doc, opts) {
|
|
1302
|
+
super(doc), _define_property$1(this, "opts", void 0), _define_property$1(this, "postAnnotate", void 0), this.opts = opts, this.postAnnotate = [];
|
|
624
1303
|
}
|
|
625
1304
|
};
|
|
626
1305
|
|
|
627
1306
|
//#endregion
|
|
628
1307
|
//#region packages/typescript/src/plugin.ts
|
|
629
|
-
const tsPlugin = () => {
|
|
1308
|
+
const tsPlugin = (opts) => {
|
|
630
1309
|
return {
|
|
631
1310
|
name: "typesccript",
|
|
632
1311
|
render(doc, format) {
|
|
633
1312
|
if (format === "dts") return [{
|
|
634
1313
|
fileName: `${doc.name}.d.ts`,
|
|
635
|
-
content: new TypeRenderer(doc).render()
|
|
1314
|
+
content: new TypeRenderer(doc, opts).render()
|
|
636
1315
|
}];
|
|
637
1316
|
if (format === "js") return [{
|
|
638
1317
|
fileName: `${doc.name}.js`,
|
|
639
|
-
content: new JsRenderer(doc).render()
|
|
1318
|
+
content: new JsRenderer(doc, opts).render()
|
|
640
1319
|
}];
|
|
641
1320
|
},
|
|
642
1321
|
async buildEnd(output, format, repo) {
|