(
code: string,
fieldsByType: Map<string, Set<string>>,
toFieldName: (jsonName: string) => string
)
| 765 | * name (typically snake_case). |
| 766 | */ |
| 767 | export function annotateInternalPythonFields( |
| 768 | code: string, |
| 769 | fieldsByType: Map<string, Set<string>>, |
| 770 | toFieldName: (jsonName: string) => string |
| 771 | ): string { |
| 772 | const escapeRegex = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 773 | let result = code; |
| 774 | for (const [typeName, fields] of fieldsByType) { |
| 775 | // Match the class body up to the next top-level statement. quicktype's |
| 776 | // generated classes are separated by blank-line boundaries. |
| 777 | const classRe = new RegExp( |
| 778 | `(@dataclass\\nclass ${escapeRegex(typeName)}[:(][^]*?)(?=\\n(?:@dataclass\\n)?class \\w|\\n\\nclass |\\n[A-Za-z_]\\w* =|$)`, |
| 779 | "g" |
| 780 | ); |
| 781 | result = result.replace(classRe, (block) => { |
| 782 | for (const jsonField of fields) { |
| 783 | const pyField = toFieldName(jsonField); |
| 784 | const escaped = escapeRegex(pyField); |
| 785 | // Match ` fieldName: type` style declarations (PEP 526). Avoid |
| 786 | // double-annotating if the comment is already present immediately above. |
| 787 | block = block.replace( |
| 788 | new RegExp(`(^(?! # Internal:.*$)(?:.*\\n)?)( )${escaped}(?=\\s*:)`, "gm"), |
| 789 | (_match, prefix, indent) => { |
| 790 | // Avoid duplicate annotation if the previous line is already an Internal: marker. |
| 791 | if (/ # Internal:/.test(prefix)) return `${prefix}${indent}${pyField}`; |
| 792 | return `${prefix}${indent}# Internal: this field is an internal SDK API and is not part of the public surface.\n${indent}${pyField}`; |
| 793 | } |
| 794 | ); |
| 795 | } |
| 796 | return block; |
| 797 | }); |
| 798 | } |
| 799 | return result; |
| 800 | } |
| 801 | |
| 802 | /** |
| 803 | * Walks a top-level JSON Schema and marks any property whose referenced type |
no test coverage detected
searching dependent graphs…