@amritk/generate-examples 0.5.1 → 0.5.3
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/LICENSE +21 -0
- package/dist/generators/build-schema.js +23 -50
- package/dist/generators/collect-example-imports.js +64 -94
- package/dist/generators/derive-example.js +507 -680
- package/dist/generators/find-schema-cycles.js +91 -115
- package/dist/generators/generate-arbitrary.js +316 -471
- package/dist/generators/generate-files.js +28 -45
- package/dist/generators/schema-validation.js +55 -78
- package/dist/index.js +10 -3
- package/package.json +4 -4
|
@@ -1,124 +1,100 @@
|
|
|
1
|
-
import { extractRefs } from
|
|
2
|
-
import { refToFilename } from
|
|
3
|
-
import { walkRefGraph } from
|
|
4
|
-
/**
|
|
5
|
-
* Collects the ref graph as an adjacency map of filename → referenced
|
|
6
|
-
* filenames, restricted to nodes that are actually generated as files. Self
|
|
7
|
-
* edges are dropped (they are `fc.letrec` recursion, not cross-file cycles).
|
|
8
|
-
*/
|
|
1
|
+
import { extractRefs } from "@amritk/helpers/extract-refs";
|
|
2
|
+
import { refToFilename } from "@amritk/helpers/ref-to-filename";
|
|
3
|
+
import { walkRefGraph } from "@amritk/helpers/walk-ref-graph";
|
|
9
4
|
const buildRefGraph = (rootSchema, rootTypeName, typeSuffix) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// never becomes an eager cross-module reference.
|
|
25
|
-
if (target !== filename && schemas.has(target))
|
|
26
|
-
targets.add(target);
|
|
27
|
-
}
|
|
28
|
-
graph.set(filename, targets);
|
|
5
|
+
const schemas = /* @__PURE__ */ new Map();
|
|
6
|
+
walkRefGraph(rootSchema, rootTypeName, { typeSuffix }, (node) => {
|
|
7
|
+
if (node.filename === "index")
|
|
8
|
+
return;
|
|
9
|
+
if (!schemas.has(node.filename))
|
|
10
|
+
schemas.set(node.filename, node.schema);
|
|
11
|
+
});
|
|
12
|
+
const graph = /* @__PURE__ */ new Map();
|
|
13
|
+
for (const [filename, schema] of schemas) {
|
|
14
|
+
const targets = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const ref of extractRefs(schema)) {
|
|
16
|
+
const target = refToFilename(ref);
|
|
17
|
+
if (target !== filename && schemas.has(target))
|
|
18
|
+
targets.add(target);
|
|
29
19
|
}
|
|
30
|
-
|
|
20
|
+
graph.set(filename, targets);
|
|
21
|
+
}
|
|
22
|
+
return graph;
|
|
31
23
|
};
|
|
32
|
-
/**
|
|
33
|
-
* Tarjan's strongly-connected-components algorithm, iterative so a deep ref
|
|
34
|
-
* graph cannot overflow the call stack. Returns one array of filenames per SCC.
|
|
35
|
-
*/
|
|
36
24
|
const stronglyConnectedComponents = (graph) => {
|
|
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
|
-
next = frame.iter.next();
|
|
73
|
-
}
|
|
74
|
-
if (descended)
|
|
75
|
-
continue;
|
|
76
|
-
// All successors visited: close this node.
|
|
77
|
-
if (lowlink.get(node) === index.get(node)) {
|
|
78
|
-
const scc = [];
|
|
79
|
-
let member;
|
|
80
|
-
do {
|
|
81
|
-
member = stack.pop();
|
|
82
|
-
onStack.delete(member);
|
|
83
|
-
scc.push(member);
|
|
84
|
-
} while (member !== node);
|
|
85
|
-
sccs.push(scc);
|
|
86
|
-
}
|
|
87
|
-
callStack.pop();
|
|
88
|
-
const parent = callStack[callStack.length - 1];
|
|
89
|
-
if (parent) {
|
|
90
|
-
lowlink.set(parent.node, Math.min(lowlink.get(parent.node), lowlink.get(node)));
|
|
91
|
-
}
|
|
25
|
+
let counter = 0;
|
|
26
|
+
const index = /* @__PURE__ */ new Map();
|
|
27
|
+
const lowlink = /* @__PURE__ */ new Map();
|
|
28
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
29
|
+
const stack = [];
|
|
30
|
+
const sccs = [];
|
|
31
|
+
for (const start of graph.keys()) {
|
|
32
|
+
if (index.has(start))
|
|
33
|
+
continue;
|
|
34
|
+
const callStack = [{ node: start, iter: (graph.get(start) ?? /* @__PURE__ */ new Set()).values() }];
|
|
35
|
+
index.set(start, counter);
|
|
36
|
+
lowlink.set(start, counter);
|
|
37
|
+
counter++;
|
|
38
|
+
stack.push(start);
|
|
39
|
+
onStack.add(start);
|
|
40
|
+
while (callStack.length > 0) {
|
|
41
|
+
const frame = callStack[callStack.length - 1];
|
|
42
|
+
const node = frame.node;
|
|
43
|
+
let descended = false;
|
|
44
|
+
let next = frame.iter.next();
|
|
45
|
+
while (!next.done) {
|
|
46
|
+
const child = next.value;
|
|
47
|
+
if (!index.has(child)) {
|
|
48
|
+
index.set(child, counter);
|
|
49
|
+
lowlink.set(child, counter);
|
|
50
|
+
counter++;
|
|
51
|
+
stack.push(child);
|
|
52
|
+
onStack.add(child);
|
|
53
|
+
callStack.push({ node: child, iter: (graph.get(child) ?? /* @__PURE__ */ new Set()).values() });
|
|
54
|
+
descended = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (onStack.has(child)) {
|
|
58
|
+
lowlink.set(node, Math.min(lowlink.get(node), index.get(child)));
|
|
92
59
|
}
|
|
60
|
+
next = frame.iter.next();
|
|
61
|
+
}
|
|
62
|
+
if (descended)
|
|
63
|
+
continue;
|
|
64
|
+
if (lowlink.get(node) === index.get(node)) {
|
|
65
|
+
const scc = [];
|
|
66
|
+
let member;
|
|
67
|
+
do {
|
|
68
|
+
member = stack.pop();
|
|
69
|
+
onStack.delete(member);
|
|
70
|
+
scc.push(member);
|
|
71
|
+
} while (member !== node);
|
|
72
|
+
sccs.push(scc);
|
|
73
|
+
}
|
|
74
|
+
callStack.pop();
|
|
75
|
+
const parent = callStack[callStack.length - 1];
|
|
76
|
+
if (parent) {
|
|
77
|
+
lowlink.set(parent.node, Math.min(lowlink.get(parent.node), lowlink.get(node)));
|
|
78
|
+
}
|
|
93
79
|
}
|
|
94
|
-
|
|
80
|
+
}
|
|
81
|
+
return sccs;
|
|
95
82
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/
|
|
108
|
-
export const findSchemaCycles = (rootSchema, rootTypeName, typeSuffix = '') => {
|
|
109
|
-
const graph = buildRefGraph(rootSchema, rootTypeName, typeSuffix);
|
|
110
|
-
const cycles = new Map();
|
|
111
|
-
for (const scc of stronglyConnectedComponents(graph)) {
|
|
112
|
-
// A single-file component is either an isolated node or pure self-recursion;
|
|
113
|
-
// neither needs cross-file lazy references.
|
|
114
|
-
if (scc.length < 2)
|
|
115
|
-
continue;
|
|
116
|
-
const members = new Set(scc);
|
|
117
|
-
for (const member of scc) {
|
|
118
|
-
const siblings = new Set(members);
|
|
119
|
-
siblings.delete(member);
|
|
120
|
-
cycles.set(member, siblings);
|
|
121
|
-
}
|
|
83
|
+
const findSchemaCycles = (rootSchema, rootTypeName, typeSuffix = "") => {
|
|
84
|
+
const graph = buildRefGraph(rootSchema, rootTypeName, typeSuffix);
|
|
85
|
+
const cycles = /* @__PURE__ */ new Map();
|
|
86
|
+
for (const scc of stronglyConnectedComponents(graph)) {
|
|
87
|
+
if (scc.length < 2)
|
|
88
|
+
continue;
|
|
89
|
+
const members = new Set(scc);
|
|
90
|
+
for (const member of scc) {
|
|
91
|
+
const siblings = new Set(members);
|
|
92
|
+
siblings.delete(member);
|
|
93
|
+
cycles.set(member, siblings);
|
|
122
94
|
}
|
|
123
|
-
|
|
95
|
+
}
|
|
96
|
+
return cycles;
|
|
97
|
+
};
|
|
98
|
+
export {
|
|
99
|
+
findSchemaCycles
|
|
124
100
|
};
|