@atscript/typescript 0.0.26 → 0.0.28

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/index.cjs CHANGED
@@ -27,7 +27,7 @@ const __atscript_core = __toESM(require("@atscript/core"));
27
27
  const path = __toESM(require("path"));
28
28
 
29
29
  //#region packages/typescript/src/codegen/code-printer.ts
30
- function _define_property$3(obj, key, value) {
30
+ function _define_property$4(obj, key, value) {
31
31
  if (key in obj) Object.defineProperty(obj, key, {
32
32
  value,
33
33
  enumerable: true,
@@ -120,17 +120,17 @@ else this.write(closing);
120
120
  return " ".repeat(this.indentLevel * this.indentSize);
121
121
  }
122
122
  constructor() {
123
- _define_property$3(this, "lines", []);
124
- _define_property$3(this, "currentLine", "");
125
- _define_property$3(this, "indentLevel", 0);
126
- _define_property$3(this, "indentSize", 2);
127
- _define_property$3(this, "blockStack", []);
123
+ _define_property$4(this, "lines", []);
124
+ _define_property$4(this, "currentLine", "");
125
+ _define_property$4(this, "indentLevel", 0);
126
+ _define_property$4(this, "indentSize", 2);
127
+ _define_property$4(this, "blockStack", []);
128
128
  }
129
129
  };
130
130
 
131
131
  //#endregion
132
132
  //#region packages/typescript/src/codegen/base-renderer.ts
133
- function _define_property$2(obj, key, value) {
133
+ function _define_property$3(obj, key, value) {
134
134
  if (key in obj) Object.defineProperty(obj, key, {
135
135
  value,
136
136
  enumerable: true,
@@ -189,7 +189,7 @@ var BaseRenderer = class extends CodePrinter {
189
189
  }
190
190
  }
191
191
  constructor(doc) {
192
- super(), _define_property$2(this, "doc", void 0), _define_property$2(this, "unused", void 0), this.doc = doc;
192
+ super(), _define_property$3(this, "doc", void 0), _define_property$3(this, "unused", void 0), this.doc = doc;
193
193
  this.unused = new Set(this.doc.getUnusedTokens().map((t) => t.text));
194
194
  }
195
195
  };
@@ -207,6 +207,16 @@ function escapeQuotes(str) {
207
207
 
208
208
  //#endregion
209
209
  //#region packages/typescript/src/codegen/type-renderer.ts
210
+ function _define_property$2(obj, key, value) {
211
+ if (key in obj) Object.defineProperty(obj, key, {
212
+ value,
213
+ enumerable: true,
214
+ configurable: true,
215
+ writable: true
216
+ });
217
+ else obj[key] = value;
218
+ return obj;
219
+ }
210
220
  var TypeRenderer = class extends BaseRenderer {
211
221
  pre() {
212
222
  this.writeln("// prettier-ignore-start");
@@ -304,6 +314,7 @@ else if (patterns.length === 1) {
304
314
  this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
305
315
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
306
316
  this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
317
+ this.writeln("static toJsonSchema: () => any");
307
318
  }
308
319
  this.pop();
309
320
  }
@@ -346,6 +357,7 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
346
357
  this.writeln(`const type: ${typeDef}`);
347
358
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
348
359
  this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
360
+ if (this.opts?.jsonSchema) this.writeln("const toJsonSchema: () => any");
349
361
  this.popln();
350
362
  }
351
363
  renderJsDoc(node) {
@@ -356,6 +368,9 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
356
368
  this.writeln(` * @see {@link ./${this.doc.name}${rangeStr}}`);
357
369
  this.writeln(` */`);
358
370
  }
371
+ constructor(doc, opts) {
372
+ super(doc), _define_property$2(this, "opts", void 0), this.opts = opts;
373
+ }
359
374
  };
360
375
  function renderPrimitiveTypeDef(def) {
361
376
  if (!def) return "unknown";
@@ -375,7 +390,7 @@ function renderPrimitiveTypeDef(def) {
375
390
  }
376
391
 
377
392
  //#endregion
378
- //#region packages/typescript/src/codegen/js-renderer.ts
393
+ //#region packages/typescript/src/validator.ts
379
394
  function _define_property$1(obj, key, value) {
380
395
  if (key in obj) Object.defineProperty(obj, key, {
381
396
  value,
@@ -386,363 +401,75 @@ function _define_property$1(obj, key, value) {
386
401
  else obj[key] = value;
387
402
  return obj;
388
403
  }
389
- var JsRenderer = class extends BaseRenderer {
390
- pre() {
391
- this.writeln("// prettier-ignore-start");
392
- this.writeln("/* eslint-disable */");
393
- this.writeln("import { defineAnnotatedType as $ } from \"@atscript/typescript\"");
394
- }
395
- post() {
396
- for (const node of this.postAnnotate) {
397
- this.annotateType(node.getDefinition(), node.id);
398
- this.indent().defineMetadata(node).unindent();
399
- this.writeln();
400
- }
401
- this.writeln("// prettier-ignore-end");
402
- super.post();
404
+ const regexCache = new Map();
405
+ var Validator = class {
406
+ isLimitExceeded() {
407
+ if (this.stackErrors.length > 0) return this.stackErrors[this.stackErrors.length - 1].length >= this.opts.errorLimit;
408
+ return this.errors.length >= this.opts.errorLimit;
403
409
  }
404
- renderInterface(node) {
405
- this.writeln();
406
- const exported = node.token("export")?.text === "export";
407
- this.write(exported ? "export " : "");
408
- this.write(`class ${node.id} `);
409
- this.blockln("{}");
410
- this.writeln("static __is_atscript_annotated_type = true");
411
- this.writeln("static type = {}");
412
- this.writeln("static metadata = new Map()");
413
- this.popln();
414
- this.postAnnotate.push(node);
415
- this.writeln();
410
+ push(name) {
411
+ this.stackPath.push(name);
412
+ this.stackErrors.push([]);
416
413
  }
417
- renderType(node) {
418
- this.writeln();
419
- const exported = node.token("export")?.text === "export";
420
- this.write(exported ? "export " : "");
421
- this.write(`class ${node.id} `);
422
- this.blockln("{}");
423
- this.writeln("static __is_atscript_annotated_type = true");
424
- this.writeln("static type = {}");
425
- this.writeln("static metadata = new Map()");
426
- this.popln();
427
- this.postAnnotate.push(node);
428
- this.writeln();
414
+ pop(saveErrors) {
415
+ this.stackPath.pop();
416
+ const popped = this.stackErrors.pop();
417
+ if (saveErrors && popped?.length) popped.forEach((error) => {
418
+ this.error(error.message, error.path, error.details);
419
+ });
420
+ return popped;
429
421
  }
430
- annotateType(_node, name) {
431
- if (!_node) return this;
432
- const node = this.doc.mergeIntersection(_node);
433
- let kind = node.entity;
434
- switch (node.entity) {
435
- case "ref": {
436
- const ref = node;
437
- const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
438
- if ((0, __atscript_core.isPrimitive)(decl)) {
439
- this.annotateType(decl, name);
440
- return this;
441
- }
442
- const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
443
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`).unindent();
444
- return this;
445
- }
446
- case "primitive": {
447
- this.definePrimitive(node, name);
448
- return this;
449
- }
450
- case "const": {
451
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().defineConst(node).unindent();
452
- return this;
453
- }
454
- case "structure": {
455
- this.writeln(`$("object"${name ? `, ${name}` : ""})`).indent().defineObject(node).unindent();
456
- return this;
457
- }
458
- case "group": {
459
- kind = node.op === "|" ? "union" : "intersection";
460
- this.writeln(`$("${kind}"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
461
- return this;
462
- }
463
- case "tuple": {
464
- this.writeln(`$("tuple"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
465
- return this;
466
- }
467
- case "array": {
468
- this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
469
- return this;
470
- }
471
- default: {
472
- console.log("!!!!!!! UNKNOWN ", node.entity);
473
- return this;
474
- }
475
- }
422
+ clear() {
423
+ this.stackErrors[this.stackErrors.length - 1] = [];
476
424
  }
477
- defineConst(node) {
478
- const t = node.token("identifier")?.type;
479
- const designType = t === "text" ? "string" : t === "number" ? "number" : "unknown";
480
- const type = t === "text" ? "String" : t === "number" ? "Number" : "undefined";
481
- this.writeln(`.designType("${escapeQuotes(designType)}")`);
482
- this.writeln(`.value(${t === "text" ? `"${escapeQuotes(node.id)}"` : node.id})`);
483
- return this;
425
+ error(message, path$2, details) {
426
+ const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
427
+ const error = {
428
+ path: path$2 || this.path,
429
+ message
430
+ };
431
+ if (details?.length) error.details = details;
432
+ errors.push(error);
484
433
  }
485
- definePrimitive(node, name) {
486
- this.renderPrimitiveDef(node.id === "never" ? "never" : node.config.type, name);
487
- this.writeln(` .tags(${Array.from(node.tags).map((f) => `"${escapeQuotes(f)}"`).join(", ")})`);
488
- return this;
434
+ throw() {
435
+ throw new ValidatorError(this.errors);
489
436
  }
490
- renderPrimitiveDef(def, name) {
491
- const d = (t) => [`"${t || ""}"`, name].filter(Boolean).join(", ").replace(/^""$/, "");
492
- if (!def) return this.writeln(`$(${d()}).designType("any")`);
493
- if (typeof def === "string") return this.writeln(`$(${d()}).designType("${def === "void" ? "undefined" : def}")`);
494
- switch (def.kind) {
495
- case "final": return this.writeln(`$(${d()}).designType("${def.value === "void" ? "undefined" : def.value}")`);
496
- case "union":
497
- case "intersection":
498
- case "tuple":
499
- this.writeln(`$(${d(def.kind)})`);
500
- this.indent();
501
- for (const itemDef of def.items) {
502
- this.write(`.item(`);
503
- this.indent();
504
- this.renderPrimitiveDef(itemDef);
505
- this.writeln(".$type");
506
- this.unindent();
507
- this.write(`)`);
508
- }
509
- this.unindent();
510
- return;
511
- case "array":
512
- this.writeln(`$(${d("array")})`);
513
- this.indent();
514
- this.write(".of(");
515
- this.indent();
516
- this.renderPrimitiveDef(def.of);
517
- this.writeln(`.$type`);
518
- this.unindent();
519
- this.writeln(`)`);
520
- this.unindent();
521
- return;
522
- case "object":
523
- this.writeln(`$(${d("object")})`);
524
- this.indent();
525
- for (const [key, propDef] of Object.entries(def.props)) {
526
- const optional = typeof propDef === "object" && propDef.optional;
527
- this.writeln(`.prop(`);
528
- this.indent();
529
- this.writeln(`"${escapeQuotes(key)}",`);
530
- this.renderPrimitiveDef(propDef);
531
- if (optional) this.writeln(".optional()");
532
- this.writeln(".$type");
533
- this.unindent();
534
- this.write(`)`);
535
- }
536
- for (const [key, propDef] of Object.entries(def.propsPatterns)) {
537
- const optional = typeof propDef === "object" && propDef.optional;
538
- this.writeln(`.propPattern(`);
539
- this.indent();
540
- this.writeln(`${key},`);
541
- this.renderPrimitiveDef(propDef);
542
- if (optional) this.writeln(".optional()");
543
- this.writeln(".$type");
544
- this.unindent();
545
- this.write(`)`);
546
- }
547
- this.unindent();
548
- return;
549
- default: return this.writeln(`$(${d()}).designType("any")`);
437
+ validate(value, safe) {
438
+ this.push("");
439
+ this.errors = [];
440
+ this.stackErrors = [];
441
+ const passed = this.validateSafe(this.def, value);
442
+ this.pop(!passed);
443
+ if (!passed) {
444
+ if (safe) return false;
445
+ this.throw();
550
446
  }
447
+ return true;
551
448
  }
552
- defineObject(node) {
553
- const props = Array.from(node.props.values());
554
- for (const prop of props) {
555
- const pattern = prop.token("identifier")?.pattern;
556
- const optional = !!prop.token("optional");
557
- if (pattern) {
558
- this.writeln(`.propPattern(`);
559
- this.indent();
560
- this.writeln(`/${pattern.source}/${pattern.flags},`);
561
- } else {
562
- this.writeln(`.prop(`);
563
- this.indent();
564
- this.writeln(`"${escapeQuotes(prop.id)}",`);
565
- }
566
- this.annotateType(prop.getDefinition());
567
- this.indent().defineMetadata(prop).unindent();
568
- if (optional) this.writeln(" .optional()");
569
- this.writeln(" .$type");
570
- this.unindent();
571
- this.write(`)`);
449
+ validateSafe(def, value) {
450
+ if (this.isLimitExceeded()) return false;
451
+ if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
452
+ if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
453
+ if (def.optional && value === undefined) return true;
454
+ for (const plugin of this.opts.plugins) {
455
+ const result = plugin(this, def, value);
456
+ if (result === false || result === true) return result;
572
457
  }
573
- this.writeln();
574
- return this;
458
+ return this.validateAnnotatedType(def, value);
575
459
  }
576
- defineGroup(node) {
577
- const items = node.unwrap();
578
- for (const item of items) this.write(".item(").indent().annotateType(item).write(" .$type").writeln(`)`).unindent();
579
- return this;
460
+ get path() {
461
+ return this.stackPath.slice(1).join(".");
580
462
  }
581
- defineArray(node) {
582
- this.write(".of(").indent().annotateType(node.getDefinition()).write(" .$type").writeln(`)`).unindent();
583
- return this;
584
- }
585
- defineMetadata(node) {
586
- const annotations = this.doc.evalAnnotationsForNode(node);
587
- annotations?.forEach((an) => {
588
- this.resolveAnnotationValue(node, an);
589
- });
590
- return this;
591
- }
592
- resolveAnnotationValue(node, an) {
593
- const spec = this.doc.resolveAnnotation(an.name);
594
- let targetValue = "true";
595
- let multiple = false;
596
- if (spec) {
597
- multiple = spec.config.multiple;
598
- const length = spec.arguments.length;
599
- if (length !== 0) if (Array.isArray(spec.config.argument)) {
600
- targetValue = "{ ";
601
- let i = 0;
602
- for (const aSpec of spec.arguments) {
603
- if (an.args[i]) targetValue += `${wrapProp(aSpec.name)}: ${aSpec.type === "string" ? `"${escapeQuotes(an.args[i]?.text)}"` : an.args[i]?.text}${i === length - 1 ? "" : ", "} `;
604
- else {}
605
- i++;
606
- }
607
- targetValue += "}";
608
- } else {
609
- const aSpec = spec.arguments[0];
610
- if (an.args[0]) targetValue = aSpec.type === "string" ? `"${escapeQuotes(an.args[0]?.text)}"` : an.args[0]?.text;
611
- else targetValue = "true";
612
- }
613
- } else {
614
- multiple = node.countAnnotations(an.name) > 1 || an.args.length > 1;
615
- if (an.args.length) targetValue = an.args[0].type === "text" ? `"${escapeQuotes(an.args[0].text)}"` : an.args[0].text;
616
- }
617
- if (multiple) this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue}, true)`);
618
- else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue})`);
619
- }
620
- constructor(...args) {
621
- super(...args), _define_property$1(this, "postAnnotate", []);
622
- }
623
- };
624
-
625
- //#endregion
626
- //#region packages/typescript/src/plugin.ts
627
- const tsPlugin = () => {
628
- return {
629
- name: "typesccript",
630
- render(doc, format) {
631
- if (format === "dts") return [{
632
- fileName: `${doc.name}.d.ts`,
633
- content: new TypeRenderer(doc).render()
634
- }];
635
- if (format === "js") return [{
636
- fileName: `${doc.name}.js`,
637
- content: new JsRenderer(doc).render()
638
- }];
639
- },
640
- async buildEnd(output, format, repo) {
641
- if (format === "dts") {
642
- const annotations = await repo.getUsedAnnotations();
643
- const tags = await repo.getPrimitivesTags() || new Set();
644
- let rendered = [];
645
- for (const [key, val] of Object.entries(annotations)) {
646
- const multiple = val.multiple;
647
- let typeLine = Array.from(val.types).map((t) => {
648
- if (t.type === "object") return `{ ${Object.entries(t.props).map(([k, v]) => `${wrapProp(k)}${v.optional ? "?" : ""}: ${v.type}`).join(", ")} }`;
649
- else return t.optional ? `${t.type} | true` : t.type;
650
- }).join(" | ");
651
- rendered.push(`${wrapProp(key)}: ${multiple ? "(" : ""}${typeLine}${multiple ? ")[]" : ""}`);
652
- }
653
- let renderedTags = Array.from(tags).map((f) => `"${escapeQuotes(f)}"`).join(" | ");
654
- output.push({
655
- content: "// prettier-ignore-start\n/* eslint-disable */\n/**\n * 🪄 This file was generated by Atscript\n * It is generated based on annotations used in this project\n * Do not edit this file!\n *\n * Use `npx asc -f dts` command to re-generate this file\n */\nexport {}\n\ndeclare global {\n interface AtscriptMetadata {\n " + rendered.join("\n ") + "\n }\n" + " type AtscriptPrimitiveTags = " + renderedTags + "\n" + "}\n" + "// prettier-ignore-end",
656
- fileName: "atscript.d.ts",
657
- source: "",
658
- target: path.default.join(repo.root, "atscript.d.ts")
659
- });
660
- }
661
- }
662
- };
663
- };
664
-
665
- //#endregion
666
- //#region packages/typescript/src/validator.ts
667
- function _define_property(obj, key, value) {
668
- if (key in obj) Object.defineProperty(obj, key, {
669
- value,
670
- enumerable: true,
671
- configurable: true,
672
- writable: true
673
- });
674
- else obj[key] = value;
675
- return obj;
676
- }
677
- const regexCache = new Map();
678
- var Validator = class {
679
- isLimitExceeded() {
680
- if (this.stackErrors.length > 0) return this.stackErrors[this.stackErrors.length - 1].length >= this.opts.errorLimit;
681
- return this.errors.length >= this.opts.errorLimit;
682
- }
683
- push(name) {
684
- this.stackPath.push(name);
685
- this.stackErrors.push([]);
686
- }
687
- pop(saveErrors) {
688
- this.stackPath.pop();
689
- const popped = this.stackErrors.pop();
690
- if (saveErrors && popped?.length) popped.forEach((error) => {
691
- this.error(error.message, error.path, error.details);
692
- });
693
- return popped;
694
- }
695
- clear() {
696
- this.stackErrors[this.stackErrors.length - 1] = [];
697
- }
698
- error(message, path$2, details) {
699
- const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
700
- const error = {
701
- path: path$2 || this.path,
702
- message
703
- };
704
- if (details?.length) error.details = details;
705
- errors.push(error);
706
- }
707
- throw() {
708
- throw new ValidatorError(this.errors);
709
- }
710
- validate(value, safe) {
711
- this.push("");
712
- this.errors = [];
713
- this.stackErrors = [];
714
- const passed = this.validateSafe(this.def, value);
715
- this.pop(!passed);
716
- if (!passed) {
717
- if (safe) return false;
718
- this.throw();
719
- }
720
- return true;
721
- }
722
- validateSafe(def, value) {
723
- if (this.isLimitExceeded()) return false;
724
- if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
725
- if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
726
- if (def.optional && value === undefined) return true;
727
- for (const plugin of this.opts.plugins) {
728
- const result = plugin(this, def, value);
729
- if (result === false || result === true) return result;
730
- }
731
- return this.validateAnnotatedType(def, value);
732
- }
733
- get path() {
734
- return this.stackPath.slice(1).join(".");
735
- }
736
- validateAnnotatedType(def, value) {
737
- switch (def.type.kind) {
738
- case "object": return this.validateObject(def, value);
739
- case "union": return this.validateUnion(def, value);
740
- case "intersection": return this.validateIntersection(def, value);
741
- case "tuple": return this.validateTuple(def, value);
742
- case "array": return this.validateArray(def, value);
743
- case "": return this.validatePrimitive(def, value);
744
- default: throw new Error(`Unknown type "${def.type.kind}"`);
745
- }
463
+ validateAnnotatedType(def, value) {
464
+ switch (def.type.kind) {
465
+ case "object": return this.validateObject(def, value);
466
+ case "union": return this.validateUnion(def, value);
467
+ case "intersection": return this.validateIntersection(def, value);
468
+ case "tuple": return this.validateTuple(def, value);
469
+ case "array": return this.validateArray(def, value);
470
+ case "": return this.validatePrimitive(def, value);
471
+ default: throw new Error(`Unknown type "${def.type.kind}"`);
472
+ }
746
473
  }
747
474
  validateUnion(def, value) {
748
475
  let i = 0;
@@ -972,11 +699,11 @@ else {
972
699
  return true;
973
700
  }
974
701
  constructor(def, opts) {
975
- _define_property(this, "def", void 0);
976
- _define_property(this, "opts", void 0);
977
- _define_property(this, "errors", void 0);
978
- _define_property(this, "stackErrors", void 0);
979
- _define_property(this, "stackPath", void 0);
702
+ _define_property$1(this, "def", void 0);
703
+ _define_property$1(this, "opts", void 0);
704
+ _define_property$1(this, "errors", void 0);
705
+ _define_property$1(this, "stackErrors", void 0);
706
+ _define_property$1(this, "stackPath", void 0);
980
707
  this.def = def;
981
708
  this.errors = [];
982
709
  this.stackErrors = [];
@@ -992,7 +719,7 @@ else {
992
719
  };
993
720
  var ValidatorError = class extends Error {
994
721
  constructor(errors) {
995
- super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property(this, "errors", void 0), _define_property(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
722
+ super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property$1(this, "errors", void 0), _define_property$1(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
996
723
  }
997
724
  };
998
725
 
@@ -1125,6 +852,503 @@ function isAnnotatedTypeOfPrimitive(t) {
1125
852
  return false;
1126
853
  }
1127
854
 
855
+ //#endregion
856
+ //#region packages/typescript/src/json-schema.ts
857
+ function buildJsonSchema(type) {
858
+ const build = (def) => {
859
+ const t = def.type;
860
+ const meta = def.metadata;
861
+ switch (t.kind) {
862
+ case "object": {
863
+ const obj = t;
864
+ const properties = {};
865
+ const required = [];
866
+ for (const [key, val] of obj.props.entries()) {
867
+ properties[key] = build(val);
868
+ if (!val.optional) required.push(key);
869
+ }
870
+ const schema = {
871
+ type: "object",
872
+ properties
873
+ };
874
+ if (required.length) schema.required = required;
875
+ return schema;
876
+ }
877
+ case "array": {
878
+ const arr = t;
879
+ const schema = {
880
+ type: "array",
881
+ items: build(arr.of)
882
+ };
883
+ const minLength = meta.get("expect.minLength");
884
+ if (typeof minLength === "number") schema.minItems = minLength;
885
+ const maxLength = meta.get("expect.maxLength");
886
+ if (typeof maxLength === "number") schema.maxItems = maxLength;
887
+ return schema;
888
+ }
889
+ case "union": {
890
+ const grp = t;
891
+ return { anyOf: grp.items.map(build) };
892
+ }
893
+ case "intersection": {
894
+ const grp = t;
895
+ return { allOf: grp.items.map(build) };
896
+ }
897
+ case "tuple": {
898
+ const grp = t;
899
+ return {
900
+ type: "array",
901
+ items: grp.items.map(build),
902
+ additionalItems: false
903
+ };
904
+ }
905
+ case "": {
906
+ const fin = t;
907
+ const schema = {};
908
+ if (fin.value !== undefined) schema.const = fin.value;
909
+ if (fin.designType && fin.designType !== "any") {
910
+ schema.type = fin.designType === "undefined" ? "null" : fin.designType;
911
+ if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
912
+ }
913
+ if (schema.type === "string") {
914
+ const minLength = meta.get("expect.minLength");
915
+ if (typeof minLength === "number") schema.minLength = minLength;
916
+ const maxLength = meta.get("expect.maxLength");
917
+ if (typeof maxLength === "number") schema.maxLength = maxLength;
918
+ const patterns = meta.get("expect.pattern");
919
+ if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
920
+ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
921
+ }
922
+ if (schema.type === "number" || schema.type === "integer") {
923
+ const min = meta.get("expect.min");
924
+ if (typeof min === "number") schema.minimum = min;
925
+ const max = meta.get("expect.max");
926
+ if (typeof max === "number") schema.maximum = max;
927
+ }
928
+ return schema;
929
+ }
930
+ default: return {};
931
+ }
932
+ };
933
+ return build(type);
934
+ }
935
+
936
+ //#endregion
937
+ //#region packages/typescript/src/codegen/js-renderer.ts
938
+ function _define_property(obj, key, value) {
939
+ if (key in obj) Object.defineProperty(obj, key, {
940
+ value,
941
+ enumerable: true,
942
+ configurable: true,
943
+ writable: true
944
+ });
945
+ else obj[key] = value;
946
+ return obj;
947
+ }
948
+ var JsRenderer = class extends BaseRenderer {
949
+ pre() {
950
+ this.writeln("// prettier-ignore-start");
951
+ this.writeln("/* eslint-disable */");
952
+ const imports = ["defineAnnotatedType as $"];
953
+ if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
954
+ this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript"`);
955
+ }
956
+ post() {
957
+ for (const node of this.postAnnotate) {
958
+ this.annotateType(node.getDefinition(), node.id);
959
+ this.indent().defineMetadata(node).unindent();
960
+ this.writeln();
961
+ }
962
+ this.writeln("// prettier-ignore-end");
963
+ super.post();
964
+ }
965
+ renderInterface(node) {
966
+ this.writeln();
967
+ const exported = node.token("export")?.text === "export";
968
+ this.write(exported ? "export " : "");
969
+ this.write(`class ${node.id} `);
970
+ this.blockln("{}");
971
+ this.writeln("static __is_atscript_annotated_type = true");
972
+ this.writeln("static type = {}");
973
+ this.writeln("static metadata = new Map()");
974
+ if (this.opts?.preRenderJsonSchema) {
975
+ const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
976
+ this.writeln(`static _jsonSchema = ${schema}`);
977
+ this.writeln("static toJsonSchema() {");
978
+ this.indent().writeln("return this._jsonSchema").unindent();
979
+ this.writeln("}");
980
+ } else {
981
+ this.writeln("static _jsonSchema");
982
+ this.writeln("static toJsonSchema() {");
983
+ this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
984
+ this.writeln("}");
985
+ }
986
+ this.popln();
987
+ this.postAnnotate.push(node);
988
+ this.writeln();
989
+ }
990
+ renderType(node) {
991
+ this.writeln();
992
+ const exported = node.token("export")?.text === "export";
993
+ this.write(exported ? "export " : "");
994
+ this.write(`class ${node.id} `);
995
+ this.blockln("{}");
996
+ this.writeln("static __is_atscript_annotated_type = true");
997
+ this.writeln("static type = {}");
998
+ this.writeln("static metadata = new Map()");
999
+ if (this.opts?.jsonSchema) if (typeof this.opts.jsonSchema === "object" && this.opts.jsonSchema.preRender) {
1000
+ const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
1001
+ this.writeln(`static _jsonSchema = ${schema}`);
1002
+ this.writeln("static toJsonSchema() {");
1003
+ this.indent().writeln("return this._jsonSchema").unindent();
1004
+ this.writeln("}");
1005
+ } else {
1006
+ this.writeln("static _jsonSchema");
1007
+ this.writeln("static toJsonSchema() {");
1008
+ this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
1009
+ this.writeln("}");
1010
+ }
1011
+ this.popln();
1012
+ this.postAnnotate.push(node);
1013
+ this.writeln();
1014
+ }
1015
+ toAnnotatedType(node) {
1016
+ return this.toAnnotatedHandle(node).$type;
1017
+ }
1018
+ toAnnotatedHandle(node, skipAnnotations = false) {
1019
+ if (!node) return defineAnnotatedType();
1020
+ switch (node.entity) {
1021
+ case "interface":
1022
+ case "type": {
1023
+ const def = node.getDefinition();
1024
+ const handle = this.toAnnotatedHandle(def, true);
1025
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1026
+ }
1027
+ case "prop": {
1028
+ const prop = node;
1029
+ const def = prop.getDefinition();
1030
+ const handle = this.toAnnotatedHandle(def, true);
1031
+ if (!skipAnnotations) {
1032
+ this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(prop));
1033
+ if (prop.token("optional")) handle.optional();
1034
+ }
1035
+ return handle;
1036
+ }
1037
+ case "ref": {
1038
+ const ref = node;
1039
+ const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1040
+ const handle = this.toAnnotatedHandle(decl, true);
1041
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1042
+ }
1043
+ case "primitive": {
1044
+ const prim = node;
1045
+ const handle = defineAnnotatedType();
1046
+ handle.designType(prim.id === "never" ? "never" : prim.config.type);
1047
+ if (!skipAnnotations) this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1048
+ return handle;
1049
+ }
1050
+ case "const": {
1051
+ const c = node;
1052
+ const handle = defineAnnotatedType();
1053
+ const t = c.token("identifier")?.type;
1054
+ handle.designType(t === "number" ? "number" : "string");
1055
+ handle.value(t === "number" ? Number(c.id) : c.id);
1056
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1057
+ }
1058
+ case "structure": {
1059
+ const struct = node;
1060
+ const handle = defineAnnotatedType("object");
1061
+ for (const prop of Array.from(struct.props.values())) {
1062
+ const propHandle = this.toAnnotatedHandle(prop);
1063
+ const pattern = prop.token("identifier")?.pattern;
1064
+ if (pattern) handle.propPattern(pattern, propHandle.$type);
1065
+ else handle.prop(prop.id, propHandle.$type);
1066
+ }
1067
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1068
+ }
1069
+ case "group": {
1070
+ const group = node;
1071
+ const kind = group.op === "|" ? "union" : "intersection";
1072
+ const handle = defineAnnotatedType(kind);
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 "tuple": {
1077
+ const group = node;
1078
+ const handle = defineAnnotatedType("tuple");
1079
+ for (const item of group.unwrap()) handle.item(this.toAnnotatedHandle(item).$type);
1080
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1081
+ }
1082
+ case "array": {
1083
+ const arr = node;
1084
+ const handle = defineAnnotatedType("array");
1085
+ handle.of(this.toAnnotatedHandle(arr.getDefinition()).$type);
1086
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1087
+ }
1088
+ default: {
1089
+ const handle = defineAnnotatedType();
1090
+ return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1091
+ }
1092
+ }
1093
+ }
1094
+ applyExpectAnnotations(handle, annotations) {
1095
+ annotations?.forEach((a) => {
1096
+ switch (a.name) {
1097
+ case "expect.minLength":
1098
+ case "expect.maxLength":
1099
+ case "expect.min":
1100
+ case "expect.max":
1101
+ if (a.args[0]) handle.annotate(a.name, Number(a.args[0].text));
1102
+ break;
1103
+ case "expect.pattern":
1104
+ handle.annotate(a.name, {
1105
+ pattern: a.args[0]?.text || "",
1106
+ flags: a.args[1]?.text,
1107
+ message: a.args[2]?.text
1108
+ }, true);
1109
+ break;
1110
+ case "expect.int":
1111
+ handle.annotate(a.name, true);
1112
+ break;
1113
+ default:
1114
+ }
1115
+ });
1116
+ return handle;
1117
+ }
1118
+ annotateType(_node, name) {
1119
+ if (!_node) return this;
1120
+ const node = this.doc.mergeIntersection(_node);
1121
+ let kind = node.entity;
1122
+ switch (node.entity) {
1123
+ case "ref": {
1124
+ const ref = node;
1125
+ const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1126
+ if ((0, __atscript_core.isPrimitive)(decl)) {
1127
+ this.annotateType(decl, name);
1128
+ return this;
1129
+ }
1130
+ const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1131
+ this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`).unindent();
1132
+ return this;
1133
+ }
1134
+ case "primitive": {
1135
+ this.definePrimitive(node, name);
1136
+ return this;
1137
+ }
1138
+ case "const": {
1139
+ this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().defineConst(node).unindent();
1140
+ return this;
1141
+ }
1142
+ case "structure": {
1143
+ this.writeln(`$("object"${name ? `, ${name}` : ""})`).indent().defineObject(node).unindent();
1144
+ return this;
1145
+ }
1146
+ case "group": {
1147
+ kind = node.op === "|" ? "union" : "intersection";
1148
+ this.writeln(`$("${kind}"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
1149
+ return this;
1150
+ }
1151
+ case "tuple": {
1152
+ this.writeln(`$("tuple"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
1153
+ return this;
1154
+ }
1155
+ case "array": {
1156
+ this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
1157
+ return this;
1158
+ }
1159
+ default: {
1160
+ console.log("!!!!!!! UNKNOWN ", node.entity);
1161
+ return this;
1162
+ }
1163
+ }
1164
+ }
1165
+ defineConst(node) {
1166
+ const t = node.token("identifier")?.type;
1167
+ const designType = t === "text" ? "string" : t === "number" ? "number" : "unknown";
1168
+ this.writeln(`.designType("${escapeQuotes(designType)}")`);
1169
+ this.writeln(`.value(${t === "text" ? `"${escapeQuotes(node.id)}"` : node.id})`);
1170
+ return this;
1171
+ }
1172
+ definePrimitive(node, name) {
1173
+ this.renderPrimitiveDef(node.id === "never" ? "never" : node.config.type, name);
1174
+ this.writeln(` .tags(${Array.from(node.tags).map((f) => `"${escapeQuotes(f)}"`).join(", ")})`);
1175
+ return this;
1176
+ }
1177
+ renderPrimitiveDef(def, name) {
1178
+ const d = (t) => [`"${t || ""}"`, name].filter(Boolean).join(", ").replace(/^""$/, "");
1179
+ if (!def) return this.writeln(`$(${d()}).designType("any")`);
1180
+ if (typeof def === "string") return this.writeln(`$(${d()}).designType("${def === "void" ? "undefined" : def}")`);
1181
+ switch (def.kind) {
1182
+ case "final": return this.writeln(`$(${d()}).designType("${def.value === "void" ? "undefined" : def.value}")`);
1183
+ case "union":
1184
+ case "intersection":
1185
+ case "tuple":
1186
+ this.writeln(`$(${d(def.kind)})`);
1187
+ this.indent();
1188
+ for (const itemDef of def.items) {
1189
+ this.write(`.item(`);
1190
+ this.indent();
1191
+ this.renderPrimitiveDef(itemDef);
1192
+ this.writeln(".$type");
1193
+ this.unindent();
1194
+ this.write(`)`);
1195
+ }
1196
+ this.unindent();
1197
+ return;
1198
+ case "array":
1199
+ this.writeln(`$(${d("array")})`);
1200
+ this.indent();
1201
+ this.write(".of(");
1202
+ this.indent();
1203
+ this.renderPrimitiveDef(def.of);
1204
+ this.writeln(`.$type`);
1205
+ this.unindent();
1206
+ this.writeln(`)`);
1207
+ this.unindent();
1208
+ return;
1209
+ case "object":
1210
+ this.writeln(`$(${d("object")})`);
1211
+ this.indent();
1212
+ for (const [key, propDef] of Object.entries(def.props)) {
1213
+ const optional = typeof propDef === "object" && propDef.optional;
1214
+ this.writeln(`.prop(`);
1215
+ this.indent();
1216
+ this.writeln(`"${escapeQuotes(key)}",`);
1217
+ this.renderPrimitiveDef(propDef);
1218
+ if (optional) this.writeln(".optional()");
1219
+ this.writeln(".$type");
1220
+ this.unindent();
1221
+ this.write(`)`);
1222
+ }
1223
+ for (const [key, propDef] of Object.entries(def.propsPatterns)) {
1224
+ const optional = typeof propDef === "object" && propDef.optional;
1225
+ this.writeln(`.propPattern(`);
1226
+ this.indent();
1227
+ this.writeln(`${key},`);
1228
+ this.renderPrimitiveDef(propDef);
1229
+ if (optional) this.writeln(".optional()");
1230
+ this.writeln(".$type");
1231
+ this.unindent();
1232
+ this.write(`)`);
1233
+ }
1234
+ this.unindent();
1235
+ return;
1236
+ default: return this.writeln(`$(${d()}).designType("any")`);
1237
+ }
1238
+ }
1239
+ defineObject(node) {
1240
+ const props = Array.from(node.props.values());
1241
+ for (const prop of props) {
1242
+ const pattern = prop.token("identifier")?.pattern;
1243
+ const optional = !!prop.token("optional");
1244
+ if (pattern) {
1245
+ this.writeln(`.propPattern(`);
1246
+ this.indent();
1247
+ this.writeln(`/${pattern.source}/${pattern.flags},`);
1248
+ } else {
1249
+ this.writeln(`.prop(`);
1250
+ this.indent();
1251
+ this.writeln(`"${escapeQuotes(prop.id)}",`);
1252
+ }
1253
+ this.annotateType(prop.getDefinition());
1254
+ this.indent().defineMetadata(prop).unindent();
1255
+ if (optional) this.writeln(" .optional()");
1256
+ this.writeln(" .$type");
1257
+ this.unindent();
1258
+ this.write(`)`);
1259
+ }
1260
+ this.writeln();
1261
+ return this;
1262
+ }
1263
+ defineGroup(node) {
1264
+ const items = node.unwrap();
1265
+ for (const item of items) this.write(".item(").indent().annotateType(item).write(" .$type").writeln(`)`).unindent();
1266
+ return this;
1267
+ }
1268
+ defineArray(node) {
1269
+ this.write(".of(").indent().annotateType(node.getDefinition()).write(" .$type").writeln(`)`).unindent();
1270
+ return this;
1271
+ }
1272
+ defineMetadata(node) {
1273
+ const annotations = this.doc.evalAnnotationsForNode(node);
1274
+ annotations?.forEach((an) => {
1275
+ this.resolveAnnotationValue(node, an);
1276
+ });
1277
+ return this;
1278
+ }
1279
+ resolveAnnotationValue(node, an) {
1280
+ const spec = this.doc.resolveAnnotation(an.name);
1281
+ let targetValue = "true";
1282
+ let multiple = false;
1283
+ if (spec) {
1284
+ multiple = spec.config.multiple;
1285
+ const length = spec.arguments.length;
1286
+ if (length !== 0) if (Array.isArray(spec.config.argument)) {
1287
+ targetValue = "{ ";
1288
+ let i = 0;
1289
+ for (const aSpec of spec.arguments) {
1290
+ if (an.args[i]) targetValue += `${wrapProp(aSpec.name)}: ${aSpec.type === "string" ? `"${escapeQuotes(an.args[i]?.text)}"` : an.args[i]?.text}${i === length - 1 ? "" : ", "} `;
1291
+ else {}
1292
+ i++;
1293
+ }
1294
+ targetValue += "}";
1295
+ } else {
1296
+ const aSpec = spec.arguments[0];
1297
+ if (an.args[0]) targetValue = aSpec.type === "string" ? `"${escapeQuotes(an.args[0]?.text)}"` : an.args[0]?.text;
1298
+ else targetValue = "true";
1299
+ }
1300
+ } else {
1301
+ multiple = node.countAnnotations(an.name) > 1 || an.args.length > 1;
1302
+ if (an.args.length) targetValue = an.args[0].type === "text" ? `"${escapeQuotes(an.args[0].text)}"` : an.args[0].text;
1303
+ }
1304
+ if (multiple) this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue}, true)`);
1305
+ else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue})`);
1306
+ }
1307
+ constructor(doc, opts) {
1308
+ super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), this.opts = opts, this.postAnnotate = [];
1309
+ }
1310
+ };
1311
+
1312
+ //#endregion
1313
+ //#region packages/typescript/src/plugin.ts
1314
+ const tsPlugin = (opts) => {
1315
+ return {
1316
+ name: "typesccript",
1317
+ render(doc, format) {
1318
+ if (format === "dts") return [{
1319
+ fileName: `${doc.name}.d.ts`,
1320
+ content: new TypeRenderer(doc, opts).render()
1321
+ }];
1322
+ if (format === "js") return [{
1323
+ fileName: `${doc.name}.js`,
1324
+ content: new JsRenderer(doc, opts).render()
1325
+ }];
1326
+ },
1327
+ async buildEnd(output, format, repo) {
1328
+ if (format === "dts") {
1329
+ const annotations = await repo.getUsedAnnotations();
1330
+ const tags = await repo.getPrimitivesTags() || new Set();
1331
+ let rendered = [];
1332
+ for (const [key, val] of Object.entries(annotations)) {
1333
+ const multiple = val.multiple;
1334
+ let typeLine = Array.from(val.types).map((t) => {
1335
+ if (t.type === "object") return `{ ${Object.entries(t.props).map(([k, v]) => `${wrapProp(k)}${v.optional ? "?" : ""}: ${v.type}`).join(", ")} }`;
1336
+ else return t.optional ? `${t.type} | true` : t.type;
1337
+ }).join(" | ");
1338
+ rendered.push(`${wrapProp(key)}: ${multiple ? "(" : ""}${typeLine}${multiple ? ")[]" : ""}`);
1339
+ }
1340
+ let renderedTags = Array.from(tags).map((f) => `"${escapeQuotes(f)}"`).join(" | ");
1341
+ output.push({
1342
+ content: "// prettier-ignore-start\n/* eslint-disable */\n/**\n * 🪄 This file was generated by Atscript\n * It is generated based on annotations used in this project\n * Do not edit this file!\n *\n * Use `npx asc -f dts` command to re-generate this file\n */\nexport {}\n\ndeclare global {\n interface AtscriptMetadata {\n " + rendered.join("\n ") + "\n }\n" + " type AtscriptPrimitiveTags = " + renderedTags + "\n" + "}\n" + "// prettier-ignore-end",
1343
+ fileName: "atscript.d.ts",
1344
+ source: "",
1345
+ target: path.default.join(repo.root, "atscript.d.ts")
1346
+ });
1347
+ }
1348
+ }
1349
+ };
1350
+ };
1351
+
1128
1352
  //#endregion
1129
1353
  //#region packages/typescript/src/index.ts
1130
1354
  var src_default = tsPlugin;
@@ -1132,6 +1356,7 @@ var src_default = tsPlugin;
1132
1356
  //#endregion
1133
1357
  exports.Validator = Validator
1134
1358
  exports.ValidatorError = ValidatorError
1359
+ exports.buildJsonSchema = buildJsonSchema
1135
1360
  exports.default = src_default
1136
1361
  exports.defineAnnotatedType = defineAnnotatedType
1137
1362
  exports.isAnnotatedType = isAnnotatedType