@akanjs/language 3.2.0-rc.1 → 3.2.0-rc.10
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/adaptorResolver.d.ts +18 -0
- package/akanGraph.d.ts +1 -1
- package/akanGraphDiff.d.ts +1 -1
- package/escapeRegExp.d.ts +1 -0
- package/guardFacts.d.ts +50 -0
- package/guardResolver.d.ts +75 -0
- package/index.cjs +1164 -436
- package/index.js +1164 -436
- package/moduleFacts.d.ts +9 -14
- package/package.json +1 -1
- package/routeFacts.d.ts +5 -2
- package/server.js +1120 -435
package/server.js
CHANGED
|
@@ -5,6 +5,261 @@ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "n
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
|
|
8
|
+
// pkgs/@akanjs/language/escapeRegExp.ts
|
|
9
|
+
var regExpSpecials = /[\\^$.*+?()[\]{}|]/g;
|
|
10
|
+
var escapeRegExp = (text) => text.replace(regExpSpecials, (special) => `\\${special}`);
|
|
11
|
+
|
|
12
|
+
// pkgs/@akanjs/language/guardFacts.ts
|
|
13
|
+
var guardLabelOf = (guard, labels) => {
|
|
14
|
+
const [, role = null, local = guard] = guard.match(/^([\w$]+) (.+)$/) ?? [];
|
|
15
|
+
const label = labels[local];
|
|
16
|
+
return label === undefined ? guard : role ? `${role} ${label}` : label;
|
|
17
|
+
};
|
|
18
|
+
var maxLabelArgs = 32;
|
|
19
|
+
var maxLayers = 4;
|
|
20
|
+
var guardRefReaderOf = (ts, ast, unwrap) => {
|
|
21
|
+
const pathOf = (expression) => {
|
|
22
|
+
if (ts.isIdentifier(expression))
|
|
23
|
+
return expression.text;
|
|
24
|
+
if (!ts.isPropertyAccessExpression(expression))
|
|
25
|
+
return null;
|
|
26
|
+
const head = pathOf(expression.expression);
|
|
27
|
+
return head && `${head}.${expression.name.text}`;
|
|
28
|
+
};
|
|
29
|
+
const layersOf = (call) => {
|
|
30
|
+
const inner = unwrap(call.expression);
|
|
31
|
+
if (!ts.isCallExpression(inner))
|
|
32
|
+
return { root: call, layers: 0 };
|
|
33
|
+
const { root, layers } = layersOf(inner);
|
|
34
|
+
return { root, layers: layers + 1 };
|
|
35
|
+
};
|
|
36
|
+
const calleeOf = (call) => pathOf(unwrap(layersOf(call).root.expression)) ?? "?";
|
|
37
|
+
const labelOf = (source, call) => {
|
|
38
|
+
const args = call.arguments.map((arg) => arg.getText(source)).join(", ");
|
|
39
|
+
const inner = unwrap(call.expression);
|
|
40
|
+
const head = ts.isCallExpression(inner) ? labelOf(source, inner) : pathOf(inner) ?? "?";
|
|
41
|
+
return `${head}(${args.length <= maxLabelArgs && !args.includes(`
|
|
42
|
+
`) ? args : "…"})`;
|
|
43
|
+
};
|
|
44
|
+
const guardRefOf = (expression, made, member) => {
|
|
45
|
+
const value = unwrap(expression);
|
|
46
|
+
if (!ts.isCallExpression(value))
|
|
47
|
+
return pathOf(value) ?? ast.expressionName(value);
|
|
48
|
+
const prefix = `${member}:${calleeOf(value)}@`;
|
|
49
|
+
const name = `${prefix}${made.filter((other) => other.name.startsWith(prefix)).length + 1}`;
|
|
50
|
+
made.push({ call: value, name });
|
|
51
|
+
return name;
|
|
52
|
+
};
|
|
53
|
+
const guardsIn = (source, object, made, member) => {
|
|
54
|
+
const guards = object?.properties.find((property) => ts.isPropertyAssignment(property) && ast.nodeName(property.name) === "guards")?.initializer;
|
|
55
|
+
if (!guards)
|
|
56
|
+
return [];
|
|
57
|
+
const refOf = (expression) => guardRefOf(expression, made, member);
|
|
58
|
+
if (ts.isArrayLiteralExpression(guards))
|
|
59
|
+
return guards.elements.map(refOf).filter((name) => name !== null);
|
|
60
|
+
if (ts.isObjectLiteralExpression(guards))
|
|
61
|
+
return guards.properties.filter(ts.isPropertyAssignment).flatMap((property) => {
|
|
62
|
+
const value = unwrap(property.initializer);
|
|
63
|
+
const names = ts.isArrayLiteralExpression(value) ? value.elements.map(refOf) : [refOf(value)];
|
|
64
|
+
return names.map((name) => `${ast.nodeName(property.name)} ${name ?? "?"}`);
|
|
65
|
+
});
|
|
66
|
+
const single = refOf(guards);
|
|
67
|
+
return single ? [single] : [];
|
|
68
|
+
};
|
|
69
|
+
return { pathOf, layersOf, labelOf, guardsIn };
|
|
70
|
+
};
|
|
71
|
+
var guardFactoryReaderOf = (ts, ast, { spanOf, extentOf, unwrap, docOf }, pathOf, layersOf) => {
|
|
72
|
+
const implementsGuard = (node, guardNames) => !!node.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ImplementsKeyword && clause.types.some(({ expression }) => guardNames.has(ast.expressionName(expression) ?? "")));
|
|
73
|
+
const scopeOf = (node) => {
|
|
74
|
+
const scope = node.members.find((member) => ts.isPropertyDeclaration(member) && ast.nodeName(member.name) === "scope" && !!ts.getModifiers(member)?.some(({ kind }) => kind === ts.SyntaxKind.StaticKeyword))?.initializer;
|
|
75
|
+
const value = scope && unwrap(scope);
|
|
76
|
+
return value && ts.isStringLiteralLike(value) ? value.text : null;
|
|
77
|
+
};
|
|
78
|
+
const functionsOf = (source) => {
|
|
79
|
+
const functions = new Map;
|
|
80
|
+
for (const statement of source.statements) {
|
|
81
|
+
if (ts.isFunctionDeclaration(statement) && statement.name && statement.body)
|
|
82
|
+
functions.set(statement.name.text, { statement, name: statement.name, body: statement.body });
|
|
83
|
+
if (!ts.isVariableStatement(statement))
|
|
84
|
+
continue;
|
|
85
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
86
|
+
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
87
|
+
if (initializer && ts.isIdentifier(declaration.name) && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)))
|
|
88
|
+
functions.set(declaration.name.text, { statement, name: declaration.name, body: initializer.body });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return functions;
|
|
92
|
+
};
|
|
93
|
+
const returnedOf = (body, guardNames, depth = 0) => {
|
|
94
|
+
const locals = new Map;
|
|
95
|
+
const pending = [];
|
|
96
|
+
const visit = (node) => {
|
|
97
|
+
if (ts.isClassDeclaration(node) && node.name)
|
|
98
|
+
locals.set(node.name.text, node);
|
|
99
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer)
|
|
100
|
+
locals.set(node.name.text, node.initializer);
|
|
101
|
+
if (ts.isReturnStatement(node) && node.expression)
|
|
102
|
+
pending.push(node.expression);
|
|
103
|
+
if (!ts.isFunctionLike(node) && !ts.isClassLike(node))
|
|
104
|
+
ts.forEachChild(node, visit);
|
|
105
|
+
};
|
|
106
|
+
if (ts.isBlock(body))
|
|
107
|
+
ts.forEachChild(body, visit);
|
|
108
|
+
else
|
|
109
|
+
pending.push(body);
|
|
110
|
+
let guard = null;
|
|
111
|
+
const calls = [];
|
|
112
|
+
const seen = new Set;
|
|
113
|
+
const passed = new Set;
|
|
114
|
+
const returned = [];
|
|
115
|
+
for (let node = pending.pop();node; node = pending.pop()) {
|
|
116
|
+
if (seen.has(node))
|
|
117
|
+
continue;
|
|
118
|
+
seen.add(node);
|
|
119
|
+
const value = ts.isExpression(node) ? unwrap(node) : node;
|
|
120
|
+
if ((ts.isClassExpression(value) || ts.isClassDeclaration(value)) && implementsGuard(value, guardNames))
|
|
121
|
+
guard ??= value;
|
|
122
|
+
else if ((ts.isArrowFunction(value) || ts.isFunctionExpression(value)) && !passed.has(node) && depth < maxLayers)
|
|
123
|
+
returned.push(value.body);
|
|
124
|
+
else if (ts.isConditionalExpression(value))
|
|
125
|
+
pending.push(value.whenTrue, value.whenFalse);
|
|
126
|
+
else if (ts.isBinaryExpression(value))
|
|
127
|
+
pending.push(value.left, value.right);
|
|
128
|
+
else if (ts.isIdentifier(value)) {
|
|
129
|
+
const local = locals.get(value.text);
|
|
130
|
+
if (local)
|
|
131
|
+
pending.push(local);
|
|
132
|
+
} else if (ts.isCallExpression(value)) {
|
|
133
|
+
const { root, layers } = layersOf(value);
|
|
134
|
+
const callee = pathOf(unwrap(root.expression));
|
|
135
|
+
if (callee && !passed.has(node) && !calls.some((call) => call.callee === callee))
|
|
136
|
+
calls.push({ callee, layers });
|
|
137
|
+
for (let call = value;ts.isCallExpression(call); call = unwrap(call.expression)) {
|
|
138
|
+
for (const arg of call.arguments)
|
|
139
|
+
passed.add(arg);
|
|
140
|
+
pending.push(...call.arguments);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const [inner] = returned;
|
|
145
|
+
if (guard || calls.length || !inner)
|
|
146
|
+
return { guard, calls, layers: 0 };
|
|
147
|
+
const nested = returnedOf(inner, guardNames, depth + 1);
|
|
148
|
+
return { ...nested, layers: nested.layers + 1 };
|
|
149
|
+
};
|
|
150
|
+
const guardFactoriesOf = (source, guardNames, isImported) => {
|
|
151
|
+
const functions = functionsOf(source);
|
|
152
|
+
const returned = new Map([...functions].map(([name, { body }]) => [name, returnedOf(body, guardNames)]));
|
|
153
|
+
const members = new Set;
|
|
154
|
+
const isFactoryCall = (callee) => members.has(callee) || !functions.has(callee) && isImported(callee);
|
|
155
|
+
for (let isGrowing = true;isGrowing; ) {
|
|
156
|
+
isGrowing = false;
|
|
157
|
+
for (const [name, { guard, calls }] of returned) {
|
|
158
|
+
if (members.has(name) || !guard && !calls.some(({ callee }) => isFactoryCall(callee)))
|
|
159
|
+
continue;
|
|
160
|
+
members.add(name);
|
|
161
|
+
isGrowing = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const factories = new Map;
|
|
165
|
+
for (const name of members) {
|
|
166
|
+
const found = functions.get(name);
|
|
167
|
+
const made = returned.get(name);
|
|
168
|
+
if (!found || !made)
|
|
169
|
+
continue;
|
|
170
|
+
factories.set(name, {
|
|
171
|
+
name,
|
|
172
|
+
span: spanOf(source, found.name),
|
|
173
|
+
extent: extentOf(source, found.statement),
|
|
174
|
+
scope: made.guard ? scopeOf(made.guard) : null,
|
|
175
|
+
doc: docOf(source, found.statement),
|
|
176
|
+
direct: !!made.guard,
|
|
177
|
+
via: made.calls.filter(({ callee }) => isFactoryCall(callee)),
|
|
178
|
+
layers: made.layers
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return factories;
|
|
182
|
+
};
|
|
183
|
+
return { implementsGuard, scopeOf, guardFactoriesOf };
|
|
184
|
+
};
|
|
185
|
+
var createGuardFactReader = (ts, ast, base) => {
|
|
186
|
+
const { spanOf, extentOf, unwrap, docOf } = base;
|
|
187
|
+
const { pathOf, layersOf, labelOf, guardsIn } = guardRefReaderOf(ts, ast, unwrap);
|
|
188
|
+
const { implementsGuard, scopeOf, guardFactoriesOf } = guardFactoryReaderOf(ts, ast, base, pathOf, layersOf);
|
|
189
|
+
const guardsOf = (source, imports, madeInPlace) => {
|
|
190
|
+
const guardNames = new Set([
|
|
191
|
+
"Guard",
|
|
192
|
+
...imports.filter(({ imported }) => imported === "Guard").map(({ name }) => name)
|
|
193
|
+
]);
|
|
194
|
+
const imported = new Set(imports.map(({ name }) => name));
|
|
195
|
+
const isImported = (callee) => imported.has(callee.split(".")[0] ?? callee);
|
|
196
|
+
const factories = guardFactoriesOf(source, guardNames, isImported);
|
|
197
|
+
const guards = [];
|
|
198
|
+
const calls = [];
|
|
199
|
+
const makeOf = (made, value, isInPlace = false) => {
|
|
200
|
+
if (ts.isClassExpression(value) && implementsGuard(value, guardNames)) {
|
|
201
|
+
guards.push({ ...made, scope: scopeOf(value), factory: null, layers: 0 });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const { root, layers } = ts.isCallExpression(value) ? layersOf(value) : { root: null, layers: 0 };
|
|
205
|
+
const callee = root && pathOf(unwrap(root.expression));
|
|
206
|
+
const factory = callee && factories.get(callee);
|
|
207
|
+
if (factory)
|
|
208
|
+
guards.push({ ...made, scope: factory.scope, factory: factory.name, layers });
|
|
209
|
+
else if (callee && (isImported(callee) || isInPlace))
|
|
210
|
+
calls.push({ ...made, callee, kind: "call", scope: null, layers });
|
|
211
|
+
};
|
|
212
|
+
for (const statement of source.statements) {
|
|
213
|
+
if (ts.isClassDeclaration(statement) && statement.name) {
|
|
214
|
+
const declared = {
|
|
215
|
+
name: statement.name.text,
|
|
216
|
+
span: spanOf(source, statement.name),
|
|
217
|
+
extent: extentOf(source, statement),
|
|
218
|
+
scope: scopeOf(statement),
|
|
219
|
+
doc: docOf(source, statement)
|
|
220
|
+
};
|
|
221
|
+
const base = statement.heritageClauses?.find(({ token }) => token === ts.SyntaxKind.ExtendsKeyword)?.types[0];
|
|
222
|
+
const callee = base && pathOf(base.expression);
|
|
223
|
+
if (implementsGuard(statement, guardNames))
|
|
224
|
+
guards.push({ ...declared, factory: null, layers: 0 });
|
|
225
|
+
else if (callee)
|
|
226
|
+
calls.push({ ...declared, callee, kind: "extends", layers: 0 });
|
|
227
|
+
}
|
|
228
|
+
if (!ts.isVariableStatement(statement))
|
|
229
|
+
continue;
|
|
230
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
231
|
+
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
232
|
+
if (!initializer || !ts.isIdentifier(declaration.name))
|
|
233
|
+
continue;
|
|
234
|
+
const holder = declaration.name.text;
|
|
235
|
+
makeOf({
|
|
236
|
+
name: holder,
|
|
237
|
+
span: spanOf(source, declaration.name),
|
|
238
|
+
extent: extentOf(source, statement),
|
|
239
|
+
doc: docOf(source, statement)
|
|
240
|
+
}, initializer);
|
|
241
|
+
if (!ts.isObjectLiteralExpression(initializer))
|
|
242
|
+
continue;
|
|
243
|
+
for (const property of initializer.properties) {
|
|
244
|
+
const key = ts.isPropertyAssignment(property) ? ast.nodeName(property.name) : null;
|
|
245
|
+
if (key && ts.isPropertyAssignment(property))
|
|
246
|
+
makeOf({
|
|
247
|
+
name: `${holder}.${key}`,
|
|
248
|
+
span: spanOf(source, property.name),
|
|
249
|
+
extent: extentOf(source, property),
|
|
250
|
+
doc: docOf(source, property)
|
|
251
|
+
}, unwrap(property.initializer));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
for (const { call, name } of madeInPlace)
|
|
256
|
+
makeOf({ name, span: spanOf(source, call), extent: extentOf(source, call), doc: null }, call, true);
|
|
257
|
+
const guardLabels = Object.fromEntries(madeInPlace.map(({ call, name }) => [name, labelOf(source, call)]));
|
|
258
|
+
return { guards, guardFactories: [...factories.values()], guardCalls: calls, guardLabels };
|
|
259
|
+
};
|
|
260
|
+
return { guardsIn, guardsOf };
|
|
261
|
+
};
|
|
262
|
+
|
|
8
263
|
// pkgs/@akanjs/language/moduleFacts.ts
|
|
9
264
|
var testFilePattern = /\.(?:test|spec)\.tsx?$/;
|
|
10
265
|
var moduleCallees = new Set([
|
|
@@ -73,21 +328,6 @@ var createFactReader = (ts, ast) => {
|
|
|
73
328
|
const value = unwrap(expression);
|
|
74
329
|
return ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression) ? `${value.expression.text}.${value.name.text}` : ast.expressionName(value);
|
|
75
330
|
};
|
|
76
|
-
const guardsIn = (object) => {
|
|
77
|
-
const guards = object?.properties.find((property) => ts.isPropertyAssignment(property) && ast.nodeName(property.name) === "guards")?.initializer;
|
|
78
|
-
if (!guards)
|
|
79
|
-
return [];
|
|
80
|
-
if (ts.isArrayLiteralExpression(guards))
|
|
81
|
-
return guards.elements.map(guardNameOf).filter((name) => name !== null);
|
|
82
|
-
if (ts.isObjectLiteralExpression(guards))
|
|
83
|
-
return guards.properties.filter(ts.isPropertyAssignment).flatMap((property) => {
|
|
84
|
-
const value = unwrap(property.initializer);
|
|
85
|
-
const names = ts.isArrayLiteralExpression(value) ? value.elements.map(guardNameOf) : [guardNameOf(value)];
|
|
86
|
-
return names.map((name) => `${ast.nodeName(property.name)} ${name ?? "?"}`);
|
|
87
|
-
});
|
|
88
|
-
const single = guardNameOf(guards);
|
|
89
|
-
return single ? [single] : [];
|
|
90
|
-
};
|
|
91
331
|
const optionObjectOf = (call) => call.arguments.find((arg) => ts.isObjectLiteralExpression(arg) && arg.properties.some((property) => ast.propertyName(property) === "guards"));
|
|
92
332
|
const chainOf = (initializer) => {
|
|
93
333
|
let root = unwrap(initializer);
|
|
@@ -104,7 +344,7 @@ var createFactReader = (ts, ast) => {
|
|
|
104
344
|
const node = keyName(property);
|
|
105
345
|
return name && node ? [{ name, span: spanOf(source, node), extent: extentOf(source, property) }] : [];
|
|
106
346
|
});
|
|
107
|
-
const sliceKeysOf = (source, builder) => (returnedObject(builder)?.properties ?? []).flatMap((property) => {
|
|
347
|
+
const sliceKeysOf = (source, builder, made, model) => (returnedObject(builder)?.properties ?? []).flatMap((property) => {
|
|
108
348
|
if (!ts.isPropertyAssignment(property))
|
|
109
349
|
return [];
|
|
110
350
|
const name = ast.nodeName(property.name);
|
|
@@ -116,7 +356,7 @@ var createFactReader = (ts, ast) => {
|
|
|
116
356
|
name,
|
|
117
357
|
span: spanOf(source, property.name),
|
|
118
358
|
extent: extentOf(source, property),
|
|
119
|
-
guards: root ? guardsIn(optionObjectOf(root)) : [],
|
|
359
|
+
guards: root ? guardsIn(source, optionObjectOf(root), made, `${model}.${name}`) : [],
|
|
120
360
|
live: methods.includes("live"),
|
|
121
361
|
argCount: methods.filter((method) => argMethods.has(method)).length
|
|
122
362
|
}
|
|
@@ -166,7 +406,7 @@ var createFactReader = (ts, ast) => {
|
|
|
166
406
|
const [size, unit] = intervalUnits.find(([size]) => ms >= size && ms % size === 0) ?? [1, "ms"];
|
|
167
407
|
return `${ms / size}${unit}`;
|
|
168
408
|
};
|
|
169
|
-
const modesOf = (option) => {
|
|
409
|
+
const modesOf = (option, type) => {
|
|
170
410
|
const object = option && unwrap(option);
|
|
171
411
|
if (!object || !ts.isObjectLiteralExpression(object))
|
|
172
412
|
return [];
|
|
@@ -176,9 +416,9 @@ var createFactReader = (ts, ast) => {
|
|
|
176
416
|
if (name === "enabled")
|
|
177
417
|
return value.kind === ts.SyntaxKind.FalseKeyword ? ["disabled"] : [];
|
|
178
418
|
if (name === "once")
|
|
179
|
-
return value.kind === ts.SyntaxKind.TrueKeyword ? ["once"] : [];
|
|
419
|
+
return type === "initialize" && value.kind === ts.SyntaxKind.TrueKeyword ? ["once"] : [];
|
|
180
420
|
if (name === "lock")
|
|
181
|
-
return value.kind === ts.SyntaxKind.FalseKeyword ? ["unlocked"] : [];
|
|
421
|
+
return type === "interval" && value.kind === ts.SyntaxKind.FalseKeyword ? ["unlocked"] : [];
|
|
182
422
|
if (name !== "serverMode" && name !== "operationMode")
|
|
183
423
|
return [];
|
|
184
424
|
const values = ts.isArrayLiteralExpression(value) ? [...value.elements] : [value];
|
|
@@ -196,7 +436,8 @@ var createFactReader = (ts, ast) => {
|
|
|
196
436
|
const { root } = chainOf(property.initializer);
|
|
197
437
|
const type = root ? verbOf(root.expression) : null;
|
|
198
438
|
const [first, second] = root?.arguments ?? [];
|
|
199
|
-
const
|
|
439
|
+
const cron = type === "cron" && first ? unwrap(first) : null;
|
|
440
|
+
const schedule = cron ? ts.isStringLiteralLike(cron) ? cron.text : cron.getText(source) : type === "interval" || type === "timeout" ? durationOf(first, source) : null;
|
|
200
441
|
const option = type === "initialize" || type === "destroy" ? first : second;
|
|
201
442
|
return [
|
|
202
443
|
{
|
|
@@ -205,7 +446,7 @@ var createFactReader = (ts, ast) => {
|
|
|
205
446
|
extent: extentOf(source, property),
|
|
206
447
|
type,
|
|
207
448
|
schedule,
|
|
208
|
-
modes: modesOf(option)
|
|
449
|
+
modes: modesOf(option, type)
|
|
209
450
|
}
|
|
210
451
|
];
|
|
211
452
|
});
|
|
@@ -222,7 +463,20 @@ var createFactReader = (ts, ast) => {
|
|
|
222
463
|
return name && targetName ? [{ name, span: spanOf(source, property.name), target: targetName }] : [];
|
|
223
464
|
});
|
|
224
465
|
};
|
|
225
|
-
const
|
|
466
|
+
const adaptorBindingsIn = (source) => {
|
|
467
|
+
const bindings = [];
|
|
468
|
+
const visit = (node) => {
|
|
469
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "applyAdaptor") {
|
|
470
|
+
const [role, adaptor] = node.arguments.map(guardNameOf);
|
|
471
|
+
if (role && adaptor)
|
|
472
|
+
bindings.push({ role, adaptor, span: spanOf(source, node.expression.name) });
|
|
473
|
+
}
|
|
474
|
+
ts.forEachChild(node, visit);
|
|
475
|
+
};
|
|
476
|
+
visit(source);
|
|
477
|
+
return bindings;
|
|
478
|
+
};
|
|
479
|
+
const endpointKeysOf = (source, builder, made, model) => {
|
|
226
480
|
const verbOf = verbReaderOf(builder?.parameters[0]);
|
|
227
481
|
return (returnedObject(builder)?.properties ?? []).flatMap((property) => {
|
|
228
482
|
if (!ts.isPropertyAssignment(property))
|
|
@@ -237,7 +491,7 @@ var createFactReader = (ts, ast) => {
|
|
|
237
491
|
span: spanOf(source, property.name),
|
|
238
492
|
extent: extentOf(source, property),
|
|
239
493
|
verb: root ? verbOf(root.expression) : null,
|
|
240
|
-
guards: root ? guardsIn(optionObjectOf(root)) : [],
|
|
494
|
+
guards: root ? guardsIn(source, optionObjectOf(root), made, `${model}.${name}`) : [],
|
|
241
495
|
argCount: methods.filter((method) => argMethods.has(method)).length
|
|
242
496
|
}
|
|
243
497
|
];
|
|
@@ -304,22 +558,6 @@ var createFactReader = (ts, ast) => {
|
|
|
304
558
|
})) : []
|
|
305
559
|
];
|
|
306
560
|
};
|
|
307
|
-
const implementsGuard = (node, guardNames) => !!node.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ImplementsKeyword && clause.types.some(({ expression }) => guardNames.has(ast.expressionName(expression) ?? "")));
|
|
308
|
-
const guardClassIn = (node, guardNames) => {
|
|
309
|
-
if ((ts.isClassExpression(node) || ts.isClassDeclaration(node)) && implementsGuard(node, guardNames))
|
|
310
|
-
return node;
|
|
311
|
-
return ts.forEachChild(node, (child) => guardClassIn(child, guardNames)) ?? null;
|
|
312
|
-
};
|
|
313
|
-
const callsIn = (node, names) => {
|
|
314
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && names.has(node.expression.text))
|
|
315
|
-
return node.expression.text;
|
|
316
|
-
return ts.forEachChild(node, (child) => callsIn(child, names)) ?? null;
|
|
317
|
-
};
|
|
318
|
-
const scopeOf = (node) => {
|
|
319
|
-
const scope = node.members.find((member) => ts.isPropertyDeclaration(member) && ast.nodeName(member.name) === "scope" && !!ts.getModifiers(member)?.some(({ kind }) => kind === ts.SyntaxKind.StaticKeyword))?.initializer;
|
|
320
|
-
const value = scope && unwrap(scope);
|
|
321
|
-
return value && ts.isStringLiteralLike(value) ? value.text : null;
|
|
322
|
-
};
|
|
323
561
|
const docOf = (source, node) => {
|
|
324
562
|
const { text } = source;
|
|
325
563
|
const ranges = ts.getLeadingCommentRanges(text, node.getFullStart()) ?? [];
|
|
@@ -335,108 +573,20 @@ var createFactReader = (ts, ast) => {
|
|
|
335
573
|
`)).map((line) => line.trim().replace(/^\/\*\*?|\*\/$/g, "").replace(/^(?:\/\/[?*!]?|\*)/, "").replace(/\{@link\s+([^}\s|]+)[^}]*\}/g, "$1").trim()).filter((line) => line && !/^(?:biome-ignore|eslint-|@ts-)/.test(line));
|
|
336
574
|
return lines.length ? lines.join(" ") : null;
|
|
337
575
|
};
|
|
338
|
-
const
|
|
339
|
-
const functions = new Map;
|
|
340
|
-
for (const statement of source.statements) {
|
|
341
|
-
if (ts.isFunctionDeclaration(statement) && statement.name && statement.body)
|
|
342
|
-
functions.set(statement.name.text, { statement, name: statement.name, body: statement.body });
|
|
343
|
-
if (!ts.isVariableStatement(statement))
|
|
344
|
-
continue;
|
|
345
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
346
|
-
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
347
|
-
if (initializer && ts.isIdentifier(declaration.name) && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)))
|
|
348
|
-
functions.set(declaration.name.text, { statement, name: declaration.name, body: initializer.body });
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return functions;
|
|
352
|
-
};
|
|
353
|
-
const guardFactoriesOf = (source, guardNames) => {
|
|
354
|
-
const functions = functionsOf(source);
|
|
355
|
-
const factories = new Map;
|
|
356
|
-
const factoryOf = (name, scope, via) => {
|
|
357
|
-
const found = functions.get(name);
|
|
358
|
-
if (!found)
|
|
359
|
-
return;
|
|
360
|
-
factories.set(name, {
|
|
361
|
-
name,
|
|
362
|
-
span: spanOf(source, found.name),
|
|
363
|
-
extent: extentOf(source, found.statement),
|
|
364
|
-
scope,
|
|
365
|
-
doc: docOf(source, found.statement),
|
|
366
|
-
via
|
|
367
|
-
});
|
|
368
|
-
};
|
|
369
|
-
for (const [name, { body }] of functions) {
|
|
370
|
-
const guard = guardClassIn(body, guardNames);
|
|
371
|
-
if (guard)
|
|
372
|
-
factoryOf(name, scopeOf(guard), null);
|
|
373
|
-
}
|
|
374
|
-
for (let isGrowing = true;isGrowing; ) {
|
|
375
|
-
isGrowing = false;
|
|
376
|
-
for (const [name, { body }] of functions) {
|
|
377
|
-
const called = factories.has(name) ? null : callsIn(body, new Set(factories.keys()));
|
|
378
|
-
if (!called)
|
|
379
|
-
continue;
|
|
380
|
-
factoryOf(name, factories.get(called)?.scope ?? null, called);
|
|
381
|
-
isGrowing = true;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return factories;
|
|
385
|
-
};
|
|
386
|
-
const guardsOf = (source, imports) => {
|
|
387
|
-
const guardNames = new Set([
|
|
388
|
-
"Guard",
|
|
389
|
-
...imports.filter(({ imported }) => imported === "Guard").map(({ name }) => name)
|
|
390
|
-
]);
|
|
391
|
-
const imported = new Set(imports.map(({ name }) => name));
|
|
392
|
-
const factories = guardFactoriesOf(source, guardNames);
|
|
393
|
-
const guards = [];
|
|
394
|
-
const calls = [];
|
|
395
|
-
for (const statement of source.statements) {
|
|
396
|
-
if (ts.isClassDeclaration(statement) && statement.name && implementsGuard(statement, guardNames))
|
|
397
|
-
guards.push({
|
|
398
|
-
name: statement.name.text,
|
|
399
|
-
span: spanOf(source, statement.name),
|
|
400
|
-
extent: extentOf(source, statement),
|
|
401
|
-
scope: scopeOf(statement),
|
|
402
|
-
doc: docOf(source, statement),
|
|
403
|
-
factory: null
|
|
404
|
-
});
|
|
405
|
-
if (!ts.isVariableStatement(statement))
|
|
406
|
-
continue;
|
|
407
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
408
|
-
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
409
|
-
if (!initializer || !ts.isIdentifier(declaration.name))
|
|
410
|
-
continue;
|
|
411
|
-
const made = {
|
|
412
|
-
name: declaration.name.text,
|
|
413
|
-
span: spanOf(source, declaration.name),
|
|
414
|
-
extent: extentOf(source, statement),
|
|
415
|
-
doc: docOf(source, statement)
|
|
416
|
-
};
|
|
417
|
-
if (ts.isClassExpression(initializer) && implementsGuard(initializer, guardNames))
|
|
418
|
-
guards.push({ ...made, scope: scopeOf(initializer), factory: null });
|
|
419
|
-
if (!ts.isCallExpression(initializer) || !ts.isIdentifier(initializer.expression))
|
|
420
|
-
continue;
|
|
421
|
-
const callee = initializer.expression.text;
|
|
422
|
-
const factory = factories.get(callee);
|
|
423
|
-
if (factory)
|
|
424
|
-
guards.push({ ...made, scope: factory.scope, factory: callee });
|
|
425
|
-
else if (imported.has(callee))
|
|
426
|
-
calls.push({ ...made, callee });
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return { guards, guardFactories: [...factories.values()], guardCalls: calls };
|
|
430
|
-
};
|
|
576
|
+
const { guardsIn, guardsOf } = createGuardFactReader(ts, ast, { spanOf, extentOf, unwrap, docOf });
|
|
431
577
|
const lowerFirst = (text) => text.charAt(0).toLowerCase() + text.slice(1);
|
|
432
578
|
const importsIn = (source) => source.statements.filter(ts.isImportDeclaration).flatMap(importsOf);
|
|
433
579
|
const read = (source, owner) => {
|
|
434
580
|
const imports = importsIn(source);
|
|
581
|
+
const madeInPlace = [];
|
|
435
582
|
const facts = {
|
|
436
583
|
fileName: source.fileName,
|
|
437
584
|
owner,
|
|
438
585
|
imports,
|
|
439
|
-
|
|
586
|
+
guards: [],
|
|
587
|
+
guardFactories: [],
|
|
588
|
+
guardCalls: [],
|
|
589
|
+
guardLabels: {},
|
|
440
590
|
constants: [],
|
|
441
591
|
enums: [],
|
|
442
592
|
filters: [],
|
|
@@ -446,6 +596,7 @@ var createFactReader = (ts, ast) => {
|
|
|
446
596
|
endpoints: [],
|
|
447
597
|
internals: [],
|
|
448
598
|
adaptors: [],
|
|
599
|
+
adaptorBindings: source.text.includes("applyAdaptor") ? adaptorBindingsIn(source) : [],
|
|
449
600
|
stores: []
|
|
450
601
|
};
|
|
451
602
|
for (const statement of source.statements) {
|
|
@@ -470,16 +621,19 @@ var createFactReader = (ts, ast) => {
|
|
|
470
621
|
...name,
|
|
471
622
|
model: moduleRefOf(call.arguments[0], "db"),
|
|
472
623
|
methods: methodsOf(source, statement),
|
|
473
|
-
plugs: plugsOf(source, builder)
|
|
624
|
+
plugs: plugsOf(source, builder),
|
|
625
|
+
fields: objectKeys(source, returnedObject(builder)).map(({ name }) => name)
|
|
474
626
|
});
|
|
475
|
-
else if (callee === "slice")
|
|
627
|
+
else if (callee === "slice") {
|
|
628
|
+
const model = moduleRefOf(call.arguments[0], "srv");
|
|
629
|
+
const member = model ?? name.name;
|
|
476
630
|
facts.slices.push({
|
|
477
631
|
...name,
|
|
478
|
-
model
|
|
479
|
-
guards: guardsIn(call.arguments.find(ts.isObjectLiteralExpression)),
|
|
480
|
-
keys: sliceKeysOf(source, builder)
|
|
632
|
+
model,
|
|
633
|
+
guards: guardsIn(source, call.arguments.find(ts.isObjectLiteralExpression), madeInPlace, member),
|
|
634
|
+
keys: sliceKeysOf(source, builder, madeInPlace, member)
|
|
481
635
|
});
|
|
482
|
-
else if (callee === "internal")
|
|
636
|
+
} else if (callee === "internal")
|
|
483
637
|
facts.internals.push({
|
|
484
638
|
...name,
|
|
485
639
|
model: moduleRefOf(call.arguments[0], "srv"),
|
|
@@ -494,13 +648,14 @@ var createFactReader = (ts, ast) => {
|
|
|
494
648
|
plugs: plugsOf(source, builder),
|
|
495
649
|
doc: docOf(source, statement)
|
|
496
650
|
});
|
|
497
|
-
} else if (callee === "endpoint")
|
|
651
|
+
} else if (callee === "endpoint") {
|
|
652
|
+
const model = moduleRefOf(call.arguments[0], "srv");
|
|
498
653
|
facts.endpoints.push({
|
|
499
654
|
...name,
|
|
500
|
-
model
|
|
501
|
-
keys: endpointKeysOf(source, builder)
|
|
655
|
+
model,
|
|
656
|
+
keys: endpointKeysOf(source, builder, madeInPlace, model ?? name.name)
|
|
502
657
|
});
|
|
503
|
-
else if (callee === "store")
|
|
658
|
+
} else if (callee === "store")
|
|
504
659
|
facts.stores.push({
|
|
505
660
|
...name,
|
|
506
661
|
model: moduleRefOf(call.arguments[0], "sig"),
|
|
@@ -508,10 +663,647 @@ var createFactReader = (ts, ast) => {
|
|
|
508
663
|
actions: methodsOf(source, statement)
|
|
509
664
|
});
|
|
510
665
|
}
|
|
511
|
-
return facts;
|
|
512
|
-
};
|
|
513
|
-
return { read, spanOf, extentOf, docOf, importsIn, moduleRefOf, chainOf, unwrap };
|
|
514
|
-
};
|
|
666
|
+
return { ...facts, ...guardsOf(source, imports, madeInPlace) };
|
|
667
|
+
};
|
|
668
|
+
return { read, spanOf, extentOf, docOf, importsIn, moduleRefOf, chainOf, unwrap };
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
// pkgs/@akanjs/language/guardResolver.ts
|
|
672
|
+
var frameworkOwner = "akanjs";
|
|
673
|
+
var frameworkPattern = /^akanjs(?:\/|$)/;
|
|
674
|
+
var aliasPattern = /^@(apps|libs)\/([^/]+)/;
|
|
675
|
+
var maxMapDepth = 4;
|
|
676
|
+
var identifierPattern = /^[\p{ID_Start}$_][\p{ID_Continue}$\u200c\u200d]*$/u;
|
|
677
|
+
var starExportPattern = /export\s*\*\s*from\s*["']([^"']+)["']/g;
|
|
678
|
+
var exportingPatternOf = (name) => new RegExp(`export\\s*\\{(?:[^}]*?[\\s,])?${escapeRegExp(name)}(?![\\w$])`);
|
|
679
|
+
var declaringPatternOf = (name, isAnyBinding) => {
|
|
680
|
+
const escaped = escapeRegExp(name);
|
|
681
|
+
const braced = "\\{(?:[^}]*?[\\s,])?";
|
|
682
|
+
const leads = [
|
|
683
|
+
"function\\s*\\*?\\s*",
|
|
684
|
+
"(?:const|let|var)\\s+",
|
|
685
|
+
`export\\s*${braced}`,
|
|
686
|
+
...isAnyBinding ? ["class\\s+", `(?:const|let|var)\\s*${braced}`] : []
|
|
687
|
+
];
|
|
688
|
+
return new RegExp(`(?:${leads.join("|")})${escaped}(?![\\w$])|,\\s*${escaped}\\s*(?::[^=;,]+)?=(?![=>])`);
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
class GuardResolver {
|
|
692
|
+
#source;
|
|
693
|
+
#routes;
|
|
694
|
+
#guards = new Map;
|
|
695
|
+
#madeInPlace = new Map;
|
|
696
|
+
#mapEntries = new Map;
|
|
697
|
+
#reexports = new Map;
|
|
698
|
+
#declarations = new Map;
|
|
699
|
+
#starExports = new Map;
|
|
700
|
+
#moduleExports = new Map;
|
|
701
|
+
#ownerFiles = null;
|
|
702
|
+
#modules = null;
|
|
703
|
+
constructor(source, routes) {
|
|
704
|
+
this.#source = source;
|
|
705
|
+
this.#routes = routes;
|
|
706
|
+
}
|
|
707
|
+
find(local, site) {
|
|
708
|
+
return this.#findGuard(local, site, 0);
|
|
709
|
+
}
|
|
710
|
+
madeInPlace(fileName, name) {
|
|
711
|
+
return this.#madeInPlace.get(`${fileName}
|
|
712
|
+
${name}`);
|
|
713
|
+
}
|
|
714
|
+
declare(facts) {
|
|
715
|
+
const factories = new Map;
|
|
716
|
+
const declaredFactories = [];
|
|
717
|
+
for (const file of facts)
|
|
718
|
+
for (const fact of file.guardFactories) {
|
|
719
|
+
const factory = {
|
|
720
|
+
fact,
|
|
721
|
+
owner: file.owner,
|
|
722
|
+
site: file,
|
|
723
|
+
made: [],
|
|
724
|
+
isReal: fact.direct,
|
|
725
|
+
scope: fact.scope,
|
|
726
|
+
layers: fact.layers,
|
|
727
|
+
targets: []
|
|
728
|
+
};
|
|
729
|
+
factories.set(fact.name, [...factories.get(fact.name) ?? [], factory]);
|
|
730
|
+
declaredFactories.push(factory);
|
|
731
|
+
}
|
|
732
|
+
for (const factory of declaredFactories)
|
|
733
|
+
factory.targets = factory.fact.via.flatMap(({ callee, layers }) => {
|
|
734
|
+
const target = this.#factoryAt(factories, callee, factory.site);
|
|
735
|
+
return target ? [{ target, layers }] : [];
|
|
736
|
+
});
|
|
737
|
+
for (let isGrowing = true;isGrowing; ) {
|
|
738
|
+
isGrowing = false;
|
|
739
|
+
for (const factory of declaredFactories) {
|
|
740
|
+
const via = factory.targets.find(({ target, layers }) => target.isReal && target.layers >= layers);
|
|
741
|
+
if (factory.isReal || !via)
|
|
742
|
+
continue;
|
|
743
|
+
factory.isReal = true;
|
|
744
|
+
factory.layers = factory.fact.layers + via.target.layers - via.layers;
|
|
745
|
+
factory.scope ??= via.target.scope;
|
|
746
|
+
isGrowing = true;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
const partsOf = (factory, seen = new Set) => {
|
|
750
|
+
if (seen.has(factory))
|
|
751
|
+
return [];
|
|
752
|
+
seen.add(factory);
|
|
753
|
+
return [
|
|
754
|
+
{ fileName: factory.site.fileName, extent: factory.fact.extent },
|
|
755
|
+
...factory.targets.filter(({ target }) => target.isReal).flatMap(({ target }) => partsOf(target, seen))
|
|
756
|
+
];
|
|
757
|
+
};
|
|
758
|
+
const declare = (file, made, scope, parts) => {
|
|
759
|
+
const { name, span, extent, doc } = made;
|
|
760
|
+
const label = file.guardLabels[name];
|
|
761
|
+
const own = { fileName: file.fileName, extent };
|
|
762
|
+
const guard = { name, owner: file.owner, span, scope, doc, parts: [own, ...parts], file: null, label };
|
|
763
|
+
if (name.includes("@"))
|
|
764
|
+
this.#madeInPlace.set(`${file.fileName}
|
|
765
|
+
${name}`, guard);
|
|
766
|
+
else
|
|
767
|
+
this.#guards.set(name, [...this.#guards.get(name) ?? [], guard]);
|
|
768
|
+
return guard;
|
|
769
|
+
};
|
|
770
|
+
const extending = [];
|
|
771
|
+
for (const file of facts) {
|
|
772
|
+
for (const made of file.guards) {
|
|
773
|
+
const factory = made.factory ? factories.get(made.factory)?.find((other) => other.site.fileName === file.fileName) : undefined;
|
|
774
|
+
if (!made.factory)
|
|
775
|
+
declare(file, made, made.scope, []);
|
|
776
|
+
else if (factory?.isReal && factory.layers === made.layers)
|
|
777
|
+
factory.made.push(declare(file, made, factory.scope, partsOf(factory)));
|
|
778
|
+
}
|
|
779
|
+
for (const made of file.guardCalls) {
|
|
780
|
+
if (made.kind === "extends") {
|
|
781
|
+
extending.push([file, made]);
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
const factory = this.#factoryAt(factories, made.callee, file);
|
|
785
|
+
if (factory?.isReal && factory.layers === made.layers)
|
|
786
|
+
factory.made.push(declare(file, made, factory.scope, partsOf(factory)));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
for (let isGrowing = true;isGrowing; ) {
|
|
790
|
+
isGrowing = false;
|
|
791
|
+
for (const [idx, entry] of extending.entries()) {
|
|
792
|
+
const [file, made] = entry;
|
|
793
|
+
const found = this.#findGuard(made.callee, file, 0, true);
|
|
794
|
+
if (!("guard" in found))
|
|
795
|
+
continue;
|
|
796
|
+
declare(file, made, made.scope ?? found.guard.scope, found.guard.parts);
|
|
797
|
+
extending.splice(idx, 1);
|
|
798
|
+
isGrowing = true;
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
for (const { fact, made, isReal } of declaredFactories)
|
|
803
|
+
if (isReal && made.every((guard) => !guard.doc))
|
|
804
|
+
for (const guard of made)
|
|
805
|
+
guard.doc = fact.doc;
|
|
806
|
+
for (const list of this.#guards.values())
|
|
807
|
+
for (const guard of list)
|
|
808
|
+
if (list.some((other) => other !== guard && other.owner === guard.owner))
|
|
809
|
+
guard.file = this.inOwner(guard.span.fileName, guard.owner);
|
|
810
|
+
for (const guard of this.#madeInPlace.values())
|
|
811
|
+
guard.file = this.inOwner(guard.span.fileName, guard.owner);
|
|
812
|
+
return [...[...this.#guards.values()].flat(), ...this.#madeInPlace.values()];
|
|
813
|
+
}
|
|
814
|
+
#factoryAt(factories, callee, site, depth = 0) {
|
|
815
|
+
const [head = callee, member] = callee.split(".");
|
|
816
|
+
const imported = site.imports.find((item) => item.name === head);
|
|
817
|
+
if (!imported)
|
|
818
|
+
return member ? undefined : factories.get(callee)?.find((other) => other.site.fileName === site.fileName);
|
|
819
|
+
if (member && imported.imported !== "*")
|
|
820
|
+
return;
|
|
821
|
+
const from = this.ownerOfSpecifier(site.fileName, imported.from);
|
|
822
|
+
if (from === undefined || from === frameworkOwner)
|
|
823
|
+
return;
|
|
824
|
+
const name = member ?? (imported.imported === "default" || imported.imported === "*" ? head : imported.imported);
|
|
825
|
+
const candidates = factories.get(name) ?? [];
|
|
826
|
+
const near = this.pathOfSpecifier(site.fileName, imported.from);
|
|
827
|
+
const own = this.#nearest(candidates.filter((other) => other.owner === from), ({ site }) => site.fileName, near);
|
|
828
|
+
if (own)
|
|
829
|
+
return own;
|
|
830
|
+
const reexport = from && depth < maxMapDepth ? this.reexportOf(from, name, near) : null;
|
|
831
|
+
if (reexport)
|
|
832
|
+
return this.#factoryAt(factories, reexport.local, reexport.site, depth + 1);
|
|
833
|
+
return from && near ? this.#throughStar(candidates, from, name, near) : undefined;
|
|
834
|
+
}
|
|
835
|
+
#findGuard(local, site, depth, isBase = false) {
|
|
836
|
+
const segments = local.split(".");
|
|
837
|
+
const head = segments[0] ?? local;
|
|
838
|
+
const imported = site.imports.find((item) => item.name === head);
|
|
839
|
+
const from = imported ? this.ownerOfSpecifier(site.fileName, imported.from) : undefined;
|
|
840
|
+
if (imported && from === undefined)
|
|
841
|
+
return { external: true };
|
|
842
|
+
const near = imported ? this.pathOfSpecifier(site.fileName, imported.from) : this.#source.relative(site.fileName).replace(/\.tsx?$/, "");
|
|
843
|
+
const named = imported && imported.imported !== "default" && imported.imported !== "*" ? imported.imported : null;
|
|
844
|
+
const path = imported?.imported === "*" ? segments.slice(1) : [named ?? head, ...segments.slice(1)];
|
|
845
|
+
const holder = imported ? from ?? null : site.owner;
|
|
846
|
+
const [objectName, member] = path;
|
|
847
|
+
if (path.length === 2 && objectName && member && holder !== frameworkOwner && depth < maxMapDepth) {
|
|
848
|
+
const made = this.#nearest((this.#guards.get(`${objectName}.${member}`) ?? []).filter((guard) => guard.owner === holder), ({ span }) => span.fileName, near);
|
|
849
|
+
if (made)
|
|
850
|
+
return { guard: made };
|
|
851
|
+
for (const entry of this.#mapEntriesOf(objectName, member, imported ? null : site.fileName, holder)) {
|
|
852
|
+
const found = this.#findGuard(entry.name, entry.site, depth + 1, isBase);
|
|
853
|
+
if ("guard" in found)
|
|
854
|
+
return found;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const name = path[path.length - 1] ?? head;
|
|
858
|
+
if (from === frameworkOwner)
|
|
859
|
+
return { name, from };
|
|
860
|
+
const declared = this.#guards.get(name) ?? [];
|
|
861
|
+
const own = this.#nearest(declared.filter((other) => other.owner === (imported ? from : site.owner)), ({ span }) => span.fileName, near);
|
|
862
|
+
if (own)
|
|
863
|
+
return { guard: own };
|
|
864
|
+
if (!imported) {
|
|
865
|
+
const visible = isBase ? undefined : declared.find((other) => this.visible(other.owner, site.owner));
|
|
866
|
+
return visible ? { guard: visible } : { name, from };
|
|
867
|
+
}
|
|
868
|
+
const reexport = from && depth < maxMapDepth ? this.reexportOf(from, name, near) : null;
|
|
869
|
+
if (reexport)
|
|
870
|
+
return this.#findGuard(reexport.local, reexport.site, depth + 1, isBase);
|
|
871
|
+
const reached = from && near ? this.#throughStar(declared, from, name, near) : undefined;
|
|
872
|
+
return reached ? { guard: reached } : { name, from };
|
|
873
|
+
}
|
|
874
|
+
reexportOf(owner, name, near) {
|
|
875
|
+
const key = `${owner}
|
|
876
|
+
${name}
|
|
877
|
+
${near ?? ""}`;
|
|
878
|
+
const cached = this.#reexports.get(key);
|
|
879
|
+
if (cached !== undefined)
|
|
880
|
+
return cached;
|
|
881
|
+
let found = null;
|
|
882
|
+
const exporting = exportingPatternOf(name);
|
|
883
|
+
for (const fileName of this.#filesOf(owner, name, near)) {
|
|
884
|
+
const text = this.#source.readText(fileName);
|
|
885
|
+
if (text === undefined || !exporting.test(text))
|
|
886
|
+
continue;
|
|
887
|
+
const reexport = this.#reexportIn(this.#source.parse(fileName, text), name);
|
|
888
|
+
if (!reexport)
|
|
889
|
+
continue;
|
|
890
|
+
const site = { fileName, owner, imports: [] };
|
|
891
|
+
found = reexport.from ? {
|
|
892
|
+
local: reexport.name,
|
|
893
|
+
site: { ...site, imports: [{ ...reexport, imported: reexport.name, from: reexport.from }] }
|
|
894
|
+
} : { local: reexport.name, site };
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
this.#reexports.set(key, found);
|
|
898
|
+
return found;
|
|
899
|
+
}
|
|
900
|
+
#reexportIn(source, name) {
|
|
901
|
+
const { ts, reader } = this.#source;
|
|
902
|
+
for (const statement of source.statements) {
|
|
903
|
+
if (!ts.isExportDeclaration(statement) || statement.isTypeOnly)
|
|
904
|
+
continue;
|
|
905
|
+
const { exportClause, moduleSpecifier } = statement;
|
|
906
|
+
if (!exportClause || !ts.isNamedExports(exportClause))
|
|
907
|
+
continue;
|
|
908
|
+
const element = exportClause.elements.find((item) => !item.isTypeOnly && item.name.text === name);
|
|
909
|
+
if (!element)
|
|
910
|
+
continue;
|
|
911
|
+
const original = (element.propertyName ?? element.name).text;
|
|
912
|
+
if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier))
|
|
913
|
+
return { name: original, from: moduleSpecifier.text };
|
|
914
|
+
const imported = reader.importsIn(source).find((item) => item.name === original);
|
|
915
|
+
if (!imported)
|
|
916
|
+
return { name: original, from: null };
|
|
917
|
+
return imported.imported === "default" || imported.imported === "*" ? null : { name: imported.imported, from: imported.from };
|
|
918
|
+
}
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
#filesOf(owner, name, near) {
|
|
922
|
+
const isNear = (fileName) => {
|
|
923
|
+
const relative = this.#source.relative(fileName);
|
|
924
|
+
return !!near && (relative.startsWith(`${near}/`) || relative.replace(/\.tsx?$/, "") === near);
|
|
925
|
+
};
|
|
926
|
+
if (!identifierPattern.test(name))
|
|
927
|
+
return [];
|
|
928
|
+
const candidates = this.#filesIn(owner).filter(([, words]) => words.includes(`
|
|
929
|
+
${name}
|
|
930
|
+
`)).map(([fileName]) => fileName);
|
|
931
|
+
return [...candidates.filter(isNear), ...candidates.filter((other) => !isNear(other))];
|
|
932
|
+
}
|
|
933
|
+
#mapEntriesOf(objectName, member, fileName, owner) {
|
|
934
|
+
const key = `${objectName}
|
|
935
|
+
${member}
|
|
936
|
+
${fileName ?? ""}
|
|
937
|
+
${owner ?? ""}`;
|
|
938
|
+
const cached = this.#mapEntries.get(key);
|
|
939
|
+
if (cached)
|
|
940
|
+
return cached;
|
|
941
|
+
const { ts, reader } = this.#source;
|
|
942
|
+
const entries = [];
|
|
943
|
+
const fileNames = [
|
|
944
|
+
...fileName ? [fileName] : [],
|
|
945
|
+
...(owner === null ? [] : this.#filesIn(owner)).filter(([other, words]) => other !== fileName && words.includes(`
|
|
946
|
+
${objectName}
|
|
947
|
+
`) && words.includes(`
|
|
948
|
+
${member}
|
|
949
|
+
`)).map(([other]) => other)
|
|
950
|
+
];
|
|
951
|
+
for (const holder of fileNames) {
|
|
952
|
+
const text = this.#source.readText(holder);
|
|
953
|
+
if (text === undefined)
|
|
954
|
+
continue;
|
|
955
|
+
const source = this.#source.parse(holder, text);
|
|
956
|
+
const site = { fileName: holder, owner: this.#source.ownerOf(holder), imports: reader.importsIn(source) };
|
|
957
|
+
for (const statement of source.statements) {
|
|
958
|
+
if (!ts.isVariableStatement(statement))
|
|
959
|
+
continue;
|
|
960
|
+
const map = statement.declarationList.declarations.find((declaration) => ts.isIdentifier(declaration.name) && declaration.name.text === objectName)?.initializer;
|
|
961
|
+
const object = map && reader.unwrap(map);
|
|
962
|
+
if (!object || !ts.isObjectLiteralExpression(object))
|
|
963
|
+
continue;
|
|
964
|
+
for (const property of object.properties) {
|
|
965
|
+
if (ts.isShorthandPropertyAssignment(property) && property.name.text === member)
|
|
966
|
+
entries.push({ name: member, site });
|
|
967
|
+
if (!ts.isPropertyAssignment(property) || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)) || property.name.text !== member)
|
|
968
|
+
continue;
|
|
969
|
+
const value = reader.unwrap(property.initializer);
|
|
970
|
+
if (ts.isIdentifier(value))
|
|
971
|
+
entries.push({ name: value.text, site });
|
|
972
|
+
else if (ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression))
|
|
973
|
+
entries.push({ name: `${value.expression.text}.${value.name.text}`, site });
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
this.#mapEntries.set(key, entries);
|
|
978
|
+
return entries;
|
|
979
|
+
}
|
|
980
|
+
declarationOf(owner, name, near, depth = 0) {
|
|
981
|
+
const key = `${owner}
|
|
982
|
+
${name}
|
|
983
|
+
${near ?? ""}`;
|
|
984
|
+
const cached = this.#declarations.get(key);
|
|
985
|
+
if (cached !== undefined)
|
|
986
|
+
return cached;
|
|
987
|
+
this.#declarations.set(key, null);
|
|
988
|
+
const declaring = declaringPatternOf(name, false);
|
|
989
|
+
let found = null;
|
|
990
|
+
for (const fileName of this.#filesOf(owner, name, near)) {
|
|
991
|
+
const text = this.#source.readText(fileName);
|
|
992
|
+
if (text === undefined || !declaring.test(text))
|
|
993
|
+
continue;
|
|
994
|
+
const source = this.#source.parse(fileName, text);
|
|
995
|
+
const declared = this.#routes.declarationIn(source, name);
|
|
996
|
+
if (declared) {
|
|
997
|
+
found = { source, ...declared, name };
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
1000
|
+
const reexport = this.#reexportIn(source, name);
|
|
1001
|
+
if (!reexport)
|
|
1002
|
+
continue;
|
|
1003
|
+
if (!reexport.from) {
|
|
1004
|
+
const local = this.#routes.declarationIn(source, reexport.name);
|
|
1005
|
+
found = local ? { source, ...local, name: reexport.name } : null;
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
const next = this.ownerOfSpecifier(fileName, reexport.from);
|
|
1009
|
+
if (next && next !== frameworkOwner && depth < maxMapDepth)
|
|
1010
|
+
found = this.declarationOf(next, reexport.name, this.pathOfSpecifier(fileName, reexport.from), depth + 1);
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
this.#declarations.set(key, found);
|
|
1014
|
+
return found;
|
|
1015
|
+
}
|
|
1016
|
+
#throughStar(declared, owner, name, near) {
|
|
1017
|
+
if (!declared.length)
|
|
1018
|
+
return;
|
|
1019
|
+
const { owners, isOpaque } = this.starExportsOf(owner, near);
|
|
1020
|
+
const reached = declared.filter((other) => other.owner !== null && owners.includes(other.owner));
|
|
1021
|
+
return reached.length === 1 && !isOpaque && !this.declares(owner, name) ? reached[0] : undefined;
|
|
1022
|
+
}
|
|
1023
|
+
starExportsOf(owner, near) {
|
|
1024
|
+
const key = `${owner}
|
|
1025
|
+
${near}`;
|
|
1026
|
+
const cached = this.#starExports.get(key);
|
|
1027
|
+
if (cached)
|
|
1028
|
+
return cached;
|
|
1029
|
+
const found = { owners: [], packages: [], paths: [near], isOpaque: false };
|
|
1030
|
+
this.#starExports.set(key, found);
|
|
1031
|
+
for (let idx = 0;idx < found.paths.length; idx++)
|
|
1032
|
+
for (const fileName of this.#modulesAt(found.paths[idx] ?? ""))
|
|
1033
|
+
for (const [, specifier = ""] of this.#source.readText(fileName)?.matchAll(starExportPattern) ?? []) {
|
|
1034
|
+
const target = this.ownerOfSpecifier(fileName, specifier);
|
|
1035
|
+
const path = this.pathOfSpecifier(fileName, specifier);
|
|
1036
|
+
if (path === null && !found.packages.includes(specifier))
|
|
1037
|
+
found.packages.push(specifier);
|
|
1038
|
+
if (target === undefined || target === frameworkOwner || path === null)
|
|
1039
|
+
found.isOpaque = true;
|
|
1040
|
+
else if (!found.paths.includes(path))
|
|
1041
|
+
found.paths.push(path);
|
|
1042
|
+
if (target && target !== frameworkOwner && target !== owner && !found.owners.includes(target))
|
|
1043
|
+
found.owners.push(target);
|
|
1044
|
+
}
|
|
1045
|
+
return found;
|
|
1046
|
+
}
|
|
1047
|
+
exportsAt(path, name) {
|
|
1048
|
+
const key = `${path}
|
|
1049
|
+
${name}`;
|
|
1050
|
+
const cached = this.#moduleExports.get(key);
|
|
1051
|
+
if (cached)
|
|
1052
|
+
return cached;
|
|
1053
|
+
const found = { named: null, stars: [] };
|
|
1054
|
+
const exporting = exportingPatternOf(name);
|
|
1055
|
+
for (const fileName of this.#modulesAt(path)) {
|
|
1056
|
+
const text = this.#source.readText(fileName);
|
|
1057
|
+
if (text === undefined)
|
|
1058
|
+
continue;
|
|
1059
|
+
const named = identifierPattern.test(name) && exporting.test(text) ? this.#reexportIn(this.#source.parse(fileName, text), name) : null;
|
|
1060
|
+
if (named) {
|
|
1061
|
+
found.named = { ...named, fileName };
|
|
1062
|
+
found.stars = [];
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
for (const [, specifier = ""] of text.matchAll(starExportPattern)) {
|
|
1066
|
+
const target = this.pathOfSpecifier(fileName, specifier);
|
|
1067
|
+
if (target !== null && !found.stars.includes(target))
|
|
1068
|
+
found.stars.push(target);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
this.#moduleExports.set(key, found);
|
|
1072
|
+
return found;
|
|
1073
|
+
}
|
|
1074
|
+
#modulesAt(path) {
|
|
1075
|
+
if (!this.#modules) {
|
|
1076
|
+
this.#modules = new Map;
|
|
1077
|
+
for (const [fileName] of this.#source.files) {
|
|
1078
|
+
const module = this.#source.relative(fileName).replace(/\.tsx?$/, "");
|
|
1079
|
+
for (const key of module.endsWith("/index") ? [module, module.slice(0, -6)] : [module])
|
|
1080
|
+
this.#modules.set(key, [...this.#modules.get(key) ?? [], fileName]);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return this.#modules.get(path) ?? [];
|
|
1084
|
+
}
|
|
1085
|
+
hasModule(path) {
|
|
1086
|
+
return this.#modulesAt(path).length > 0;
|
|
1087
|
+
}
|
|
1088
|
+
packagesUnder(owner, folder) {
|
|
1089
|
+
const packages = new Set;
|
|
1090
|
+
for (const [fileName, words] of this.#filesIn(owner)) {
|
|
1091
|
+
if (!this.#source.relative(fileName).startsWith(`${folder}/`) || !words.includes(`
|
|
1092
|
+
export
|
|
1093
|
+
`))
|
|
1094
|
+
continue;
|
|
1095
|
+
for (const [, specifier = ""] of this.#source.readText(fileName)?.matchAll(starExportPattern) ?? [])
|
|
1096
|
+
if (this.pathOfSpecifier(fileName, specifier) === null)
|
|
1097
|
+
packages.add(specifier);
|
|
1098
|
+
}
|
|
1099
|
+
return [...packages];
|
|
1100
|
+
}
|
|
1101
|
+
declares(owner, name) {
|
|
1102
|
+
const declaring = declaringPatternOf(name, true);
|
|
1103
|
+
return this.#filesOf(owner, name, null).some((fileName) => declaring.test(this.#source.readText(fileName) ?? ""));
|
|
1104
|
+
}
|
|
1105
|
+
declaresAt(path, name) {
|
|
1106
|
+
const declaring = declaringPatternOf(name, true);
|
|
1107
|
+
return this.#modulesAt(path).some((fileName) => declaring.test(this.#source.readText(fileName) ?? ""));
|
|
1108
|
+
}
|
|
1109
|
+
#filesIn(owner) {
|
|
1110
|
+
if (!this.#ownerFiles) {
|
|
1111
|
+
this.#ownerFiles = new Map;
|
|
1112
|
+
for (const entry of this.#source.files) {
|
|
1113
|
+
const fileOwner = testFilePattern.test(entry[0]) ? null : this.#source.ownerOf(entry[0]);
|
|
1114
|
+
const files = fileOwner === null ? null : this.#ownerFiles.get(fileOwner);
|
|
1115
|
+
if (files)
|
|
1116
|
+
files.push(entry);
|
|
1117
|
+
else if (fileOwner !== null)
|
|
1118
|
+
this.#ownerFiles.set(fileOwner, [entry]);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return this.#ownerFiles.get(owner) ?? [];
|
|
1122
|
+
}
|
|
1123
|
+
#nearest(items, fileOf, near) {
|
|
1124
|
+
const isNear = (fileName) => {
|
|
1125
|
+
const relative = this.#source.relative(fileName);
|
|
1126
|
+
return !!near && (relative.startsWith(`${near}/`) || relative.replace(/\.tsx?$/, "") === near);
|
|
1127
|
+
};
|
|
1128
|
+
return items.find((item) => isNear(fileOf(item))) ?? items[0];
|
|
1129
|
+
}
|
|
1130
|
+
inOwner(fileName, owner) {
|
|
1131
|
+
const relative = this.#source.relative(fileName);
|
|
1132
|
+
return owner && relative.startsWith(`${owner}/`) ? relative.slice(owner.length + 1) : relative;
|
|
1133
|
+
}
|
|
1134
|
+
ownerOfSpecifier(fileName, specifier) {
|
|
1135
|
+
if (frameworkPattern.test(specifier))
|
|
1136
|
+
return frameworkOwner;
|
|
1137
|
+
const path = this.pathOfSpecifier(fileName, specifier);
|
|
1138
|
+
return path === null ? undefined : ownerOf(path) ?? undefined;
|
|
1139
|
+
}
|
|
1140
|
+
pathOfSpecifier(fileName, specifier) {
|
|
1141
|
+
const alias = specifier.match(aliasPattern);
|
|
1142
|
+
if (alias)
|
|
1143
|
+
return `${alias[1]}/${alias[2]}${specifier.slice(alias[0].length)}`;
|
|
1144
|
+
if (!specifier.startsWith("."))
|
|
1145
|
+
return null;
|
|
1146
|
+
const segments = this.#source.relative(fileName).split("/").slice(0, -1);
|
|
1147
|
+
for (const segment of specifier.split("/"))
|
|
1148
|
+
if (segment === "..")
|
|
1149
|
+
segments.pop();
|
|
1150
|
+
else if (segment !== ".")
|
|
1151
|
+
segments.push(segment);
|
|
1152
|
+
return segments.join("/");
|
|
1153
|
+
}
|
|
1154
|
+
visible(target, from) {
|
|
1155
|
+
return !from || !target || target === from || target.startsWith("libs/");
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// pkgs/@akanjs/language/adaptorResolver.ts
|
|
1160
|
+
var maxModules = 64;
|
|
1161
|
+
|
|
1162
|
+
class AdaptorResolver {
|
|
1163
|
+
#source;
|
|
1164
|
+
#resolver;
|
|
1165
|
+
#sink;
|
|
1166
|
+
#adaptors = new Map;
|
|
1167
|
+
#plugs = new Map;
|
|
1168
|
+
#serviceFields = new Set;
|
|
1169
|
+
constructor(source, resolver, sink) {
|
|
1170
|
+
this.#source = source;
|
|
1171
|
+
this.#resolver = resolver;
|
|
1172
|
+
this.#sink = sink;
|
|
1173
|
+
}
|
|
1174
|
+
declare(facts) {
|
|
1175
|
+
const counts = new Map;
|
|
1176
|
+
for (const file of facts)
|
|
1177
|
+
for (const fact of file.adaptors) {
|
|
1178
|
+
const key = this.#sink.key("adaptor", file.owner, fact.name);
|
|
1179
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
1180
|
+
}
|
|
1181
|
+
const declared = facts.flatMap((file) => file.adaptors.map((fact) => {
|
|
1182
|
+
const key = this.#sink.key("adaptor", file.owner, fact.name);
|
|
1183
|
+
const id = (counts.get(key) ?? 0) > 1 ? this.#sink.key("adaptor", file.owner, `${this.#resolver.inOwner(file.fileName, file.owner)}#${fact.name}`) : key;
|
|
1184
|
+
const adaptor = { fact, owner: file.owner, fileName: file.fileName, id };
|
|
1185
|
+
this.#adaptors.set(fact.name, [...this.#adaptors.get(fact.name) ?? [], adaptor]);
|
|
1186
|
+
this.#nodeOf(adaptor);
|
|
1187
|
+
return { adaptor, file };
|
|
1188
|
+
}));
|
|
1189
|
+
for (const { adaptor, file } of declared)
|
|
1190
|
+
for (const plug of adaptor.fact.plugs)
|
|
1191
|
+
this.#sink.addEdge(adaptor.id, this.#idOf(plug.target, file), "plugs", plug.name, this.#sink.location(plug.span));
|
|
1192
|
+
for (const file of facts)
|
|
1193
|
+
for (const binding of file.adaptorBindings)
|
|
1194
|
+
this.#sink.addEdge(this.#idOf(binding.role, file), this.#idOf(binding.adaptor, file), "binds", file.owner, this.#sink.location(binding.span));
|
|
1195
|
+
}
|
|
1196
|
+
addService(refName, service, file) {
|
|
1197
|
+
for (const plug of service.plugs) {
|
|
1198
|
+
const key = `${refName}
|
|
1199
|
+
${plug.name}`;
|
|
1200
|
+
this.#plugs.set(key, [
|
|
1201
|
+
...this.#plugs.get(key) ?? [],
|
|
1202
|
+
{ owner: file.owner, target: this.#idOf(plug.target, file) }
|
|
1203
|
+
]);
|
|
1204
|
+
}
|
|
1205
|
+
for (const field of service.fields)
|
|
1206
|
+
this.#serviceFields.add(`${file.owner ?? ""}
|
|
1207
|
+
${refName}
|
|
1208
|
+
${field}`);
|
|
1209
|
+
}
|
|
1210
|
+
pluggedOf(owner, module, field) {
|
|
1211
|
+
const plugs = this.#plugs.get(`${module}
|
|
1212
|
+
${field}`) ?? [];
|
|
1213
|
+
const own = plugs.filter((plug) => plug.owner === owner);
|
|
1214
|
+
if (!own.length && this.#serviceFields.has(`${owner ?? ""}
|
|
1215
|
+
${module}
|
|
1216
|
+
${field}`))
|
|
1217
|
+
return [];
|
|
1218
|
+
const inherited = own.length ? own : plugs.filter((plug) => this.#resolver.visible(plug.owner, owner));
|
|
1219
|
+
return [...new Set(inherited.map(({ target }) => target))];
|
|
1220
|
+
}
|
|
1221
|
+
#idOf(local, file) {
|
|
1222
|
+
const [head, member] = local.split(".");
|
|
1223
|
+
const imported = file.imports.find((item) => item.name === head);
|
|
1224
|
+
const name = member ?? (imported && imported.imported !== "default" && imported.imported !== "*" ? imported.imported : head);
|
|
1225
|
+
const path = imported ? this.#resolver.pathOfSpecifier(file.fileName, imported.from) : null;
|
|
1226
|
+
if (imported && path === null && !frameworkPattern.test(imported.from))
|
|
1227
|
+
return this.#packageAdaptorOf(name, imported.from);
|
|
1228
|
+
const from = imported ? this.#resolver.ownerOfSpecifier(file.fileName, imported.from) : undefined;
|
|
1229
|
+
const declared = this.#adaptors.get(name) ?? [];
|
|
1230
|
+
const holder = from === undefined ? file.owner : from;
|
|
1231
|
+
const own = this.#adaptorIn(name, declared, holder, path, file.fileName);
|
|
1232
|
+
if (own && "from" in own)
|
|
1233
|
+
return this.#packageAdaptorOf(own.name, own.from);
|
|
1234
|
+
const found = own === undefined && from !== frameworkOwner ? declared.find((adaptor) => this.#resolver.visible(adaptor.owner, holder)) : own;
|
|
1235
|
+
if (found)
|
|
1236
|
+
return found.id;
|
|
1237
|
+
const reexported = own === undefined && from && from !== frameworkOwner ? this.#packageShimOf(from, name, path) : null;
|
|
1238
|
+
if (reexported)
|
|
1239
|
+
return this.#packageAdaptorOf(reexported.name, reexported.from);
|
|
1240
|
+
const owner = from === frameworkOwner ? null : imported ? from ?? null : file.owner;
|
|
1241
|
+
const detail = from === frameworkOwner ? [frameworkOwner] : [];
|
|
1242
|
+
return this.#sink.addNode(this.#sink.key("adaptor", owner, name), "adaptor", owner, name, null, detail).id;
|
|
1243
|
+
}
|
|
1244
|
+
#adaptorIn(name, declared, owner, path, fileName) {
|
|
1245
|
+
const own = declared.filter((adaptor) => adaptor.owner === owner);
|
|
1246
|
+
const [first] = own;
|
|
1247
|
+
if (path === null)
|
|
1248
|
+
return own.find((adaptor) => adaptor.fileName === fileName) ?? first;
|
|
1249
|
+
if (own.length === 1)
|
|
1250
|
+
return first;
|
|
1251
|
+
if (!this.#resolver.hasModule(path))
|
|
1252
|
+
return own.find((adaptor) => this.#source.relative(adaptor.fileName).startsWith(`${path}/`)) ?? first;
|
|
1253
|
+
const exported = this.#exportedAt(name, path, new Set).filter((item) => first || ("fact" in item));
|
|
1254
|
+
const [only] = exported;
|
|
1255
|
+
if (only)
|
|
1256
|
+
return exported.length === 1 ? only : null;
|
|
1257
|
+
return first ? null : undefined;
|
|
1258
|
+
}
|
|
1259
|
+
#exportedAt(name, path, seen) {
|
|
1260
|
+
const key = `${path}
|
|
1261
|
+
${name}`;
|
|
1262
|
+
if (seen.has(key) || seen.size >= maxModules)
|
|
1263
|
+
return [];
|
|
1264
|
+
seen.add(key);
|
|
1265
|
+
const here = (this.#adaptors.get(name) ?? []).filter((adaptor) => {
|
|
1266
|
+
const module = this.#source.relative(adaptor.fileName).replace(/\.tsx?$/, "");
|
|
1267
|
+
return module === path || module === `${path}/index`;
|
|
1268
|
+
});
|
|
1269
|
+
if (here.length)
|
|
1270
|
+
return here;
|
|
1271
|
+
const { named, stars } = this.#resolver.exportsAt(path, name);
|
|
1272
|
+
if (named?.from && this.#resolver.pathOfSpecifier(named.fileName, named.from) === null)
|
|
1273
|
+
return [{ name: named.name === "default" ? name : named.name, from: named.from }];
|
|
1274
|
+
if (named) {
|
|
1275
|
+
const target = named.from === null ? path : this.#resolver.pathOfSpecifier(named.fileName, named.from);
|
|
1276
|
+
return target === null ? [] : this.#exportedAt(named.name, target, seen);
|
|
1277
|
+
}
|
|
1278
|
+
return [...new Set(stars.flatMap((star) => this.#exportedAt(name, star, seen)))];
|
|
1279
|
+
}
|
|
1280
|
+
#packageAdaptorOf(name, specifier) {
|
|
1281
|
+
if (frameworkPattern.test(specifier))
|
|
1282
|
+
return this.#sink.addNode(this.#sink.key("adaptor", null, name), "adaptor", null, name, null, [frameworkOwner]).id;
|
|
1283
|
+
const pkg = specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
|
|
1284
|
+
return this.#sink.addNode(this.#sink.key("adaptor", pkg, name), "adaptor", null, name, null, [pkg]).id;
|
|
1285
|
+
}
|
|
1286
|
+
#packageShimOf(owner, name, near) {
|
|
1287
|
+
const reexport = this.#resolver.reexportOf(owner, name, near);
|
|
1288
|
+
const [shim] = reexport?.site.imports ?? [];
|
|
1289
|
+
if (reexport && shim && this.#resolver.pathOfSpecifier(reexport.site.fileName, shim.from) === null)
|
|
1290
|
+
return { name: reexport.local === "default" ? name : reexport.local, from: shim.from };
|
|
1291
|
+
if (reexport || !near)
|
|
1292
|
+
return null;
|
|
1293
|
+
const { packages, paths } = this.#resolver.hasModule(near) ? this.#resolver.starExportsOf(owner, near) : { packages: this.#resolver.packagesUnder(owner, near), paths: [] };
|
|
1294
|
+
const [only] = packages;
|
|
1295
|
+
const isDeclared = this.#resolver.declares(owner, name) || paths.some((path) => this.#resolver.declaresAt(path, name));
|
|
1296
|
+
return packages.length === 1 && only && !isDeclared ? { name, from: only } : null;
|
|
1297
|
+
}
|
|
1298
|
+
#nodeOf({ fact, owner, fileName, id }) {
|
|
1299
|
+
const detail = fact.refName ? [fact.refName] : [];
|
|
1300
|
+
const node = this.#sink.addNode(id, "adaptor", owner, fact.name, fact.span, detail);
|
|
1301
|
+
if (fact.doc && !node.doc)
|
|
1302
|
+
node.doc = fact.doc;
|
|
1303
|
+
this.#sink.digest(node, this.#source.readText(fileName), fact.extent);
|
|
1304
|
+
return node.id;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
515
1307
|
|
|
516
1308
|
// pkgs/akanjs/common/trimSlashes.ts
|
|
517
1309
|
var trimTrailingSlashes = (value) => {
|
|
@@ -545,7 +1337,8 @@ var ROOT_LAYOUT_ROUTE_EXPORTS = new Set([
|
|
|
545
1337
|
"wsConnect",
|
|
546
1338
|
"layoutStyle",
|
|
547
1339
|
"viewport",
|
|
548
|
-
"McpAppRoot"
|
|
1340
|
+
"McpAppRoot",
|
|
1341
|
+
"LevelBoundary"
|
|
549
1342
|
]);
|
|
550
1343
|
var RESERVED_ROUTE_CONFIG_EXPORTS = new Set([...ROOT_LAYOUT_ROUTE_EXPORTS].filter((name) => name !== "default" && !/^[A-Z]/.test(name)));
|
|
551
1344
|
function parseRouteModuleKey(key) {
|
|
@@ -660,24 +1453,23 @@ rootLayout
|
|
|
660
1453
|
}
|
|
661
1454
|
declarationIn(source, name) {
|
|
662
1455
|
const ts = this.#ts;
|
|
1456
|
+
const overloads = source.statements.filter((statement) => ts.isFunctionDeclaration(statement) && statement.name?.text === name);
|
|
1457
|
+
const [first] = overloads;
|
|
1458
|
+
const last = overloads.at(-1);
|
|
1459
|
+
if (first?.name && last)
|
|
1460
|
+
return { statement: first, declared: first.name, extent: { start: first.getStart(source), end: last.getEnd() } };
|
|
663
1461
|
for (const statement of source.statements) {
|
|
664
|
-
if (ts.isFunctionDeclaration(statement) && statement.name?.text === name)
|
|
665
|
-
return { statement, declared: statement.name, initializer: null };
|
|
666
1462
|
if (!ts.isVariableStatement(statement))
|
|
667
1463
|
continue;
|
|
668
1464
|
const declaration = statement.declarationList.declarations.find((item) => ts.isIdentifier(item.name) && item.name.text === name);
|
|
669
1465
|
if (declaration)
|
|
670
|
-
return { statement, declared: declaration.name,
|
|
1466
|
+
return { statement, declared: declaration.name, extent: this.#reader.extentOf(source, statement) };
|
|
671
1467
|
}
|
|
672
1468
|
return null;
|
|
673
1469
|
}
|
|
674
1470
|
#guardOf(source, exported) {
|
|
675
1471
|
const ts = this.#ts;
|
|
676
1472
|
let current = ts.isExportAssignment(exported) ? this.#reader.unwrap(exported.expression) : null;
|
|
677
|
-
if (current && ts.isIdentifier(current)) {
|
|
678
|
-
const initializer = this.declarationIn(source, current.text)?.initializer;
|
|
679
|
-
current = initializer ? this.#reader.unwrap(initializer) : null;
|
|
680
|
-
}
|
|
681
1473
|
while (current && ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
|
|
682
1474
|
const { name, expression } = current.expression;
|
|
683
1475
|
const [stage] = current.arguments;
|
|
@@ -689,9 +1481,20 @@ rootLayout
|
|
|
689
1481
|
}
|
|
690
1482
|
#callsIn(source, stage) {
|
|
691
1483
|
const ts = this.#ts;
|
|
692
|
-
const
|
|
1484
|
+
const imports = this.#reader.importsIn(source).filter(({ from }) => RouteFactReader.#workspacePattern.test(from));
|
|
1485
|
+
const imported = new Set(imports.filter(({ imported }) => imported !== "*").map(({ name }) => name));
|
|
1486
|
+
const namespaces = new Set(imports.filter(({ imported }) => imported === "*").map(({ name }) => name));
|
|
693
1487
|
const calls = new Map;
|
|
694
|
-
const callOf = (
|
|
1488
|
+
const callOf = (callee, shadowed) => {
|
|
1489
|
+
if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression) && namespaces.has(callee.expression.text) && !shadowed.has(callee.expression.text)) {
|
|
1490
|
+
const name = `${callee.expression.text}.${callee.name.text}`;
|
|
1491
|
+
if (!calls.has(name))
|
|
1492
|
+
calls.set(name, { name, local: null });
|
|
1493
|
+
return true;
|
|
1494
|
+
}
|
|
1495
|
+
if (!ts.isIdentifier(callee) || shadowed.has(callee.text))
|
|
1496
|
+
return false;
|
|
1497
|
+
const name = callee.text;
|
|
695
1498
|
if (calls.has(name))
|
|
696
1499
|
return true;
|
|
697
1500
|
const declared = this.declarationIn(source, name);
|
|
@@ -701,23 +1504,71 @@ rootLayout
|
|
|
701
1504
|
name,
|
|
702
1505
|
local: declared && {
|
|
703
1506
|
span: this.#reader.spanOf(source, declared.declared),
|
|
704
|
-
extent:
|
|
1507
|
+
extent: declared.extent,
|
|
705
1508
|
doc: this.#reader.docOf(source, declared.statement)
|
|
706
1509
|
}
|
|
707
1510
|
});
|
|
708
1511
|
return true;
|
|
709
1512
|
};
|
|
710
|
-
const visit = (node) => {
|
|
711
|
-
|
|
1513
|
+
const visit = (node, outer) => {
|
|
1514
|
+
const shadowed = this.#scopeOf(node, outer);
|
|
1515
|
+
if (ts.isCallExpression(node) && callOf(node.expression, shadowed))
|
|
712
1516
|
return;
|
|
713
|
-
ts.forEachChild(node, visit);
|
|
1517
|
+
ts.forEachChild(node, (child) => visit(child, shadowed));
|
|
714
1518
|
};
|
|
715
1519
|
if (ts.isIdentifier(stage))
|
|
716
|
-
callOf(stage
|
|
1520
|
+
callOf(stage, new Set);
|
|
717
1521
|
else
|
|
718
|
-
visit(stage);
|
|
1522
|
+
visit(stage, new Set);
|
|
719
1523
|
return [...calls.values()];
|
|
720
1524
|
}
|
|
1525
|
+
#scopeOf(node, outer) {
|
|
1526
|
+
const ts = this.#ts;
|
|
1527
|
+
const names = [];
|
|
1528
|
+
const bind = (name) => {
|
|
1529
|
+
if (ts.isIdentifier(name))
|
|
1530
|
+
names.push(name.text);
|
|
1531
|
+
else
|
|
1532
|
+
for (const element of name.elements)
|
|
1533
|
+
if (!ts.isOmittedExpression(element))
|
|
1534
|
+
bind(element.name);
|
|
1535
|
+
};
|
|
1536
|
+
const declare = (statement) => {
|
|
1537
|
+
if (ts.isVariableStatement(statement))
|
|
1538
|
+
for (const declaration of statement.declarationList.declarations)
|
|
1539
|
+
bind(declaration.name);
|
|
1540
|
+
else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name)
|
|
1541
|
+
names.push(statement.name.text);
|
|
1542
|
+
};
|
|
1543
|
+
const hoist = (child) => {
|
|
1544
|
+
if (ts.isFunctionLike(child) || ts.isClassLike(child))
|
|
1545
|
+
return;
|
|
1546
|
+
if (ts.isVariableDeclarationList(child) && !(child.flags & ts.NodeFlags.BlockScoped))
|
|
1547
|
+
for (const declaration of child.declarations)
|
|
1548
|
+
bind(declaration.name);
|
|
1549
|
+
ts.forEachChild(child, hoist);
|
|
1550
|
+
};
|
|
1551
|
+
if (ts.isFunctionLike(node)) {
|
|
1552
|
+
for (const parameter of node.parameters)
|
|
1553
|
+
bind(parameter.name);
|
|
1554
|
+
if (ts.isFunctionExpression(node) && node.name)
|
|
1555
|
+
names.push(node.name.text);
|
|
1556
|
+
if ("body" in node && node.body)
|
|
1557
|
+
ts.forEachChild(node.body, hoist);
|
|
1558
|
+
} else if (ts.isClassExpression(node) && node.name)
|
|
1559
|
+
names.push(node.name.text);
|
|
1560
|
+
else if (ts.isBlock(node))
|
|
1561
|
+
node.statements.forEach(declare);
|
|
1562
|
+
else if (ts.isCaseBlock(node))
|
|
1563
|
+
for (const clause of node.clauses)
|
|
1564
|
+
clause.statements.forEach(declare);
|
|
1565
|
+
else if (ts.isCatchClause(node) && node.variableDeclaration)
|
|
1566
|
+
bind(node.variableDeclaration.name);
|
|
1567
|
+
else if ((ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node)) && node.initializer && ts.isVariableDeclarationList(node.initializer))
|
|
1568
|
+
for (const declaration of node.initializer.declarations)
|
|
1569
|
+
bind(declaration.name);
|
|
1570
|
+
return names.length ? new Set([...outer, ...names]) : outer;
|
|
1571
|
+
}
|
|
721
1572
|
}
|
|
722
1573
|
|
|
723
1574
|
// pkgs/@akanjs/language/akanGraph.ts
|
|
@@ -733,10 +1584,6 @@ var builtinGuards = {
|
|
|
733
1584
|
Public: { scope: "account", doc: "Passes every caller, signed in or not." },
|
|
734
1585
|
None: { scope: "account", doc: "Refuses every caller." }
|
|
735
1586
|
};
|
|
736
|
-
var frameworkOwner = "akanjs";
|
|
737
|
-
var frameworkPattern = /^akanjs(?:\/|$)/;
|
|
738
|
-
var aliasPattern = /^@(apps|libs)\/([^/]+)/;
|
|
739
|
-
var maxMapDepth = 4;
|
|
740
1587
|
|
|
741
1588
|
class GraphBuilder {
|
|
742
1589
|
#source;
|
|
@@ -744,16 +1591,21 @@ class GraphBuilder {
|
|
|
744
1591
|
#edges = new Map;
|
|
745
1592
|
#contexts = new Map;
|
|
746
1593
|
#members = new Map;
|
|
747
|
-
#guards = new Map;
|
|
748
|
-
#mapEntries = new Map;
|
|
749
1594
|
#routeFiles = new Map;
|
|
750
|
-
#declarations = new Map;
|
|
751
1595
|
#routes;
|
|
752
|
-
#
|
|
753
|
-
#
|
|
1596
|
+
#resolver;
|
|
1597
|
+
#adaptors;
|
|
754
1598
|
constructor(source) {
|
|
755
1599
|
this.#source = source;
|
|
756
1600
|
this.#routes = new RouteFactReader(source.ts, source.reader, source.parse);
|
|
1601
|
+
this.#resolver = new GuardResolver(source, this.#routes);
|
|
1602
|
+
this.#adaptors = new AdaptorResolver(source, this.#resolver, {
|
|
1603
|
+
key: (kind, owner, scope) => this.#key(kind, owner, scope),
|
|
1604
|
+
addNode: (id, kind, owner, name, span, detail) => this.#addNode(id, kind, owner, null, name, span, detail),
|
|
1605
|
+
addEdge: (from, to, kind, label, at) => this.#addEdge(from, to, kind, label, at),
|
|
1606
|
+
digest: (node, text, extent) => this.#digest(node, text, extent),
|
|
1607
|
+
location: (span) => this.#location(span)
|
|
1608
|
+
});
|
|
757
1609
|
}
|
|
758
1610
|
build() {
|
|
759
1611
|
const { facts } = this.#source;
|
|
@@ -765,17 +1617,9 @@ class GraphBuilder {
|
|
|
765
1617
|
...[...constantsByOwner].filter(([key]) => key !== owner && key?.startsWith("libs/")).flatMap(([, list]) => list)
|
|
766
1618
|
];
|
|
767
1619
|
const typed = [];
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
this.#adaptors.set(fact.name, [...this.#adaptors.get(fact.name) ?? [], declared]);
|
|
772
|
-
this.#adaptorNodeOf(declared);
|
|
773
|
-
}
|
|
774
|
-
for (const file of facts)
|
|
775
|
-
for (const fact of file.adaptors)
|
|
776
|
-
for (const plug of fact.plugs)
|
|
777
|
-
this.#addEdge(this.#key("adaptor", file.owner, fact.name), this.#adaptorIdOf(plug.target, file), "plugs", plug.name, this.#location(plug.span));
|
|
778
|
-
this.#declareGuards(facts);
|
|
1620
|
+
this.#adaptors.declare(facts);
|
|
1621
|
+
for (const guard of this.#resolver.declare(facts))
|
|
1622
|
+
this.#guardNodeOf(guard);
|
|
779
1623
|
for (const file of facts) {
|
|
780
1624
|
const { owner } = file;
|
|
781
1625
|
const scalar = this.#source.relative(file.fileName).match(scalarPattern)?.[1];
|
|
@@ -806,10 +1650,7 @@ class GraphBuilder {
|
|
|
806
1650
|
}
|
|
807
1651
|
for (const method of service.methods)
|
|
808
1652
|
this.#addMember("method", owner, refName, method, `${service.name}.${method.name}`, file.fileName);
|
|
809
|
-
|
|
810
|
-
this.#plugs.set(`${owner ?? ""}
|
|
811
|
-
${refName}
|
|
812
|
-
${plug.name}`, this.#adaptorIdOf(plug.target, file));
|
|
1653
|
+
this.#adaptors.addService(refName, service, file);
|
|
813
1654
|
}
|
|
814
1655
|
for (const filter of file.filters) {
|
|
815
1656
|
if (!filter.name.endsWith("Filter"))
|
|
@@ -819,31 +1660,7 @@ ${plug.name}`, this.#adaptorIdOf(plug.target, file));
|
|
|
819
1660
|
for (const query of filter.queries)
|
|
820
1661
|
this.#digest(this.#addNode(this.#key("query", owner, refName, query.name), "query", owner, refName, query.name, query.span), this.#source.readText(file.fileName), query.extent);
|
|
821
1662
|
}
|
|
822
|
-
|
|
823
|
-
const refName = slice.model;
|
|
824
|
-
if (!refName)
|
|
825
|
-
continue;
|
|
826
|
-
const fallback = this.#addNode(this.#key("slice", owner, refName), "slice", owner, refName, "", slice.span, slice.guards);
|
|
827
|
-
for (const guard of slice.guards)
|
|
828
|
-
this.#guard(fallback, guard, file);
|
|
829
|
-
for (const key of slice.keys) {
|
|
830
|
-
const detail = [...key.guards, ...key.live ? ["live"] : []];
|
|
831
|
-
const node = this.#addMember("slice", owner, refName, key, `${refName}.${key.name}`, file.fileName, detail);
|
|
832
|
-
for (const guard of key.guards)
|
|
833
|
-
this.#guard(node, guard, file);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
for (const endpoint of file.endpoints) {
|
|
837
|
-
const refName = endpoint.model;
|
|
838
|
-
if (!refName)
|
|
839
|
-
continue;
|
|
840
|
-
for (const key of endpoint.keys) {
|
|
841
|
-
const detail = [...key.verb ? [key.verb] : [], ...key.guards];
|
|
842
|
-
const node = this.#addMember("endpoint", owner, refName, key, `${refName}.${key.name}`, file.fileName, detail);
|
|
843
|
-
for (const guard of key.guards)
|
|
844
|
-
this.#guard(node, guard, file);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
1663
|
+
this.#addGuarded(file);
|
|
847
1664
|
for (const internal of file.internals)
|
|
848
1665
|
for (const key of internal.keys)
|
|
849
1666
|
if (internal.model && key.type !== "resolveField")
|
|
@@ -859,7 +1676,7 @@ ${plug.name}`, this.#adaptorIdOf(plug.target, file));
|
|
|
859
1676
|
types.set(node.label, [...types.get(node.label) ?? [], node]);
|
|
860
1677
|
for (const node of typed)
|
|
861
1678
|
for (const field of node.fields ?? []) {
|
|
862
|
-
const candidates = (field.type ? types.get(field.type) ?? [] : []).filter((type) => this.#visible(type.owner, node.owner));
|
|
1679
|
+
const candidates = (field.type ? types.get(field.type) ?? [] : []).filter((type) => this.#resolver.visible(type.owner, node.owner));
|
|
863
1680
|
field.ref = (candidates.find((type) => type.owner === node.owner) ?? candidates[0])?.id ?? null;
|
|
864
1681
|
if (field.ref)
|
|
865
1682
|
this.#addEdge(node.id, field.ref, "field", `${field.name}${field.array ? "[]" : ""}`, field.at);
|
|
@@ -941,61 +1758,52 @@ ${label ?? ""}`;
|
|
|
941
1758
|
else
|
|
942
1759
|
this.#edges.set(key, { from, to, kind, label, count: 1, at });
|
|
943
1760
|
}
|
|
944
|
-
#
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
for (const
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
};
|
|
962
|
-
const madeBy = (factory, fileName, extent) => {
|
|
963
|
-
const parts = [{ fileName, extent }];
|
|
964
|
-
const seen = new Set;
|
|
965
|
-
for (let current = factory;current && !seen.has(current); current = sameFile(current, current.fact.via)) {
|
|
966
|
-
seen.add(current);
|
|
967
|
-
parts.push({ fileName: current.fileName, extent: current.fact.extent });
|
|
968
|
-
}
|
|
969
|
-
return parts;
|
|
970
|
-
};
|
|
971
|
-
for (const file of facts) {
|
|
972
|
-
const { owner, fileName } = file;
|
|
973
|
-
for (const { name, span, extent, scope, doc, factory: callee } of file.guards) {
|
|
974
|
-
const factory = callee ? factories.get(callee)?.find((other) => other.fileName === fileName) : undefined;
|
|
975
|
-
declare({ name, owner, span, scope, doc, parts: madeBy(factory, fileName, extent) }, factory);
|
|
1761
|
+
#addGuarded(file) {
|
|
1762
|
+
const { owner } = file;
|
|
1763
|
+
for (const slice of file.slices) {
|
|
1764
|
+
const refName = slice.model;
|
|
1765
|
+
if (!refName)
|
|
1766
|
+
continue;
|
|
1767
|
+
const fallback = this.#addNode(this.#key("slice", owner, refName), "slice", owner, refName, "", slice.span, slice.guards.map((guard) => guardLabelOf(guard, file.guardLabels)));
|
|
1768
|
+
for (const guard of slice.guards)
|
|
1769
|
+
this.#guard(fallback, guard, file);
|
|
1770
|
+
for (const key of slice.keys) {
|
|
1771
|
+
const detail = [
|
|
1772
|
+
...key.guards.map((guard) => guardLabelOf(guard, file.guardLabels)),
|
|
1773
|
+
...key.live ? ["live"] : []
|
|
1774
|
+
];
|
|
1775
|
+
const node = this.#addMember("slice", owner, refName, key, `${refName}.${key.name}`, file.fileName, detail);
|
|
1776
|
+
for (const guard of key.guards)
|
|
1777
|
+
this.#guard(node, guard, file);
|
|
976
1778
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
1779
|
+
}
|
|
1780
|
+
for (const endpoint of file.endpoints) {
|
|
1781
|
+
const refName = endpoint.model;
|
|
1782
|
+
if (!refName)
|
|
1783
|
+
continue;
|
|
1784
|
+
for (const key of endpoint.keys) {
|
|
1785
|
+
const detail = [
|
|
1786
|
+
...key.verb ? [key.verb] : [],
|
|
1787
|
+
...key.guards.map((guard) => guardLabelOf(guard, file.guardLabels))
|
|
1788
|
+
];
|
|
1789
|
+
const node = this.#addMember("endpoint", owner, refName, key, `${refName}.${key.name}`, file.fileName, detail);
|
|
1790
|
+
for (const guard of key.guards)
|
|
1791
|
+
this.#guard(node, guard, file);
|
|
986
1792
|
}
|
|
987
1793
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1794
|
+
}
|
|
1795
|
+
#guard(node, guard, file) {
|
|
1796
|
+
const [, role = null, local = guard] = guard.match(/^([\w$]+) (.+)$/) ?? [];
|
|
1797
|
+
if (!local || local === "?")
|
|
1798
|
+
return;
|
|
1799
|
+
const id = local.includes("@") ? this.#madeInPlaceIdOf(local, file) : this.#guardIdOf(local, file);
|
|
1800
|
+
if (id)
|
|
1801
|
+
this.#addEdge(node.id, id, "guard", role, node.at);
|
|
996
1802
|
}
|
|
997
1803
|
#guardIdOf(local, site) {
|
|
998
|
-
const found = this.#
|
|
1804
|
+
const found = this.#resolver.find(local, site);
|
|
1805
|
+
if ("external" in found)
|
|
1806
|
+
return null;
|
|
999
1807
|
if ("guard" in found)
|
|
1000
1808
|
return this.#guardNodeOf(found.guard);
|
|
1001
1809
|
const { name, from } = found;
|
|
@@ -1008,99 +1816,18 @@ ${label ?? ""}`;
|
|
|
1008
1816
|
node.doc = builtin.doc;
|
|
1009
1817
|
return node.id;
|
|
1010
1818
|
}
|
|
1011
|
-
#
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}
|
|
1024
|
-
const name = member ?? (imported && imported.imported !== "default" && imported.imported !== "*" ? imported.imported : head);
|
|
1025
|
-
const declared = this.#guards.get(name) ?? [];
|
|
1026
|
-
const guard = from === undefined ? declared.find((other) => other.owner === site.owner) ?? declared.find((other) => this.#visible(other.owner, site.owner)) : declared.find((other) => other.owner === from) ?? (from === frameworkOwner ? undefined : this.#soleVisible(declared, from));
|
|
1027
|
-
return guard ? { guard } : { name, from };
|
|
1028
|
-
}
|
|
1029
|
-
#mapEntriesOf(objectName, member, fileName, owner) {
|
|
1030
|
-
const key = `${objectName}
|
|
1031
|
-
${member}
|
|
1032
|
-
${fileName ?? ""}
|
|
1033
|
-
${owner ?? ""}`;
|
|
1034
|
-
const cached = this.#mapEntries.get(key);
|
|
1035
|
-
if (cached)
|
|
1036
|
-
return cached;
|
|
1037
|
-
const { ts, reader } = this.#source;
|
|
1038
|
-
const entries = [];
|
|
1039
|
-
const fileNames = [
|
|
1040
|
-
...fileName ? [fileName] : [],
|
|
1041
|
-
...this.#source.files.filter(([other, words]) => other !== fileName && owner !== null && !testFilePattern.test(other) && words.includes(`
|
|
1042
|
-
${objectName}
|
|
1043
|
-
`) && words.includes(`
|
|
1044
|
-
${member}
|
|
1045
|
-
`) && this.#source.ownerOf(other) === owner).map(([other]) => other)
|
|
1046
|
-
];
|
|
1047
|
-
for (const holder of fileNames) {
|
|
1048
|
-
const text = this.#source.readText(holder);
|
|
1049
|
-
if (text === undefined)
|
|
1050
|
-
continue;
|
|
1051
|
-
const source = this.#source.parse(holder, text);
|
|
1052
|
-
const site = { fileName: holder, owner: this.#source.ownerOf(holder), imports: reader.importsIn(source) };
|
|
1053
|
-
for (const statement of source.statements) {
|
|
1054
|
-
if (!ts.isVariableStatement(statement))
|
|
1055
|
-
continue;
|
|
1056
|
-
const map = statement.declarationList.declarations.find((declaration) => ts.isIdentifier(declaration.name) && declaration.name.text === objectName)?.initializer;
|
|
1057
|
-
const object = map && reader.unwrap(map);
|
|
1058
|
-
if (!object || !ts.isObjectLiteralExpression(object))
|
|
1059
|
-
continue;
|
|
1060
|
-
for (const property of object.properties) {
|
|
1061
|
-
if (ts.isShorthandPropertyAssignment(property) && property.name.text === member)
|
|
1062
|
-
entries.push({ name: member, site });
|
|
1063
|
-
if (!ts.isPropertyAssignment(property) || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)) || property.name.text !== member)
|
|
1064
|
-
continue;
|
|
1065
|
-
const value = reader.unwrap(property.initializer);
|
|
1066
|
-
if (ts.isIdentifier(value))
|
|
1067
|
-
entries.push({ name: value.text, site });
|
|
1068
|
-
else if (ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression))
|
|
1069
|
-
entries.push({ name: `${value.expression.text}.${value.name.text}`, site });
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
this.#mapEntries.set(key, entries);
|
|
1074
|
-
return entries;
|
|
1075
|
-
}
|
|
1076
|
-
#declarationOf(owner, name, near) {
|
|
1077
|
-
const key = `${owner}
|
|
1078
|
-
${name}
|
|
1079
|
-
${near ?? ""}`;
|
|
1080
|
-
const cached = this.#declarations.get(key);
|
|
1081
|
-
if (cached !== undefined)
|
|
1082
|
-
return cached;
|
|
1083
|
-
const isNear = (fileName) => {
|
|
1084
|
-
const relative = this.#source.relative(fileName);
|
|
1085
|
-
return !!near && (relative.startsWith(`${near}/`) || relative.replace(/\.tsx?$/, "") === near);
|
|
1086
|
-
};
|
|
1087
|
-
const candidates = this.#source.files.filter(([fileName, words]) => !testFilePattern.test(fileName) && words.includes(`
|
|
1088
|
-
${name}
|
|
1089
|
-
`) && this.#source.ownerOf(fileName) === owner).map(([fileName]) => fileName);
|
|
1090
|
-
let found = null;
|
|
1091
|
-
for (const fileName of [...candidates.filter(isNear), ...candidates.filter((other) => !isNear(other))]) {
|
|
1092
|
-
const text = this.#source.readText(fileName);
|
|
1093
|
-
if (text === undefined)
|
|
1094
|
-
continue;
|
|
1095
|
-
const source = this.#source.parse(fileName, text);
|
|
1096
|
-
const declared = this.#routes.declarationIn(source, name);
|
|
1097
|
-
if (!declared)
|
|
1098
|
-
continue;
|
|
1099
|
-
found = { source, statement: declared.statement, declared: declared.declared };
|
|
1100
|
-
break;
|
|
1101
|
-
}
|
|
1102
|
-
this.#declarations.set(key, found);
|
|
1103
|
-
return found;
|
|
1819
|
+
#madeInPlaceIdOf(name, file) {
|
|
1820
|
+
const made = this.#resolver.madeInPlace(file.fileName, name);
|
|
1821
|
+
if (made)
|
|
1822
|
+
return this.#guardNodeOf(made);
|
|
1823
|
+
const head = name.slice(name.indexOf(":") + 1, name.indexOf("@")).split(".")[0] ?? name;
|
|
1824
|
+
const imported = file.imports.find((item) => item.name === head);
|
|
1825
|
+
const from = imported ? this.#resolver.ownerOfSpecifier(file.fileName, imported.from) : file.owner;
|
|
1826
|
+
if (from === undefined || from === frameworkOwner)
|
|
1827
|
+
return null;
|
|
1828
|
+
const scope = `${this.#resolver.inOwner(file.fileName, file.owner)}#${name}`;
|
|
1829
|
+
const label = guardLabelOf(name, file.guardLabels);
|
|
1830
|
+
return this.#addNode(this.#key("guard", file.owner, scope), "guard", file.owner, null, name, null, [], label).id;
|
|
1104
1831
|
}
|
|
1105
1832
|
#addRoutes() {
|
|
1106
1833
|
const routes = [];
|
|
@@ -1153,22 +1880,23 @@ ${dir}`, node);
|
|
|
1153
1880
|
}
|
|
1154
1881
|
#routeGuardIdOf({ name: local, local: declared }, fact, text) {
|
|
1155
1882
|
if (declared) {
|
|
1156
|
-
const
|
|
1157
|
-
const id = this.#key("guard", fact.owner, `${file}#${local}`);
|
|
1883
|
+
const id = this.#key("guard", fact.owner, `${this.#resolver.inOwner(fact.fileName, fact.owner)}#${local}`);
|
|
1158
1884
|
return this.#routeGuardNodeOf(id, fact.owner, local, declared.span, declared.doc, text, declared.extent);
|
|
1159
1885
|
}
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1886
|
+
const [head = local, member] = local.split(".");
|
|
1887
|
+
const imported = fact.imports.find((item) => item.name === head);
|
|
1888
|
+
if (!imported || member && imported.imported !== "*")
|
|
1162
1889
|
return null;
|
|
1163
|
-
const name = imported.imported !== "default" && imported.imported !== "*" ? imported.imported : local;
|
|
1164
|
-
const from = this.#ownerOfSpecifier(fact.fileName, imported.from);
|
|
1890
|
+
const name = member ?? (imported.imported !== "default" && imported.imported !== "*" ? imported.imported : local);
|
|
1891
|
+
const from = this.#resolver.ownerOfSpecifier(fact.fileName, imported.from);
|
|
1165
1892
|
if (!from || from === frameworkOwner)
|
|
1166
1893
|
return null;
|
|
1167
|
-
const found = this.#declarationOf(from, name, this.#pathOfSpecifier(fact.fileName, imported.from));
|
|
1894
|
+
const found = this.#resolver.declarationOf(from, name, this.#resolver.pathOfSpecifier(fact.fileName, imported.from));
|
|
1168
1895
|
if (!found)
|
|
1169
1896
|
return null;
|
|
1170
1897
|
const { reader } = this.#source;
|
|
1171
|
-
|
|
1898
|
+
const owner = this.#source.ownerOf(found.source.fileName) ?? from;
|
|
1899
|
+
return this.#routeGuardNodeOf(this.#key("guard", owner, `${this.#resolver.inOwner(found.source.fileName, owner)}#${found.name}`), owner, found.name, reader.spanOf(found.source, found.declared), reader.docOf(found.source, found.statement), found.source.text, found.extent);
|
|
1172
1900
|
}
|
|
1173
1901
|
#routeGuardNodeOf(id, owner, name, span, doc, text, extent) {
|
|
1174
1902
|
const isNew = !this.#nodes.has(id);
|
|
@@ -1178,60 +1906,22 @@ ${dir}`, node);
|
|
|
1178
1906
|
this.#digest(node, text, extent);
|
|
1179
1907
|
return node.id;
|
|
1180
1908
|
}
|
|
1181
|
-
#
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1184
|
-
const from = imported ? this.#ownerOfSpecifier(file.fileName, imported.from) : undefined;
|
|
1185
|
-
const name = member ?? (imported && imported.imported !== "default" && imported.imported !== "*" ? imported.imported : head);
|
|
1186
|
-
const declared = this.#adaptors.get(name) ?? [];
|
|
1187
|
-
const found = from === undefined ? declared.find((adaptor) => adaptor.owner === file.owner) ?? declared.find((adaptor) => this.#visible(adaptor.owner, file.owner)) : declared.find((adaptor) => adaptor.owner === from) ?? (from === frameworkOwner ? undefined : declared.find((adaptor) => this.#visible(adaptor.owner, from)));
|
|
1188
|
-
if (found)
|
|
1189
|
-
return this.#adaptorNodeOf(found);
|
|
1190
|
-
const owner = from === frameworkOwner ? null : imported ? from ?? null : file.owner;
|
|
1191
|
-
const detail = from === frameworkOwner ? [frameworkOwner] : [];
|
|
1192
|
-
return this.#addNode(this.#key("adaptor", owner, name), "adaptor", owner, null, name, null, detail).id;
|
|
1193
|
-
}
|
|
1194
|
-
#adaptorNodeOf({ fact, owner, fileName }) {
|
|
1195
|
-
const id = this.#key("adaptor", owner, fact.name);
|
|
1196
|
-
const detail = fact.refName ? [fact.refName] : [];
|
|
1197
|
-
const node = this.#addNode(id, "adaptor", owner, null, fact.name, fact.span, detail);
|
|
1198
|
-
if (fact.doc && !node.doc)
|
|
1199
|
-
node.doc = fact.doc;
|
|
1200
|
-
this.#digest(node, this.#source.readText(fileName), fact.extent);
|
|
1201
|
-
return node.id;
|
|
1202
|
-
}
|
|
1203
|
-
#guardNodeOf({ name, owner, span, scope, doc, parts }) {
|
|
1204
|
-
const node = this.#addNode(this.#key("guard", owner, name), "guard", owner, null, name, span, scope ? [scope] : []);
|
|
1909
|
+
#guardNodeOf({ name, owner, span, scope, doc, parts, file, label }) {
|
|
1910
|
+
const id = this.#key("guard", owner, file ? `${file}#${name}` : name);
|
|
1911
|
+
const node = this.#addNode(id, "guard", owner, null, name, span, scope ? [scope] : [], label ?? name);
|
|
1205
1912
|
if (doc && !node.doc)
|
|
1206
1913
|
node.doc = doc;
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1914
|
+
if (node.digest)
|
|
1915
|
+
return node.id;
|
|
1916
|
+
const digest = this.#hashOf(...parts.map(({ fileName, extent }) => [this.#source.readText(fileName), extent]));
|
|
1917
|
+
if (!digest)
|
|
1918
|
+
return node.id;
|
|
1919
|
+
node.digest = digest;
|
|
1920
|
+
const sources = [...new Set(parts.map(({ fileName }) => this.#source.relative(fileName)))].filter((source) => source !== node.at?.file);
|
|
1921
|
+
if (sources.length)
|
|
1922
|
+
node.sources = sources;
|
|
1213
1923
|
return node.id;
|
|
1214
1924
|
}
|
|
1215
|
-
#ownerOfSpecifier(fileName, specifier) {
|
|
1216
|
-
if (frameworkPattern.test(specifier))
|
|
1217
|
-
return frameworkOwner;
|
|
1218
|
-
const path = this.#pathOfSpecifier(fileName, specifier);
|
|
1219
|
-
return path === null ? undefined : ownerOf(path) ?? undefined;
|
|
1220
|
-
}
|
|
1221
|
-
#pathOfSpecifier(fileName, specifier) {
|
|
1222
|
-
const alias = specifier.match(aliasPattern);
|
|
1223
|
-
if (alias)
|
|
1224
|
-
return `${alias[1]}/${alias[2]}${specifier.slice(alias[0].length)}`;
|
|
1225
|
-
if (!specifier.startsWith("."))
|
|
1226
|
-
return null;
|
|
1227
|
-
const segments = this.#source.relative(fileName).split("/").slice(0, -1);
|
|
1228
|
-
for (const segment of specifier.split("/"))
|
|
1229
|
-
if (segment === "..")
|
|
1230
|
-
segments.pop();
|
|
1231
|
-
else if (segment !== ".")
|
|
1232
|
-
segments.push(segment);
|
|
1233
|
-
return segments.join("/");
|
|
1234
|
-
}
|
|
1235
1925
|
#moduleDirOf(fileName) {
|
|
1236
1926
|
const dir = this.#source.relative(fileName).match(moduleDirPattern)?.[1] ?? null;
|
|
1237
1927
|
return dir?.startsWith("_") && !dir.startsWith("__") ? dir.slice(1) : dir;
|
|
@@ -1267,20 +1957,13 @@ ${dir}`, node);
|
|
|
1267
1957
|
visit(constant);
|
|
1268
1958
|
return [...fields.values()];
|
|
1269
1959
|
}
|
|
1270
|
-
#visible(target, from) {
|
|
1271
|
-
return !from || !target || target === from || target.startsWith("libs/");
|
|
1272
|
-
}
|
|
1273
|
-
#soleVisible(declared, from) {
|
|
1274
|
-
const visible = declared.filter((other) => this.#visible(other.owner, from));
|
|
1275
|
-
return visible.length === 1 ? visible[0] : undefined;
|
|
1276
|
-
}
|
|
1277
1960
|
#targetNodeOf({ family, owner, refName, sourceKey }) {
|
|
1278
1961
|
const isDefault = /^[A-Z]/.test(sourceKey);
|
|
1279
1962
|
const id = family === "slice" || family === "query" ? this.#key(family, owner, refName, isDefault ? "" : sourceKey) : family === "model" || family === "formSetter" ? this.#key("model", owner, refName) : family === "endpoint" ? this.#key("endpoint", owner, refName, sourceKey) : null;
|
|
1280
1963
|
return id && this.#nodes.has(id) ? id : null;
|
|
1281
1964
|
}
|
|
1282
1965
|
#membersNamed(kind, name, owner, refName) {
|
|
1283
|
-
const nodes = (this.#members.get(`${kind}:${name}`) ?? []).filter((node) => (!refName || node.module === refName) && this.#visible(node.owner, owner));
|
|
1966
|
+
const nodes = (this.#members.get(`${kind}:${name}`) ?? []).filter((node) => (!refName || node.module === refName) && this.#resolver.visible(node.owner, owner));
|
|
1284
1967
|
const own = nodes.filter((node) => node.owner === owner);
|
|
1285
1968
|
return (own.length ? own : nodes).map((node) => node.id);
|
|
1286
1969
|
}
|
|
@@ -1325,10 +2008,8 @@ st
|
|
|
1325
2008
|
visit(source);
|
|
1326
2009
|
}
|
|
1327
2010
|
#link(fileName, source, identifier, chain, context, owner) {
|
|
1328
|
-
const plugged = chain?.length === 2 && chain[0] === "this" && context.kind === "method" && this.#isServiceThis(identifier) ? this.#
|
|
1329
|
-
|
|
1330
|
-
${chain[1]}`) : undefined;
|
|
1331
|
-
const resolved = plugged ? [plugged] : this.#source.resolve(fileName, identifier).map((target) => this.#targetNodeOf(target)).filter((id) => id !== null);
|
|
2011
|
+
const plugged = chain?.length === 2 && chain[0] === "this" && context.kind === "method" && this.#isServiceThis(identifier) ? this.#adaptors.pluggedOf(context.owner, context.module, chain[1]) : [];
|
|
2012
|
+
const resolved = plugged.length ? plugged : this.#source.resolve(fileName, identifier).map((target) => this.#targetNodeOf(target)).filter((id) => id !== null);
|
|
1332
2013
|
const targets = resolved.length ? resolved : this.#plainTargetsOf(chain, identifier.text, context, owner);
|
|
1333
2014
|
if (!targets.length)
|
|
1334
2015
|
return;
|
|
@@ -1634,6 +2315,10 @@ var endpointNames = (key) => {
|
|
|
1634
2315
|
};
|
|
1635
2316
|
|
|
1636
2317
|
// pkgs/@akanjs/language/nameTable.ts
|
|
2318
|
+
var guardDetailOf = (facts, guards) => {
|
|
2319
|
+
const labels = Object.assign({}, ...facts.map((file) => file.guardLabels));
|
|
2320
|
+
return guards.length ? [`guards: ${guards.map((guard) => guardLabelOf(guard, labels)).join(", ")}`] : [];
|
|
2321
|
+
};
|
|
1637
2322
|
var capitalize3 = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
1638
2323
|
|
|
1639
2324
|
class NameTable {
|
|
@@ -1733,7 +2418,7 @@ class NameTable {
|
|
|
1733
2418
|
role: "sig",
|
|
1734
2419
|
gate: "sig",
|
|
1735
2420
|
label: sliceClass.name,
|
|
1736
|
-
detail:
|
|
2421
|
+
detail: guardDetailOf(facts, sliceClass.guards)
|
|
1737
2422
|
});
|
|
1738
2423
|
if (modelDoc)
|
|
1739
2424
|
this.#add({
|
|
@@ -1798,7 +2483,7 @@ class NameTable {
|
|
|
1798
2483
|
sourceKey: slice.name,
|
|
1799
2484
|
span: slice.span,
|
|
1800
2485
|
label: `slice ${refName} (default)`,
|
|
1801
|
-
detail:
|
|
2486
|
+
detail: guardDetailOf(facts, slice.guards)
|
|
1802
2487
|
};
|
|
1803
2488
|
this.#addSliceNames(defaultTarget, refName, "", modelNameSet);
|
|
1804
2489
|
for (const key of slice.keys)
|
|
@@ -1808,7 +2493,7 @@ class NameTable {
|
|
|
1808
2493
|
sourceKey: key.name,
|
|
1809
2494
|
span: key.span,
|
|
1810
2495
|
label: `slice ${key.name}`,
|
|
1811
|
-
detail: [...
|
|
2496
|
+
detail: [...guardDetailOf(facts, key.guards), ...key.live ? ["live"] : []]
|
|
1812
2497
|
}, refName, key.name, modelNameSet, slice.name);
|
|
1813
2498
|
}
|
|
1814
2499
|
if (filterClass) {
|
|
@@ -1865,7 +2550,7 @@ class NameTable {
|
|
|
1865
2550
|
role: key.verb === "pubsub" ? "subscribe" : "listen",
|
|
1866
2551
|
gate: null,
|
|
1867
2552
|
label: `${key.verb} ${key.name}`,
|
|
1868
|
-
detail:
|
|
2553
|
+
detail: guardDetailOf(facts, key.guards)
|
|
1869
2554
|
});
|
|
1870
2555
|
}
|
|
1871
2556
|
}
|
|
@@ -1922,15 +2607,13 @@ var sourcePattern = /\.(constant|document|service|signal|store)\.ts$/;
|
|
|
1922
2607
|
var dictionaryPattern = /\.dictionary\.ts$/;
|
|
1923
2608
|
var srvkitPattern = /\/srvkit\//;
|
|
1924
2609
|
var wordPattern = /[A-Za-z_$][\w$]*/g;
|
|
1925
|
-
var
|
|
1926
|
-
var regExpSpecials = /[\\^$.*+?()[\]{}|]/g;
|
|
2610
|
+
var identifierPattern2 = /^[A-Za-z_$][\w$]*$/;
|
|
1927
2611
|
var stateReceivers = new Set(["use", "sel", "get", "do"]);
|
|
1928
2612
|
var lensFamilies = new Set(["slice", "query", "formSetter", "storeState", "endpoint"]);
|
|
1929
2613
|
var modellessFamilies = new Set(["query", "storeState", "endpoint"]);
|
|
1930
2614
|
var gateWords = { srv: "srv", sig: "sig", db: "db", slice: "slice" };
|
|
1931
2615
|
var parseCacheSize = 64;
|
|
1932
2616
|
var pathOf = (fileName) => fileName.replace(/\\/g, "/");
|
|
1933
|
-
var escapeRegExp = (text) => text.replace(regExpSpecials, (special) => `\\${special}`);
|
|
1934
2617
|
var receiverGap = String.raw`(?:\s|\)|\/\*[\s\S]*?\*\/)*`;
|
|
1935
2618
|
|
|
1936
2619
|
class AkanIndex {
|
|
@@ -2033,7 +2716,7 @@ ${[...new Set(text.match(wordPattern))].join(`
|
|
|
2033
2716
|
}
|
|
2034
2717
|
renameAt(givenName, offset, newKey, readText) {
|
|
2035
2718
|
const sources = this.#sourceEntriesAt(pathOf(givenName), offset).filter((entry) => entry.renamed);
|
|
2036
|
-
if (!sources.length || !
|
|
2719
|
+
if (!sources.length || !identifierPattern2.test(newKey))
|
|
2037
2720
|
return null;
|
|
2038
2721
|
return [
|
|
2039
2722
|
{ span: sources[0].span, newText: newKey },
|
|
@@ -2061,6 +2744,8 @@ canPass
|
|
|
2061
2744
|
adapt
|
|
2062
2745
|
`) || words.includes(`
|
|
2063
2746
|
dangerouslyAdapt
|
|
2747
|
+
`) || words.includes(`
|
|
2748
|
+
applyAdaptor
|
|
2064
2749
|
`);
|
|
2065
2750
|
}
|
|
2066
2751
|
static #sourceKeyOf({ family, sourceKey, label, detail, span }) {
|