quicktype-core 23.2.6 → 23.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -132,6 +132,9 @@ export declare abstract class ConvenienceRenderer extends Renderer {
132
132
  protected emitComments(comments: Comment[]): void;
133
133
  protected emitCommentLines(lines: Sourcelike[], { lineStart, firstLineStart, lineEnd, beforeComment, afterComment, }?: CommentOptions): void;
134
134
  protected emitDescription(description: Sourcelike[] | undefined): void;
135
+ private commentLineEscapes;
136
+ protected get commentLinesSpliceOnBackslash(): boolean;
137
+ private escapeCommentLine;
135
138
  protected emitDescriptionBlock(lines: Sourcelike[]): void;
136
139
  protected emitPropertyTable(c: ClassType, makePropertyRow: (name: Name, jsonName: string, p: ClassProperty) => Sourcelike[]): void;
137
140
  private processGraph;
@@ -32,7 +32,13 @@ const unionMemberNameOrder = 40;
32
32
  function splitDescription(descriptions) {
33
33
  if (descriptions === undefined)
34
34
  return undefined;
35
- const description = Array.from(descriptions).join("\n\n").trim();
35
+ // U+0085, U+2028 and U+2029 count as line terminators in some
36
+ // target languages (JavaScript, C#), so they must not survive
37
+ // into single-line comments.
38
+ const description = Array.from(descriptions)
39
+ .join("\n\n")
40
+ .replace(/\r\n?|[\u0085\u2028\u2029]/g, "\n")
41
+ .trim();
36
42
  if (description === "")
37
43
  return undefined;
38
44
  return wordWrap(description)
@@ -577,6 +583,13 @@ class ConvenienceRenderer extends Renderer_1.Renderer {
577
583
  });
578
584
  }
