Skip to content

Commit a8effba

Browse files
committed
Manual dereference :fingers-crossed:
1 parent a667f40 commit a8effba

File tree

5 files changed

+21418
-4035
lines changed

5 files changed

+21418
-4035
lines changed

.swcrc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://swc.rs/schema.json",
3+
"jsc": {
4+
"parser": {
5+
"syntax": "typescript",
6+
"topLevelAwait": true
7+
},
8+
"target": "esnext",
9+
"baseUrl": "."
10+
},
11+
"module": {
12+
"type": "nodenext"
13+
},
14+
"isModule": true
15+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
},
133133
"scripts": {
134134
"gen:parsers": "yarn --cwd packages/kas gen:parsers",
135-
"gen:schema": "utils/generate-schema.ts",
135+
"gen:schema": "SWCRC=true utils/generate-schema.ts",
136136
"prebuild": "yarn gen:parsers",
137137
"build": "rollup -c config/build/rollup.config.js",
138138
"build:types": "yarn tsc --build tsconfig-build.json",

utils/generate-schema.ts

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,121 @@ import path from "path";
55

66
import {createGenerator} from "ts-json-schema-generator";
77

8-
import type {Config} from "ts-json-schema-generator";
8+
import type {Config, SchemaGenerator} from "ts-json-schema-generator";
99

10-
const config: Config = {
11-
schemaId: "https://khanacademy.org/schema/perseus.json",
12-
tsconfig: path.join(__dirname, "..", "tsconfig.json"),
13-
jsDoc: "extended",
10+
const RefNameExtractorRegex = /#\/definitions\/(?<name>.*)/;
1411

15-
// type: "PerseusItem",
12+
const getRefDefinitionName = (ref: string) => {
13+
const match = ref.match(RefNameExtractorRegex);
14+
if (match) {
15+
return match.groups?.["name"];
16+
}
1617
};
1718

18-
const outputPath = path.join(__dirname, "generated/schema.json");
19-
fs.mkdirSync(path.dirname(outputPath), {recursive: true});
19+
function derefSchema(schema: ReturnType<SchemaGenerator["createSchema"]>) {
20+
const derefIfRef = (level, key, obj) => {
21+
try {
22+
if (
23+
obj != null &&
24+
typeof obj === "object" &&
25+
!Array.isArray(obj) &&
26+
"$ref" in obj
27+
) {
28+
const defName = getRefDefinitionName(obj["$ref"]);
29+
30+
if (defName != null) {
31+
return schema.definitions?.[defName];
32+
}
33+
}
34+
} catch (e) {
35+
console.log(key, obj);
36+
throw e;
37+
}
38+
};
39+
40+
const processProps = (path: string[], props, level: number = 0) => {
41+
if (props == null) {
42+
return;
43+
}
44+
45+
if (Array.isArray(props)) {
46+
for (let i = 0; i < props.length; i++) {
47+
const newPath = [...path, `[${i}]`];
48+
const refTarget = derefIfRef(level, i, props[i]);
49+
if (refTarget) {
50+
props[i] = refTarget;
51+
}
52+
53+
processProps(newPath, props[i], level + 1);
54+
}
55+
} else if (typeof props === "object") {
56+
for (const [key, val] of Object.entries(props)) {
57+
const newPath = [...path, key];
58+
const refTarget = derefIfRef(level, key, val);
59+
if (refTarget) {
60+
props[key] = refTarget;
61+
}
62+
63+
// Stop recursion of nested Renderers
64+
if (key === "widgets" && path.indexOf("widgets") !== -1) {
65+
delete props[key];
66+
continue;
67+
}
68+
processProps(newPath, props[key], level + 1);
69+
}
70+
}
71+
};
72+
73+
processProps(["root"], schema.properties);
74+
processProps(["root"], schema.additionalProperties);
75+
76+
return schema;
77+
}
2078

21-
const schema = createGenerator(config).createSchema("PerseusItem"); // config.type);
22-
const schemaString = JSON.stringify(schema, null, 2);
23-
fs.writeFile(outputPath, schemaString, (err) => {
24-
if (err) {
25-
throw err;
79+
function generateSchema() {
80+
const config: Config = {
81+
schemaId: "https://khanacademy.org/schema/perseus.json",
82+
tsconfig: path.join(__dirname, "..", "tsconfig.json"),
83+
jsDoc: "extended",
84+
expose: "all",
85+
functions: "hide",
86+
encodeRefs: false,
87+
88+
skipTypeCheck: true,
89+
};
90+
91+
const generatedDir = path.join(__dirname, "generated");
92+
const rawSchemaPath = path.join(generatedDir, "schema-raw.json");
93+
const schemaPath = path.join(generatedDir, "schema.json");
94+
fs.mkdirSync(path.dirname(generatedDir), {recursive: true});
95+
96+
console.log("Generating schema...");
97+
const schema = createGenerator(config).createSchema("PerseusItem");
98+
const perseusItem = schema.definitions?.["PerseusItem"];
99+
if (perseusItem == null) {
100+
throw new Error("Could not find PerseusItem type in defs!");
101+
}
102+
// Raise up the "PerseusItem" to the root
103+
schema.type = "object";
104+
for (const [name, val] of Object.entries(perseusItem)) {
105+
schema[name] = val;
26106
}
27-
});
107+
108+
const schemaString = JSON.stringify(schema, undefined, " ");
109+
fs.writeFileSync(rawSchemaPath, schemaString);
110+
111+
console.log("Dereferencing schema...");
112+
const dereferencedSchema = derefSchema(schema);
113+
fs.writeFileSync(
114+
schemaPath,
115+
JSON.stringify(dereferencedSchema, undefined, " "),
116+
);
117+
}
118+
119+
try {
120+
generateSchema();
121+
} catch (e) {
122+
console.log("Script failed!!!");
123+
console.error(e);
124+
process.exit(1);
125+
}

0 commit comments

Comments
 (0)