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