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
|
// Helpers common to #find and #reverse logic.
import thingConstructors from '#things';
export function getAllSpecs({
word,
constructorKey,
hardcodedSpecs,
postprocessSpec,
}) {
try {
thingConstructors;
} catch (error) {
throw new Error(`Thing constructors aren't ready yet, can't get all ${word} specs`);
}
const specs = {...hardcodedSpecs};
for (const thingConstructor of Object.values(thingConstructors)) {
const thingSpecs = thingConstructor[constructorKey];
if (!thingSpecs) continue;
for (const [key, spec] of Object.entries(thingSpecs)) {
specs[key] =
postprocessSpec(spec, {
thingConstructor,
});
}
}
return specs;
}
export function findSpec(key, {
word,
constructorKey,
hardcodedSpecs,
postprocessSpec,
}) {
if (Object.hasOwn(hardcodedSpecs, key)) {
return hardcodedSpecs[key];
}
try {
thingConstructors;
} catch (error) {
throw new Error(`Thing constructors aren't ready yet, can't check if "${word}.${key}" available`);
}
for (const thingConstructor of Object.values(thingConstructors)) {
const thingSpecs = thingConstructor[constructorKey];
if (!thingSpecs) continue;
if (Object.hasOwn(thingSpecs, key)) {
return postprocessSpec(thingSpecs[key], {
thingConstructor,
});
}
}
throw new Error(`"${word}.${key}" isn't available`);
}
export function tokenProxy({
findSpec,
prepareBehavior,
handle: customHandle =
(_key) => undefined,
}) {
return new Proxy({}, {
get: (store, key) => {
const custom = customHandle(key);
if (custom !== undefined) {
return custom;
}
if (!Object.hasOwn(store, key)) {
let behavior = (...args) => {
// This will error if the spec isn't available...
const spec = findSpec(key);
// ...or, if it is available, replace this function with the
// ready-for-use find function made out of that spec.
return (behavior = prepareBehavior(spec))(...args);
};
store[key] = (...args) => behavior(...args);
store[key][tokenKey] = key;
}
return store[key];
},
});
}
export function bind(wikiData, opts1, {
getAllSpecs,
prepareBehavior,
}) {
const specs = getAllSpecs();
const bound = {};
for (const [key, spec] of Object.entries(specs)) {
if (!spec.bindTo) continue;
const behavior = prepareBehavior(spec);
const data =
(spec.bindTo === 'wikiData'
? wikiData
: wikiData[spec.bindTo]);
bound[key] =
(opts1
? (ref, opts2) =>
(opts2
? behavior(ref, data, {...opts1, ...opts2})
: behavior(ref, data, opts1))
: (ref, opts2) =>
(opts2
? behavior(ref, data, opts2)
: behavior(ref, data)));
bound[key][boundData] = data;
bound[key][boundOptions] = opts1 ?? {};
}
return bound;
}
export const tokenKey = Symbol.for('find.tokenKey');
export const boundData = Symbol.for('find.boundData');
export const boundOptions = Symbol.for('find.boundOptions');
|