forked from Universal-Commerce-Protocol/ucp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_ts_schema_types.js
More file actions
145 lines (129 loc) · 4.89 KB
/
generate_ts_schema_types.js
File metadata and controls
145 lines (129 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const fs = require('node:fs');
const path = require('node:path');
const { compile } = require('json-schema-to-typescript');
const SOURCE_ROOT = path.resolve(__dirname, 'spec');
const OUTPUT_FILE = path.resolve(__dirname, './generated/schema-types.ts');
const WRAPPER_NAME = 'SCHEMA_WRAPPER';
/**
* Dynamically finds all JSON schemas and generates TypeScript types.
*/
async function generate() {
if (!fs.existsSync(path.dirname(OUTPUT_FILE))) {
fs.mkdirSync(path.dirname(OUTPUT_FILE), {recursive: true});
}
const properties = {};
// Add shopping schemas
const shoppingDir = path.join(SOURCE_ROOT, 'schemas/shopping');
if (fs.existsSync(shoppingDir)) {
for (const file of fs.readdirSync(shoppingDir)) {
if (file.endsWith('.json')) {
properties[path.basename(file, '.json')] = {
$ref: path.join(shoppingDir, file)
};
}
}
}
// Add handler schemas
const handlersDir = path.join(SOURCE_ROOT, 'handlers');
if (fs.existsSync(handlersDir)) {
for (const handler of fs.readdirSync(handlersDir)) {
const handlerPath = path.join(handlersDir, handler);
if (fs.statSync(handlerPath).isDirectory()) {
for (const file of fs.readdirSync(handlerPath)) {
if (file.endsWith('.json')) {
const name =
`${handler}_${path.basename(file, '.json')}`.replace(/-/g, '_');
properties[name] = {$ref: path.join(handlerPath, file)};
}
}
}
}
}
console.log(`Found ${Object.keys(properties).length} schemas. Compiling...`);
const wrappedSchema = {
title: WRAPPER_NAME,
type: 'object',
properties,
additionalProperties: false
};
try {
let ts = await compile(wrappedSchema, WRAPPER_NAME, {
cwd: SOURCE_ROOT,
$refOptions: {
resolve: {
file: {
order: 1,
canRead: true,
read: (file) => {
let filePath = file.url;
if (filePath.startsWith('file://')) {
try {
filePath = require('node:url').fileURLToPath(filePath);
} catch {
filePath = filePath.replace('file://', '');
}
}
const content = fs.readFileSync(filePath, 'utf8');
const json = JSON.parse(content);
/**
* Cleans up the JSON object by removing properties that interfere
* with `json-schema-to-typescript`.
* This function mutates the input object. While acceptable here,
* be mindful of side effects. If this JSON object were used
* elsewhere, this could lead to unexpected behavior.
* @param {!any} obj The object to clean.
*/
function clean(obj) {
if (typeof obj !== 'object' || obj === null) return;
// When $ref is present, other properties like title and
// description are technically ignored in older JSON Schema
// drafts. We remove them here to prevent
// json-schema-to-typescript from generating duplicate interface
// definitions or JSDoc comments that conflict with the
// referenced type.
if (obj.$ref) {
delete obj.description;
delete obj.title;
}
for (const key in obj) {
clean(obj[key]);
}
}
clean(json);
return json;
}
}
}
},
bannerComment: `
/* tslint:disable:enforce-comments-on-exported-symbols */
/* eslint-disable */
/* tslint:disable:enforce-name-casing */
/**
* This file was automatically generated by json-schema-to-typescript.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run json-schema-to-typescript to regenerate this file.
*/
`,
style: {singleQuote: true, bracketSpacing: true},
declareExternallyReferenced: true,
enableConstEnums: false,
unreachableDefinitions: true,
strictIndexSignatures: false
});
// Cleanup: Remove the wrapper interface and convert to 'export declare interface'
// We use \n} to match the closing brace at the start of a line to avoid matching nested braces
const wrapperRegex = new RegExp(`export interface ${WRAPPER_NAME}\\s*\\{[\\s\\S]*?\\n\\}\\s*`, 'g');
ts = ts.replace(wrapperRegex, '')
.replace(/export interface/g, 'export declare interface');
// Replace (A | B)[] with Array<A | B>
ts = ts.replace(/:\s*\(([^)]+)\)\[\]/g, ': Array<$1>');
// Replace { ... }[] with Array<{ ... }>
ts = ts.replace(/:\s*(\{[^}]+\})\[\]/g, ': Array<$1>');
fs.writeFileSync(OUTPUT_FILE, ts.trim());
console.log(`Success! Types written to ${OUTPUT_FILE}`);
} catch (err) {
console.error('Error generating types:', err);
}
}
generate();