579
585
  emitCommentLines(lines, { lineStart = this.commentLineStart, firstLineStart = lineStart, lineEnd, beforeComment, afterComment, } = {}) {
586
+ const replacements = this.commentLineEscapes({
587
+ lineStart,
588
+ firstLineStart,
589
+ lineEnd,
590
+ beforeComment,
591
+ afterComment,
592
+ });
580
593
  if (beforeComment !== undefined) {
581
594
  this.emitLine(beforeComment);
582
595
  }
@@ -584,14 +597,15 @@ class ConvenienceRenderer extends Renderer_1.Renderer {
584
597
  for (const line of lines) {
585
598
  let start = first ? firstLineStart : lineStart;
586
599
  first = false;
587
- if (this.sourcelikeToString(line) === "") {
600
+ const escapedLine = this.escapeCommentLine(line, replacements, lineEnd);
601
+ if (this.sourcelikeToString(escapedLine) === "") {
588
602
  start = (0, Strings_1.trimEnd)(start);
589
603
  }
590
604
  if (lineEnd) {
591
- this.emitLine(start, line, lineEnd);
605
+ this.emitLine(start, escapedLine, lineEnd);
592
606
  }
593
607
  else {
594
- this.emitLine(start, line);
608
+ this.emitLine(start, escapedLine);
595
609
  }
596
610
  }
597
611
  if (afterComment !== undefined) {
@@ -604,6 +618,59 @@ class ConvenienceRenderer extends Renderer_1.Renderer {
604
618
  // FIXME: word-wrap
605
619
  this.emitDescriptionBlock(description);
606
620
  }
621
+ commentLineEscapes(options) {
622
+ const delimiters = [
623
+ options.lineStart,
624
+ options.firstLineStart,
625
+ options.lineEnd,
626
+ options.beforeComment,
627
+ options.afterComment,
628
+ ];
629
+ const containsDelimiter = (delimiter) => delimiters.some((part) => { var _a; return (_a = part === null || part === void 0 ? void 0 : part.includes(delimiter)) !== null && _a !== void 0 ? _a : false; });
630
+ const replacements = [];
631
+ if (containsDelimiter("/*") || containsDelimiter("*/")) {
632
+ // The opener must be escaped, too: some languages (Kotlin,
633
+ // Scala) nest block comments, so a lone `/*` would reopen a
634
+ // comment that the closing delimiter can't terminate.
635
+ replacements.push(["/*", "/ *"], ["*/", "* /"]);
636
+ }
637
+ if (containsDelimiter("{-") || containsDelimiter("-}")) {
638
+ replacements.push(["{-", "{ -"], ["-}", "- }"]);
639
+ }
640
+ if (containsDelimiter('"""')) {
641
+ // Triple-quoted comments (Python docstrings, Elixir
642
+ // moduledocs) are string literals, so backslashes are escape
643
+ // characters — in particular a trailing backslash would
644
+ // swallow the first quote of the closing delimiter.
645
+ replacements.push(["\\", "\\\\"], ['"""', '\\"\\"\\"']);
646
+ }
647
+ return replacements;
648
+ }
649
+ // C-family lexers splice a backslash-newline into one line even
650
+ // inside comments, which would pull the following line of
651
+ // generated code into an attacker-controlled comment.
652
+ get commentLinesSpliceOnBackslash() {
653
+ return false;
654
+ }
655
+ escapeCommentLine(line, replacements, lineEnd) {
656
+ if (replacements.length === 0 && !this.commentLinesSpliceOnBackslash) {
657
+ return line;
658
+ }
659
+ let escaped = replacements.reduce((result, [unsafe, safe]) => result.split(unsafe).join(safe), this.sourcelikeToString(line));
660
+ // A quote at the end of the line would merge with a closing
661
+ // `"""` emitted right after it. The quote needs escaping iff
662
+ // it's preceded by an even number of backslashes.
663
+ if (lineEnd !== undefined && lineEnd.startsWith('"')) {
664
+ const trailing = /(\\*)"$/.exec(escaped);
665
+ if (trailing !== null && trailing[1].length % 2 === 0) {
666
+ escaped = `${escaped.slice(0, -1)}\\"`;
667
+ }
668
+ }
669
+ if (this.commentLinesSpliceOnBackslash && escaped.endsWith("\\")) {
670
+ escaped = `${escaped}.`;
671
+ }
672
+ return escaped;
673
+ }
607
674
  emitDescriptionBlock(lines) {
608
675
  this.emitCommentLines(lines);
609
676
  }
@@ -54,7 +54,7 @@ const is_url_1 = __importDefault(require("is-url"));
54
54
  const Messages_1 = require("../../Messages");
55
55
  const Support_1 = require("../../support/Support");
56
56
  const get_stream_1 = require("./get-stream");
57
- const _fetch_1 = require("./$fetch");
57
+ const _fetch_ci_1 = require("./$fetch.ci");
58
58
  function parseHeaders(httpHeaders) {
59
59
  if (!Array.isArray(httpHeaders)) {
60
60
  return {};
@@ -76,7 +76,7 @@ function readableFromFileOrURL(fileOrURL, httpHeaders) {
76
76
  return __awaiter(this, void 0, void 0, function* () {
77
77
  try {
78
78
  if ((0, is_url_1.default)(fileOrURL)) {
79
- const response = yield (0, _fetch_1.fetch)(fileOrURL, {
79
+ const response = yield (0, _fetch_ci_1.fetch)(fileOrURL, {
80
80
  headers: parseHeaders(httpHeaders),
81
81
  });
82
82
  return (0, ts_necessities_1.defined)(response.body);
@@ -219,6 +219,7 @@ export declare class CJSONRenderer extends ConvenienceRenderer {
219
219
  * @pram global: true if global include, false otherwise (default)
220
220
  */
221
221
  protected emitIncludeLine(name: Sourcelike, global?: boolean): void;
222
+ protected get commentLinesSpliceOnBackslash(): boolean;
222
223
  /**
223
224
  * Emit description block
224
225
  * @param lines: description block lines
@@ -3067,6 +3067,9 @@ class CJSONRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
3067
3067
  emitIncludeLine(name, global = false) {
3068
3068
  this.emitLine("#include ", global ? "<" : '"', name, global ? ">" : '"');
3069
3069
  }
3070
+ get commentLinesSpliceOnBackslash() {
3071
+ return true;
3072
+ }
3070
3073
  /**
3071
3074
  * Emit description block
3072
3075
  * @param lines: description block lines
@@ -65,6 +65,7 @@ export declare class CPlusPlusRenderer extends ConvenienceRenderer {
65
65
  protected finishFile(): void;
66
66
  protected get needsTypeDeclarationBeforeUse(): boolean;
67
67
  protected canBeForwardDeclared(t: Type): boolean;
68
+ protected get commentLinesSpliceOnBackslash(): boolean;
68
69
  protected emitDescriptionBlock(lines: Sourcelike[]): void;
69
70
  protected emitBlock(line: Sourcelike, withSemicolon: boolean, f: () => void, withIndent?: boolean): void;
70
71
  protected emitNamespaces(namespaceNames: Iterable<string>, f: () => void): void;
@@ -458,6 +458,9 @@ class CPlusPlusRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
458
458
  const kind = t.kind;
459
459
  return kind === "class";
460
460
  }
461
+ get commentLinesSpliceOnBackslash() {
462
+ return true;
463
+ }
461
464
  emitDescriptionBlock(lines) {
462
465
  this.emitCommentLines(lines, {
463
466
  lineStart: " * ",
@@ -143,12 +143,18 @@ class CSharpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
143
143
  return [...propertyArray, csType, " ", name, " { get; set; }"];
144
144
  }
145
145
  emitDescriptionBlock(lines) {
146
+ // Doc comments are XML, so anything that could be mistaken
147
+ // for markup must be escaped.
148
+ const escapedLines = lines.map((line) => this.sourcelikeToString(line)
149
+ .replace(/&/g, "&amp;")
150
+ .replace(/</g, "&lt;")
151
+ .replace(/>/g, "&gt;"));
146
152
  const start = "/// <summary>";
147
153
  if (this._csOptions.dense) {
148
- this.emitLine(start, lines.join("; "), "</summary>");
154
+ this.emitLine(start, escapedLines.join("; "), "</summary>");
149
155
  }
150
156
  else {
151
- this.emitCommentLines(lines, {
157
+ this.emitCommentLines(escapedLines, {
152
158
  lineStart: "/// ",
153
159
  beforeComment: start,
154
160
  afterComment: "/// </summary>",
@@ -21,6 +21,7 @@ export declare class ObjectiveCRenderer extends ConvenienceRenderer {
21
21
  protected makeUnionMemberNamer(): null;
22
22
  protected makeEnumCaseNamer(): Namer;
23
23
  protected namedTypeToNameForTopLevel(type: Type): Type | undefined;
24
+ protected get commentLinesSpliceOnBackslash(): boolean;
24
25
  protected emitDescriptionBlock(lines: Sourcelike[]): void;
25
26
  protected emitBlock(line: Sourcelike, f: () => void): void;
26
27
  protected emitMethod(declaration: Sourcelike, f: () => void): void;
@@ -64,6 +64,9 @@ class ObjectiveCRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
64
64
  namedTypeToNameForTopLevel(type) {
65
65
  return type;
66
66
  }
67
+ get commentLinesSpliceOnBackslash() {
68
+ return true;
69
+ }
67
70
  emitDescriptionBlock(lines) {
68
71
  this.emitCommentLines(lines, { lineStart: "/// " });
69
72
  }
@@ -35,6 +35,7 @@ export declare class PhpRenderer extends ConvenienceRenderer {
35
35
  protected finishFile(): void;
36
36
  protected emitFileHeader(fileName: Sourcelike, _imports: string[]): void;
37
37
  emitDescriptionBlock(lines: Sourcelike[]): void;
38
+ private emitDocBlockDescription;
38
39
  emitBlock(line: Sourcelike, f: () => void): void;
39
40
  protected phpType(_reference: boolean, t: Type, isOptional?: boolean, prefix?: string, suffix?: string): Sourcelike;
40
41
  protected phpDocConvertType(className: Name, t: Type): Sourcelike;
@@ -133,6 +133,17 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
133
133
  afterComment: " */",
134
134
  });
135
135
  }
136
+ emitDocBlockDescription(desc) {
137
+ if (desc === undefined) {
138
+ this.emitLine("/**");
139
+ return;
140
+ }
141
+ this.emitCommentLines(desc, {
142
+ lineStart: " * ",
143
+ beforeComment: "/**",
144
+ });
145
+ this.emitLine(" *");
146
+ }
136
147
  emitBlock(line, f) {
137
148
  this.emitLine(line, " {");
138
149
  this.indent(f);
@@ -350,11 +361,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
350
361
  });
351
362
  }
352
363
  emitFromMethod(names, p, className, _name, desc) {
353
- this.emitLine("/**");
354
- if (desc !== undefined) {
355
- this.emitLine(" * ", desc);
356
- this.emitLine(" *");
357
- }
364
+ this.emitDocBlockDescription(desc);
358
365
  // this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null"));
359
366
  this.emitLine(" * @param ", this.phpConvertType(className, p.type), " $value");
360
367
  this.emitLine(" * @throws Exception");
@@ -373,11 +380,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
373
380
  });
374
381
  }
375
382
  emitToMethod(names, p, className, name, desc) {
376
- this.emitLine("/**");
377
- if (desc !== undefined) {
378
- this.emitLine(" * ", desc);
379
- this.emitLine(" *");
380
- }
383
+ this.emitDocBlockDescription(desc);
381
384
  this.emitLine(" * @throws Exception");
382
385
  this.emitLine(" * @return ", this.phpConvertType(className, p.type));
383
386
  this.emitLine(" */");
@@ -402,11 +405,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
402
405
  });
403
406
  }
404
407
  emitValidateMethod(names, p, className, name, desc) {
405
- this.emitLine("/**");
406
- if (desc !== undefined) {
407
- this.emitLine(" * ", desc);
408
- this.emitLine(" *");
409
- }
408
+ this.emitDocBlockDescription(desc);
410
409
  this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null"));
411
410
  this.emitLine(" * @return bool");
412
411
  this.emitLine(" * @throws Exception");
@@ -424,11 +423,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
424
423
  }
425
424
  emitGetMethod(names, p, className, name, desc) {
426
425
  if (this._options.withGet) {
427
- this.emitLine("/**");
428
- if (desc !== undefined) {
429
- this.emitLine(" * ", desc);
430
- this.emitLine(" *");
431
- }
426
+ this.emitDocBlockDescription(desc);
432
427
  if (!this._options.fastGet) {
433
428
  this.emitLine(" * @throws Exception");
434
429
  }
@@ -458,11 +453,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
458
453
  }
459
454
  emitSetMethod(names, p, className, name, desc) {
460
455
  if (this._options.withSet) {
461
- this.emitLine("/**");
462
- if (desc !== undefined) {
463
- this.emitLine(" * ", desc);
464
- this.emitLine(" *");
465
- }
456
+ this.emitDocBlockDescription(desc);
466
457
  this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null"));
467
458
  this.emitLine(" * @throws Exception");
468
459
  this.emitLine(" */");
@@ -481,11 +472,7 @@ class PhpRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
481
472
  }
482
473
  emitSampleMethod(names, p, className, name, desc, idx) {
483
474
  if (this._options.withGet) {
484
- this.emitLine("/**");
485
- if (desc !== undefined) {
486
- this.emitLine(" * ", desc);
487
- this.emitLine(" *");
488
- }
475
+ this.emitDocBlockDescription(desc);
489
476
  const rendered = this.phpType(false, p.type);
490
477
  this.emitLine(" * @return ", rendered);
491
478
  this.emitLine(" */");
@@ -2,7 +2,7 @@ import { ConvenienceRenderer, type ForbiddenWordsInfo } from "../../ConvenienceR
2
2
  import { type Name, type Namer } from "../../Naming";
3
3
  import type { RenderContext } from "../../Renderer";
4
4
  import type { OptionValues } from "../../RendererOptions";
5
- import { type Sourcelike } from "../../Source";
5
+ import type { Sourcelike } from "../../Source";
6
6
  import type { TargetLanguage } from "../../TargetLanguage";
7
7
  import { type ClassProperty, ClassType, EnumType, type Type } from "../../Type";
8
8
  import type { pythonOptions } from "./language";
@@ -4,7 +4,6 @@ exports.PythonRenderer = void 0;
4
4
  const collection_utils_1 = require("collection-utils");
5
5
  const ConvenienceRenderer_1 = require("../../ConvenienceRenderer");
6
6
  const Naming_1 = require("../../Naming");
7
- const Source_1 = require("../../Source");
8
7
  const Strings_1 = require("../../support/Strings");
9
8
  const Support_1 = require("../../support/Support");
10
9
  const Transformers_1 = require("../../Transformers");
@@ -45,14 +44,8 @@ class PythonRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
45
44
  }
46
45
  emitDescriptionBlock(lines) {
47
46
  if (lines.length === 1) {
48
- const docstring = (0, Source_1.modifySource)((content) => {
49
- if (content.endsWith('"')) {
50
- return content.slice(0, -1) + '\\"';
51
- }
52
- return content;
53
- }, lines[0]);
54
47
  this.emitComments([
55
- { customLines: [docstring], lineStart: '"""', lineEnd: '"""' },
48
+ { customLines: lines, lineStart: '"""', lineEnd: '"""' },
56
49
  ]);
57
50
  }
58
51
  else {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "quicktype-core",
3
- "version": "23.2.6",
3
+ "version": "23.3.1",
4
4
  "description": "The quicktype engine as a library",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
- "repository": "https://github.com/quicktype/quicktype",
8
+ "repository": "https://github.com/glideapps/quicktype",
9
9
  "scripts": {
10
10
  "clean": "rm -rf dist node_modules *~",
11
11
  "build": "./env.sh && tsc"