@akanjs/cli 3.0.0-alpha.9 → 3.0.0-alpha.90
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/.build-stamp +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/{abstractCompactor-e5yq60qz.js → abstractCompactor-cefxzggj.js} +3 -5
- package/{agent.command-f0gqkfgz.js → agent.command-r93wmq7m.js} +11 -13
- package/{application.command-x903m2qq.js → application.command-5nqfqgdn.js} +62 -38
- package/{applicationBuildRunner-ha3y8ew8.js → applicationBuildRunner-b21m07fs.js} +23 -20
- package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-3wh58n2m.js} +8 -9
- package/buildBatch.proc.js +12 -14
- package/capacitorApp-gzcx6txb.js +12 -0
- package/{cloud.command-zs6kyf83.js → cloud.command-g0pk0k60.js} +15 -18
- package/commandManifest.json +1 -1
- package/{context.command-fmepgjzg.js → context.command-qd8kqrnh.js} +19 -23
- package/dependencyScanner-shs82ynp.js +8 -0
- package/devTui-2xf0meaq.js +552 -0
- package/devTuiView-7jy8f30g.js +9 -0
- package/getDirname-dr3jz1xg.js +7 -0
- package/{guideline.command-n6sw0a0c.js → guideline.command-f38x8jhw.js} +16 -18
- package/guidelines/agentRule/agentRule.generate.json +31 -0
- package/guidelines/agentRule/agentRule.instruction.md +533 -0
- package/guidelines/conventions/conventions.instruction.md +406 -197
- package/guidelines/cssRule/cssRule.instruction.md +7 -0
- package/guidelines/framework/framework.generate.json +3 -2
- package/guidelines/framework/framework.instruction.md +1 -9
- package/guidelines/mcpRule/mcpRule.generate.json +25 -0
- package/guidelines/mcpRule/mcpRule.instruction.md +217 -0
- package/guidelines/modelStore/modelStore.instruction.md +12 -0
- package/guidelines/modelTemplate/modelTemplate.instruction.md +5 -0
- package/guidelines/queryRule/queryRule.generate.json +31 -0
- package/guidelines/queryRule/queryRule.instruction.md +143 -0
- package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
- package/guidelines/runtimeRule/runtimeRule.instruction.md +213 -0
- package/guidelines/ssrRule/ssrRule.instruction.md +49 -5
- package/guidelines/transportRule/transportRule.generate.json +31 -0
- package/guidelines/transportRule/transportRule.instruction.md +60 -0
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +51 -760
- package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +402 -0
- package/incrementalBuilder.proc.js +26 -25
- package/{index-774gcx29.js → index-0emb9eat.js} +41 -34
- package/index-3d9y066c.js +76 -0
- package/{index-3vta1tqa.js → index-3vvbv974.js} +32 -32
- package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
- package/{index-y4vkyhvv.js → index-5xdqxbn9.js} +14 -14
- package/{index-1577bej2.js → index-6wwzs1bc.js} +2 -2
- package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
- package/{index-jhpa13zj.js → index-9bwhxrqf.js} +57 -57
- package/{index-bjpxzr6s.js → index-9wtbw98m.js} +6 -3
- package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
- package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
- package/{index-kdh76j26.js → index-cezy7yb3.js} +100 -32
- package/{index-62955kn5.js → index-d61rcfa2.js} +758 -127
- package/{index-82jrh4hj.js → index-deekbtkj.js} +13 -13
- package/{index-rxkpk7n4.js → index-e82dyeqy.js} +4 -4
- package/{index-qzdjd4ct.js → index-h759vjfa.js} +18 -18
- package/{index-j37qq1f2.js → index-h7yw2z9n.js} +7 -7
- package/{index-qb61h0k7.js → index-jydtyzva.js} +1098 -1360
- package/{index-46tjzh6s.js → index-kcxmv48c.js} +29 -29
- package/{index-4m0bphac.js → index-kfnr6c19.js} +31 -30
- package/{index-xmmzf150.js → index-mpmyqm1y.js} +1 -1
- package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
- package/{index-0cj2zxbm.js → index-n85fer99.js} +12 -11
- package/{index-779ske1y.js → index-pgsswppj.js} +27 -27
- package/index-pvb4vdwr.js +159 -0
- package/{index-67546d0j.js → index-q0y2jq9j.js} +14 -2
- package/{index-z8ptfkzv.js → index-q8sn827n.js} +969 -240
- package/{index-5sn5w2mt.js → index-r50qen0z.js} +94 -61
- package/{index-eyyf2149.js → index-rntcdss1.js} +36 -37
- package/{index-gcv8ncfh.js → index-sjak5v5b.js} +16 -12
- package/{index-j9cxndc0.js → index-w8kmx1b6.js} +20 -11
- package/{index-jf2ya7r2.js → index-y74k5anj.js} +8 -8
- package/{index-d43t8af2.js → index-zkvpaggp.js} +58 -25
- package/index.js +22 -25
- package/{library.command-cv9gdbmg.js → library.command-em103wtn.js} +11 -10
- package/{localRegistry.command-d8fg36zc.js → localRegistry.command-gz8apn4y.js} +20 -23
- package/{module.command-qyr1s9cn.js → module.command-451pztkc.js} +13 -16
- package/{package.command-wsjx0k47.js → package.command-rfhyf38t.js} +8 -10
- package/package.json +5 -7
- package/{page.command-rdzg65rm.js → page.command-kt19da5g.js} +7 -9
- package/{primitive.command-89g38b53.js → primitive.command-pgy69ere.js} +13 -16
- package/{quality.command-0cs2z92d.js → quality.command-gpe2vqb5.js} +226 -105
- package/recipeScanner-fpagtgc1.js +11 -0
- package/{repair.command-6fphxfwj.js → repair.command-2sbrz187.js} +12 -14
- package/{routeSourceValidator-e4jmbfcx.js → routeSourceValidator-ze6x894c.js} +0 -2
- package/{scalar.command-xfer69s6.js → scalar.command-jcf4e5sv.js} +11 -14
- package/subspace.command-kqzqtk0p.js +755 -0
- package/templates/app/env/env.client.debug.ts.template +1 -5
- package/templates/app/env/env.client.develop.ts.template +1 -5
- package/templates/app/env/env.client.local.ts.template +1 -5
- package/templates/app/env/env.client.main.ts.template +1 -5
- package/templates/app/env/env.client.testing.ts.template +1 -5
- package/templates/app/env/env.client.type.ts +2 -6
- package/templates/app/env/env.server.debug.ts.template +1 -5
- package/templates/app/env/env.server.develop.ts.template +1 -5
- package/templates/app/env/env.server.local.ts.template +1 -5
- package/templates/app/env/env.server.main.ts.template +1 -5
- package/templates/app/main.ts +3 -1
- package/templates/appSample/lib/task/task.signal.ts +11 -6
- package/templates/appSample/srvkit/AuthGuard.ts +9 -5
- package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
- package/templates/crudPages/[__model__Id]/edit/page.tsx +5 -5
- package/templates/crudPages/[__model__Id]/page.tsx +10 -13
- package/templates/crudPages/new/page.tsx +5 -5
- package/templates/crudPages/page.tsx +9 -9
- package/templates/crudSinglePage/page.tsx +11 -7
- package/templates/env/_env.server.type.ts +7 -5
- package/templates/lib/db.ts +1 -1
- package/templates/lib/dict.ts +1 -1
- package/templates/lib/sig.ts +1 -1
- package/templates/lib/srv.ts +6 -2
- package/templates/module/__model__.dictionary.ts +4 -1
- package/templates/module/__model__.signal.ts +2 -3
- package/templates/module/__model__.store.ts +1 -2
- package/templates/workspaceRoot/.gitignore.template +0 -1
- package/templates/workspaceRoot/.husky/pre-commit.template +16 -0
- package/templates/workspaceRoot/.vscode/settings.json.template +1 -1
- package/templates/workspaceRoot/AGENTS.md.template +4 -0
- package/templates/workspaceRoot/CLAUDE.md.template +19 -0
- package/templates/workspaceRoot/biome.json.template +2 -1
- package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +10 -14
- package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
- package/templates/workspaceRoot/package.json.template +2 -4
- package/typeChecker-3xgrzc2v.js +7 -0
- package/typecheck.proc.js +4 -5
- package/{workflow.command-2ygfqt4f.js → workflow.command-m4z25a2p.js} +22 -25
- package/{workspace.command-88m0zs64.js → workspace.command-dqgj9zbk.js} +96 -81
- package/capacitorApp-9xswcgx7.js +0 -61
- package/dependencyScanner-f6grrep3.js +0 -9
- package/getDirname-m9s0cmhq.js +0 -8
- package/index-mxvakhsm.js +0 -66
- package/index-r24hmh0q.js +0 -4
- package/recipeScanner-tghkzrrt.js +0 -12
- package/typeChecker-kravn7ns.js +0 -8
- /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
- /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
AbstractDoc
|
|
4
|
-
} from "./index-0wae5ebk.js";
|
|
5
2
|
import {
|
|
6
3
|
Workspace,
|
|
7
|
-
command,
|
|
8
4
|
runner,
|
|
9
|
-
script
|
|
10
|
-
|
|
5
|
+
script,
|
|
6
|
+
command
|
|
7
|
+
} from "./index-cezy7yb3.js";
|
|
8
|
+
import"./index-q0y2jq9j.js";
|
|
9
|
+
import {
|
|
10
|
+
rootAllowedFiles,
|
|
11
|
+
rootAllowedDirs,
|
|
12
|
+
isAllowedLibFacetRootFile
|
|
13
|
+
} from "./index-q8sn827n.js";
|
|
14
|
+
import"./index-6wwzs1bc.js";
|
|
11
15
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "./index-z8ptfkzv.js";
|
|
15
|
-
import"./index-mxvakhsm.js";
|
|
16
|
-
import"./index-1577bej2.js";
|
|
17
|
-
import"./index-67546d0j.js";
|
|
18
|
-
import"./index-r24hmh0q.js";
|
|
16
|
+
AbstractDoc
|
|
17
|
+
} from "./index-meh558jy.js";
|
|
19
18
|
|
|
20
19
|
// pkgs/@akanjs/devkit/qualityScanner.ts
|
|
21
20
|
import { createHash } from "crypto";
|
|
@@ -23,10 +22,79 @@ import { readdir, readFile, stat } from "fs/promises";
|
|
|
23
22
|
import path from "path";
|
|
24
23
|
import { RESERVED_ROUTE_CONFIG_EXPORTS } from "akanjs/common";
|
|
25
24
|
import ignore from "ignore";
|
|
26
|
-
import
|
|
25
|
+
import ts3 from "typescript";
|
|
27
26
|
|
|
28
|
-
// pkgs/@akanjs/devkit/
|
|
27
|
+
// pkgs/@akanjs/devkit/formSetterScanner.ts
|
|
29
28
|
import ts from "typescript";
|
|
29
|
+
|
|
30
|
+
class FormSetterScanner {
|
|
31
|
+
static #fieldSetter = /^set[A-Za-z0-9_$]*On[A-Za-z0-9_$]*$/;
|
|
32
|
+
scan(sourceFiles) {
|
|
33
|
+
return sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".tsx")).flatMap((f) => this.#scanFile(f));
|
|
34
|
+
}
|
|
35
|
+
#scanFile({ file, sourceFile }) {
|
|
36
|
+
const wrapped = [];
|
|
37
|
+
const visit = (node) => {
|
|
38
|
+
if (ts.isJsxAttribute(node)) {
|
|
39
|
+
const setter = FormSetterScanner.#wrappedSetterOf(node);
|
|
40
|
+
if (setter)
|
|
41
|
+
wrapped.push({ setter, line: FormSetterScanner.#lineOf(sourceFile, node) });
|
|
42
|
+
}
|
|
43
|
+
ts.forEachChild(node, visit);
|
|
44
|
+
};
|
|
45
|
+
visit(sourceFile);
|
|
46
|
+
if (!wrapped.length)
|
|
47
|
+
return [];
|
|
48
|
+
const setters = [...new Set(wrapped.map((entry) => entry.setter))].sort();
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
rule: "akan.agent.unpublished-form-setter",
|
|
52
|
+
scope: "agent",
|
|
53
|
+
severity: "warning",
|
|
54
|
+
message: setters.length === 1 ? `${setters[0]} is reached through a wrapper, so that field publishes no agent tool and carries no data-akan-action.` : `${setters.length} field setters are reached through a wrapper, so those fields publish no agent tool and carry no data-akan-action: ${setters.join(", ")}.`,
|
|
55
|
+
file,
|
|
56
|
+
line: wrapped[0]?.line,
|
|
57
|
+
locations: wrapped.map(({ line }) => ({ file, line }))
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
static #wrappedSetterOf(attribute) {
|
|
62
|
+
if (!ts.isIdentifier(attribute.name) || !/^on[A-Z]/.test(attribute.name.text))
|
|
63
|
+
return null;
|
|
64
|
+
const initializer = attribute.initializer;
|
|
65
|
+
if (!initializer || !ts.isJsxExpression(initializer) || !initializer.expression)
|
|
66
|
+
return null;
|
|
67
|
+
const handler = initializer.expression;
|
|
68
|
+
if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler))
|
|
69
|
+
return null;
|
|
70
|
+
if (!handler.parameters.length)
|
|
71
|
+
return null;
|
|
72
|
+
return FormSetterScanner.#setterCallIn(handler.body);
|
|
73
|
+
}
|
|
74
|
+
static #setterCallIn(node) {
|
|
75
|
+
let found = null;
|
|
76
|
+
const visit = (current) => {
|
|
77
|
+
if (found)
|
|
78
|
+
return;
|
|
79
|
+
if (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
|
|
80
|
+
const { expression, name } = current.expression;
|
|
81
|
+
if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "st" && expression.name.text === "do" && FormSetterScanner.#fieldSetter.test(name.text)) {
|
|
82
|
+
found = name.text;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
ts.forEachChild(current, visit);
|
|
87
|
+
};
|
|
88
|
+
visit(node);
|
|
89
|
+
return found;
|
|
90
|
+
}
|
|
91
|
+
static #lineOf(sourceFile, node) {
|
|
92
|
+
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// pkgs/@akanjs/devkit/ssrScanner.ts
|
|
97
|
+
import ts2 from "typescript";
|
|
30
98
|
var STATIC_COMPONENT_MIN_MASS = 4;
|
|
31
99
|
var MIXED_COMPONENT_MIN_MASS = 10;
|
|
32
100
|
var MIXED_COMPONENT_MAX_TOUCHES = 2;
|
|
@@ -144,31 +212,31 @@ class SsrScanner {
|
|
|
144
212
|
});
|
|
145
213
|
}
|
|
146
214
|
}
|
|
147
|
-
|
|
215
|
+
ts2.forEachChild(node, visit);
|
|
148
216
|
};
|
|
149
|
-
|
|
217
|
+
ts2.forEachChild(sourceFile.sourceFile, visit);
|
|
150
218
|
return warnings;
|
|
151
219
|
}
|
|
152
220
|
#isMountEffect(sourceFile, node) {
|
|
153
|
-
if (!
|
|
221
|
+
if (!ts2.isCallExpression(node))
|
|
154
222
|
return false;
|
|
155
223
|
const callee = node.expression.getText(sourceFile);
|
|
156
224
|
if (callee !== "useEffect" && callee !== "useLayoutEffect")
|
|
157
225
|
return false;
|
|
158
226
|
const deps = node.arguments[1];
|
|
159
|
-
return !!deps &&
|
|
227
|
+
return !!deps && ts2.isArrayLiteralExpression(deps) && deps.elements.length === 0;
|
|
160
228
|
}
|
|
161
229
|
#getLoadCalls(sourceFile, node) {
|
|
162
230
|
const calls = [];
|
|
163
231
|
const visit = (child) => {
|
|
164
|
-
if (
|
|
232
|
+
if (ts2.isCallExpression(child)) {
|
|
165
233
|
const callee = child.expression.getText(sourceFile);
|
|
166
234
|
if (/^fetch\.[a-z]/.test(callee) || /^st\.do\.(init|get|view|load|list|count|insight)[A-Z]/.test(callee))
|
|
167
235
|
calls.push({ callee, node: child });
|
|
168
236
|
}
|
|
169
|
-
|
|
237
|
+
ts2.forEachChild(child, visit);
|
|
170
238
|
};
|
|
171
|
-
|
|
239
|
+
ts2.forEachChild(node, visit);
|
|
172
240
|
return calls;
|
|
173
241
|
}
|
|
174
242
|
#getTemplateStateWarnings(sourceFile) {
|
|
@@ -176,7 +244,7 @@ class SsrScanner {
|
|
|
176
244
|
return [];
|
|
177
245
|
const warnings = [];
|
|
178
246
|
const visit = (node) => {
|
|
179
|
-
if (
|
|
247
|
+
if (ts2.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
|
|
180
248
|
warnings.push({
|
|
181
249
|
rule: "akan.ssr.template-client-state",
|
|
182
250
|
scope: "ssr",
|
|
@@ -186,9 +254,9 @@ class SsrScanner {
|
|
|
186
254
|
message: "Template holds form state in useState. Templates are store-driven and carry no local state."
|
|
187
255
|
});
|
|
188
256
|
}
|
|
189
|
-
|
|
257
|
+
ts2.forEachChild(node, visit);
|
|
190
258
|
};
|
|
191
|
-
|
|
259
|
+
ts2.forEachChild(sourceFile.sourceFile, visit);
|
|
192
260
|
return warnings;
|
|
193
261
|
}
|
|
194
262
|
#measureBalance(sourceFiles) {
|
|
@@ -229,14 +297,14 @@ class SsrScanner {
|
|
|
229
297
|
return components;
|
|
230
298
|
}
|
|
231
299
|
#getComponentNodes(statement) {
|
|
232
|
-
if (
|
|
300
|
+
if (ts2.isFunctionDeclaration(statement) && statement.body)
|
|
233
301
|
return [{ name: statement.name?.text ?? "default", node: statement.body }];
|
|
234
|
-
if (!
|
|
302
|
+
if (!ts2.isVariableStatement(statement))
|
|
235
303
|
return [];
|
|
236
304
|
return statement.declarationList.declarations.flatMap((declaration) => {
|
|
237
|
-
if (!
|
|
305
|
+
if (!ts2.isIdentifier(declaration.name) || !declaration.initializer)
|
|
238
306
|
return [];
|
|
239
|
-
if (!
|
|
307
|
+
if (!ts2.isArrowFunction(declaration.initializer) && !ts2.isFunctionExpression(declaration.initializer))
|
|
240
308
|
return [];
|
|
241
309
|
return [{ name: declaration.name.text, node: declaration.initializer }];
|
|
242
310
|
});
|
|
@@ -244,7 +312,7 @@ class SsrScanner {
|
|
|
244
312
|
#getTouches(sourceFile, node) {
|
|
245
313
|
const touches = [];
|
|
246
314
|
const visit = (child) => {
|
|
247
|
-
if (
|
|
315
|
+
if (ts2.isCallExpression(child)) {
|
|
248
316
|
const callee = child.expression.getText(sourceFile);
|
|
249
317
|
const bareName = callee.split(".").pop() ?? callee;
|
|
250
318
|
if (callee === "createContext" || callee === "lazy")
|
|
@@ -252,60 +320,60 @@ class SsrScanner {
|
|
|
252
320
|
else if (/^use[A-Z]/.test(bareName) && !SsrScanner.#serverSafeCalls.has(bareName))
|
|
253
321
|
touches.push(bareName);
|
|
254
322
|
}
|
|
255
|
-
if (
|
|
323
|
+
if (ts2.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
|
|
256
324
|
touches.push(child.name.getText(sourceFile));
|
|
257
|
-
if (
|
|
325
|
+
if (ts2.isPropertyAccessExpression(child)) {
|
|
258
326
|
const root = getAccessRoot(child);
|
|
259
327
|
if (root === "st")
|
|
260
328
|
touches.push("st");
|
|
261
329
|
else if (SsrScanner.#clientGlobals.has(root))
|
|
262
330
|
touches.push(root);
|
|
263
331
|
}
|
|
264
|
-
|
|
332
|
+
ts2.forEachChild(child, visit);
|
|
265
333
|
};
|
|
266
|
-
|
|
334
|
+
ts2.forEachChild(node, visit);
|
|
267
335
|
return touches;
|
|
268
336
|
}
|
|
269
337
|
#getTagNames(sourceFile, node) {
|
|
270
338
|
const tags = new Set;
|
|
271
339
|
const visit = (child) => {
|
|
272
|
-
if (
|
|
340
|
+
if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
|
|
273
341
|
tags.add(child.tagName.getText(sourceFile).split(".")[0]);
|
|
274
|
-
|
|
342
|
+
ts2.forEachChild(child, visit);
|
|
275
343
|
};
|
|
276
|
-
|
|
344
|
+
ts2.forEachChild(node, visit);
|
|
277
345
|
return tags;
|
|
278
346
|
}
|
|
279
347
|
#getMass(node) {
|
|
280
348
|
let mass = 0;
|
|
281
349
|
const visit = (child) => {
|
|
282
|
-
if (
|
|
350
|
+
if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
|
|
283
351
|
mass += 1;
|
|
284
|
-
|
|
352
|
+
ts2.forEachChild(child, visit);
|
|
285
353
|
};
|
|
286
|
-
|
|
354
|
+
ts2.forEachChild(node, visit);
|
|
287
355
|
return mass;
|
|
288
356
|
}
|
|
289
357
|
#hasUseClient(sourceFile) {
|
|
290
358
|
const first = sourceFile.statements[0];
|
|
291
|
-
if (!first || !
|
|
359
|
+
if (!first || !ts2.isExpressionStatement(first) || !ts2.isStringLiteral(first.expression))
|
|
292
360
|
return false;
|
|
293
361
|
return first.expression.text === "use client";
|
|
294
362
|
}
|
|
295
363
|
#hasVendorImport(sourceFile) {
|
|
296
|
-
return sourceFile.statements.some((statement) =>
|
|
364
|
+
return sourceFile.statements.some((statement) => ts2.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
|
|
297
365
|
}
|
|
298
366
|
#getVendorNames(sourceFile) {
|
|
299
367
|
const names = new Set;
|
|
300
368
|
for (const statement of sourceFile.statements) {
|
|
301
|
-
if (!
|
|
369
|
+
if (!ts2.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
|
|
302
370
|
continue;
|
|
303
371
|
const clause = statement.importClause;
|
|
304
372
|
if (clause?.name)
|
|
305
373
|
names.add(clause.name.text);
|
|
306
|
-
if (clause?.namedBindings &&
|
|
374
|
+
if (clause?.namedBindings && ts2.isNamespaceImport(clause.namedBindings))
|
|
307
375
|
names.add(clause.namedBindings.name.text);
|
|
308
|
-
if (clause?.namedBindings &&
|
|
376
|
+
if (clause?.namedBindings && ts2.isNamedImports(clause.namedBindings))
|
|
309
377
|
for (const element of clause.namedBindings.elements)
|
|
310
378
|
names.add(element.name.text);
|
|
311
379
|
}
|
|
@@ -313,10 +381,10 @@ class SsrScanner {
|
|
|
313
381
|
}
|
|
314
382
|
#importsClientRuntime(sourceFile) {
|
|
315
383
|
for (const statement of sourceFile.statements) {
|
|
316
|
-
if (!
|
|
384
|
+
if (!ts2.isImportDeclaration(statement))
|
|
317
385
|
continue;
|
|
318
386
|
const bindings = statement.importClause?.namedBindings;
|
|
319
|
-
if (!bindings || !
|
|
387
|
+
if (!bindings || !ts2.isNamedImports(bindings))
|
|
320
388
|
continue;
|
|
321
389
|
if (bindings.elements.some((element) => SsrScanner.#clientRuntimeImports.has(element.name.text)))
|
|
322
390
|
return true;
|
|
@@ -367,7 +435,7 @@ function getShare(serverMass, clientMass) {
|
|
|
367
435
|
return total === 0 ? 1 : serverMass / total;
|
|
368
436
|
}
|
|
369
437
|
function getSpecifier(statement) {
|
|
370
|
-
return
|
|
438
|
+
return ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
|
|
371
439
|
}
|
|
372
440
|
function isVendorSpecifier(specifier) {
|
|
373
441
|
if (specifier === "" || specifier.startsWith(".") || specifier.startsWith("/"))
|
|
@@ -380,9 +448,9 @@ function isVendorSpecifier(specifier) {
|
|
|
380
448
|
}
|
|
381
449
|
function getAccessRoot(node) {
|
|
382
450
|
let current = node;
|
|
383
|
-
while (
|
|
451
|
+
while (ts2.isPropertyAccessExpression(current))
|
|
384
452
|
current = current.expression;
|
|
385
|
-
return
|
|
453
|
+
return ts2.isIdentifier(current) ? current.text : "";
|
|
386
454
|
}
|
|
387
455
|
|
|
388
456
|
// pkgs/@akanjs/devkit/qualityScanner.ts
|
|
@@ -406,7 +474,8 @@ var SUGGESTED_RULES = [
|
|
|
406
474
|
"Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
|
|
407
475
|
"Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
|
|
408
476
|
"Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
|
|
409
|
-
"
|
|
477
|
+
"Hand a form control its store setter by reference so the field publishes an agent tool; put normalization in the control's transform prop and a multi-write in a _postSet<Field> hook on the store.",
|
|
478
|
+
"Move shared app and lib utilities to common/ instead of creating apps/*/base or libs/*/base.",
|
|
410
479
|
"Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
|
|
411
480
|
];
|
|
412
481
|
var CONVENTION_SUFFIXES = [
|
|
@@ -424,15 +493,20 @@ var RULE_FIXES = {
|
|
|
424
493
|
"akan.file.max-lines": "Break the file into smaller focused modules; keep one primary responsibility per file.",
|
|
425
494
|
"akan.file.abstract-max-lines": "Run `akan compact <app-or-lib>` to rewrite the abstract with the AI editor, keeping only the invariants and workflows the source files cannot show.",
|
|
426
495
|
"akan.file.placeholder-export": "Remove the placeholder export; generated indexes should only re-export real modules.",
|
|
496
|
+
"akan.file.bang-comment-in-client": "Rewrite the marker as `// FIXME:` or `// TODO:`. Keep the bang form for server, srvkit, and CLI files.",
|
|
427
497
|
"akan.file.dictionary-stale-text": "Replace the scaffold text with real localized copy for this dictionary entry.",
|
|
428
498
|
"akan.file.global-declaration": "Move the global declaration into an approved low-level integration file (e.g. webkit) and keep it isolated.",
|
|
429
499
|
"akan.file.window-augmentation": "Move the Window augmentation into an approved browser integration file and keep it isolated.",
|
|
430
500
|
"akan.file.prototype-mutation": "Avoid prototype mutation, or isolate it in an approved low-level integration file.",
|
|
431
501
|
"akan.file.class-export-global-declaration": "Move the helper to a sibling file and import it into the class module.",
|
|
502
|
+
"akan.agent.unpublished-form-setter": "Pass the setter by reference where the wrapper only forwards (onChange={st.do.setTitleOnTask}); move normalization into the control's own transform prop; move a multi-write into a _postSet<Field> hook on the store, keeping the generated setter on the control.",
|
|
432
503
|
"akan.file.component-internal-declaration": "Move the type or helper to a type/util file in ui/, webkit/, or common/ by purpose. If it is the component's props, declare it as `interface <Component>Props`.",
|
|
433
504
|
"akan.file.component-export": "Move the value or type to a util/constant/type file in ui/, webkit/, or common/ and import it. Adding `export` is not a valid fix \u2014 only PascalCase components and their `<Component>Props` interface belong here.",
|
|
434
505
|
"akan.layout.app-root-file": "Move the file into a conventional app folder (common, env, lib, page, private, public, script, srvkit, ui, or webkit).",
|
|
435
|
-
"akan.layout.
|
|
506
|
+
"akan.layout.app-root-folder": "Move the folder's contents into a conventional app folder (common, env, lib, page, private, public, script, srvkit, ui, or webkit) and delete it.",
|
|
507
|
+
"akan.layout.lib-root-file": "Move the file into a conventional lib folder (common, env, lib, page, private, public, srvkit, ui, or webkit).",
|
|
508
|
+
"akan.layout.lib-root-folder": "Move the folder's contents into a conventional lib folder (common, env, lib, page, private, public, srvkit, ui, or webkit) and delete it.",
|
|
509
|
+
"akan.layout.lib-facet-file": "Move the file into a domain module folder under lib/; keep the lib facet root limited to generated support facets.",
|
|
436
510
|
"akan.layout.module-ui-file": "Rename the file to an allowed module UI name, or move it to ui/ if it is not a module component.",
|
|
437
511
|
"akan.ssr.unnecessary-use-client": 'Delete the "use client" directive so the file renders on the server. If it exists only to wrap one client child, drop the wrapper and use the child directly.',
|
|
438
512
|
"akan.ssr.client-static-component": "Move the component to a server file \u2014 a <Model>.Unit.tsx / <Model>.View.tsx for a module, or a ui/ file with no directive \u2014 and reference it from the client file.",
|
|
@@ -460,6 +534,7 @@ class AkanQualityScanner {
|
|
|
460
534
|
...sourceFiles.flatMap((sourceFile) => this.#scanConventionQuality(sourceFile)),
|
|
461
535
|
...sourceFiles.flatMap((sourceFile) => this.#scanLayoutQuality(sourceFile)),
|
|
462
536
|
...abstractFiles.flatMap((abstractFile) => this.#scanAbstractQuality(abstractFile)),
|
|
537
|
+
...new FormSetterScanner().scan(sourceFiles),
|
|
463
538
|
...ssr.warnings
|
|
464
539
|
];
|
|
465
540
|
return {
|
|
@@ -512,7 +587,7 @@ class AkanQualityScanner {
|
|
|
512
587
|
file,
|
|
513
588
|
absolutePath,
|
|
514
589
|
content,
|
|
515
|
-
sourceFile:
|
|
590
|
+
sourceFile: ts3.createSourceFile(file, content, ts3.ScriptTarget.Latest, true, getScriptKind(file))
|
|
516
591
|
};
|
|
517
592
|
}
|
|
518
593
|
async#readTextFile(workspaceRoot, file) {
|
|
@@ -570,6 +645,7 @@ class AkanQualityScanner {
|
|
|
570
645
|
message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
|
|
571
646
|
});
|
|
572
647
|
}
|
|
648
|
+
warnings.push(...getBangCommentWarnings(sourceFile));
|
|
573
649
|
warnings.push(...getPlaceholderExportWarnings(sourceFile));
|
|
574
650
|
warnings.push(...getDictionaryTextWarnings(sourceFile));
|
|
575
651
|
warnings.push(...getGlobalMutationWarnings(sourceFile));
|
|
@@ -671,25 +747,30 @@ class AkanQualityScanner {
|
|
|
671
747
|
];
|
|
672
748
|
}
|
|
673
749
|
#scanLayoutQuality(sourceFile) {
|
|
674
|
-
const segments = sourceFile.file.split("/");
|
|
675
750
|
const warnings = [];
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
751
|
+
const rootEntry = getSysRootEntry(sourceFile.file);
|
|
752
|
+
if (rootEntry) {
|
|
753
|
+
const { type, name, isDir } = rootEntry;
|
|
754
|
+
const allowed = isDir ? rootAllowedDirs[type].has(name) : rootAllowedFiles[type].has(name);
|
|
755
|
+
const kind = isDir ? "folder" : "file";
|
|
756
|
+
if (!allowed) {
|
|
757
|
+
warnings.push({
|
|
758
|
+
rule: `akan.layout.${type}-root-${kind}`,
|
|
759
|
+
scope: "layout",
|
|
760
|
+
severity: "warning",
|
|
761
|
+
file: sourceFile.file,
|
|
762
|
+
message: `Unexpected ${type} root ${kind} "${name}". Keep ${type} code in conventional ${type} folders.`
|
|
763
|
+
});
|
|
764
|
+
}
|
|
684
765
|
}
|
|
685
|
-
const
|
|
686
|
-
if (
|
|
766
|
+
const libFacetFile = getLibFacetRootFile(sourceFile.file);
|
|
767
|
+
if (libFacetFile && !isAllowedLibFacetRootFile(libFacetFile)) {
|
|
687
768
|
warnings.push({
|
|
688
|
-
rule: "akan.layout.lib-
|
|
769
|
+
rule: "akan.layout.lib-facet-file",
|
|
689
770
|
scope: "layout",
|
|
690
771
|
severity: "warning",
|
|
691
772
|
file: sourceFile.file,
|
|
692
|
-
message: `Unexpected lib root file "${
|
|
773
|
+
message: `Unexpected lib facet root file "${libFacetFile}". Keep direct lib/ root files limited to generated support facets.`
|
|
693
774
|
});
|
|
694
775
|
}
|
|
695
776
|
const moduleUiWarning = getModuleUiWarning(sourceFile.file);
|
|
@@ -759,7 +840,7 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
759
840
|
const declarations = [];
|
|
760
841
|
const nameExempt = isPageRouteFile(sourceFile.file) || isUiComponentFile(sourceFile.file);
|
|
761
842
|
for (const statement of sourceFile.sourceFile.statements) {
|
|
762
|
-
if (
|
|
843
|
+
if (ts3.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
|
|
763
844
|
declarations.push({
|
|
764
845
|
name: statement.name.text,
|
|
765
846
|
kind: "function",
|
|
@@ -769,7 +850,7 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
769
850
|
duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, false)
|
|
770
851
|
});
|
|
771
852
|
}
|
|
772
|
-
if (
|
|
853
|
+
if (ts3.isClassDeclaration(statement) && statement.name && isExported(statement)) {
|
|
773
854
|
declarations.push({
|
|
774
855
|
name: statement.name.text,
|
|
775
856
|
kind: "class",
|
|
@@ -779,9 +860,9 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
779
860
|
duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, isEnumClassStatement(sourceFile.sourceFile, statement))
|
|
780
861
|
});
|
|
781
862
|
}
|
|
782
|
-
if (
|
|
863
|
+
if (ts3.isVariableStatement(statement) && isExported(statement)) {
|
|
783
864
|
for (const declaration of statement.declarationList.declarations) {
|
|
784
|
-
if (!
|
|
865
|
+
if (!ts3.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
|
|
785
866
|
continue;
|
|
786
867
|
declarations.push({
|
|
787
868
|
name: declaration.name.text,
|
|
@@ -820,14 +901,14 @@ function isInLibModule(file) {
|
|
|
820
901
|
return (segments[0] === "apps" || segments[0] === "libs") && segments.includes("lib");
|
|
821
902
|
}
|
|
822
903
|
function isEnumClassStatement(sourceFile, statement) {
|
|
823
|
-
if (!
|
|
904
|
+
if (!ts3.isClassDeclaration(statement))
|
|
824
905
|
return false;
|
|
825
|
-
const heritageClause = statement.heritageClauses?.find((clause) => clause.token ===
|
|
906
|
+
const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
|
|
826
907
|
const expression = heritageClause?.types[0]?.expression;
|
|
827
908
|
return !!expression && expression.getText(sourceFile).startsWith("enumOf(");
|
|
828
909
|
}
|
|
829
910
|
function getExportedClassNames(sourceFile) {
|
|
830
|
-
return sourceFile.statements.filter((statement) =>
|
|
911
|
+
return sourceFile.statements.filter((statement) => ts3.isClassDeclaration(statement)).filter((statement) => isExported(statement)).map((statement) => statement.name?.text).filter((name) => !!name);
|
|
831
912
|
}
|
|
832
913
|
function isComponentDeclarationFile(file) {
|
|
833
914
|
if (!file.endsWith(".tsx"))
|
|
@@ -843,7 +924,7 @@ function isComponentDeclarationFile(file) {
|
|
|
843
924
|
function getComponentFileDeclarations(sourceFile) {
|
|
844
925
|
const reExportedNames = new Set;
|
|
845
926
|
for (const statement of sourceFile.statements) {
|
|
846
|
-
if (
|
|
927
|
+
if (ts3.isExportDeclaration(statement) && statement.exportClause && ts3.isNamedExports(statement.exportClause)) {
|
|
847
928
|
for (const element of statement.exportClause.elements)
|
|
848
929
|
reExportedNames.add((element.propertyName ?? element.name).text);
|
|
849
930
|
}
|
|
@@ -853,19 +934,19 @@ function getComponentFileDeclarations(sourceFile) {
|
|
|
853
934
|
const isDefaultExport = isDefaultExportStatement(statement);
|
|
854
935
|
const inlineExported = isExported(statement);
|
|
855
936
|
const add = (name, kind, line) => declarations.push({ name, kind, line, exported: inlineExported || reExportedNames.has(name), isDefaultExport });
|
|
856
|
-
if (
|
|
937
|
+
if (ts3.isInterfaceDeclaration(statement))
|
|
857
938
|
add(statement.name.text, "interface", getLine(sourceFile, statement));
|
|
858
|
-
else if (
|
|
939
|
+
else if (ts3.isTypeAliasDeclaration(statement))
|
|
859
940
|
add(statement.name.text, "type", getLine(sourceFile, statement));
|
|
860
|
-
else if (
|
|
941
|
+
else if (ts3.isEnumDeclaration(statement))
|
|
861
942
|
add(statement.name.text, "enum", getLine(sourceFile, statement));
|
|
862
|
-
else if (
|
|
943
|
+
else if (ts3.isFunctionDeclaration(statement) && statement.name)
|
|
863
944
|
add(statement.name.text, "function", getLine(sourceFile, statement));
|
|
864
|
-
else if (
|
|
945
|
+
else if (ts3.isClassDeclaration(statement) && statement.name)
|
|
865
946
|
add(statement.name.text, "class", getLine(sourceFile, statement));
|
|
866
|
-
else if (
|
|
947
|
+
else if (ts3.isVariableStatement(statement)) {
|
|
867
948
|
for (const declaration of statement.declarationList.declarations) {
|
|
868
|
-
if (!
|
|
949
|
+
if (!ts3.isIdentifier(declaration.name))
|
|
869
950
|
continue;
|
|
870
951
|
const kind = isFunctionLikeInitializer(declaration.initializer) ? "function" : "variable";
|
|
871
952
|
add(declaration.name.text, kind, getLine(sourceFile, declaration));
|
|
@@ -877,15 +958,15 @@ function getComponentFileDeclarations(sourceFile) {
|
|
|
877
958
|
function getCompoundComponentNames(sourceFile) {
|
|
878
959
|
const names = new Set;
|
|
879
960
|
for (const statement of sourceFile.statements) {
|
|
880
|
-
if (!
|
|
961
|
+
if (!ts3.isExpressionStatement(statement))
|
|
881
962
|
continue;
|
|
882
963
|
const { expression } = statement;
|
|
883
|
-
if (!
|
|
964
|
+
if (!ts3.isBinaryExpression(expression) || expression.operatorToken.kind !== ts3.SyntaxKind.EqualsToken)
|
|
884
965
|
continue;
|
|
885
|
-
if (!
|
|
966
|
+
if (!ts3.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
|
|
886
967
|
continue;
|
|
887
968
|
names.add(expression.left.name.text);
|
|
888
|
-
if (
|
|
969
|
+
if (ts3.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
|
|
889
970
|
names.add(expression.right.text);
|
|
890
971
|
}
|
|
891
972
|
return names;
|
|
@@ -905,9 +986,37 @@ function isPascalCaseName(name) {
|
|
|
905
986
|
return /^[A-Z]/.test(name) && !/^[A-Z0-9_]+$/.test(name);
|
|
906
987
|
}
|
|
907
988
|
function isDefaultExportStatement(statement) {
|
|
908
|
-
if (
|
|
989
|
+
if (ts3.isExportAssignment(statement))
|
|
909
990
|
return !statement.isExportEquals;
|
|
910
|
-
|
|
991
|
+
if (!ts3.canHaveModifiers(statement))
|
|
992
|
+
return false;
|
|
993
|
+
return !!ts3.getModifiers(statement)?.some((modifier) => modifier.kind === ts3.SyntaxKind.DefaultKeyword);
|
|
994
|
+
}
|
|
995
|
+
function getBangCommentWarnings(sourceFile) {
|
|
996
|
+
if (!isClientReachableFile(sourceFile.file))
|
|
997
|
+
return [];
|
|
998
|
+
return findPatternLines(sourceFile.content, /(?:^|[^\s/][ \t]+)\/[*/]!/).map((line) => ({
|
|
999
|
+
rule: "akan.file.bang-comment-in-client",
|
|
1000
|
+
scope: "file",
|
|
1001
|
+
severity: "warning",
|
|
1002
|
+
file: sourceFile.file,
|
|
1003
|
+
line,
|
|
1004
|
+
message: "A `//!` or `/*!` marker survives minification and ships to the browser."
|
|
1005
|
+
}));
|
|
1006
|
+
}
|
|
1007
|
+
function isClientReachableFile(file) {
|
|
1008
|
+
if (/\.(test|spec)\.tsx?$/.test(file))
|
|
1009
|
+
return false;
|
|
1010
|
+
if (/\.(constant|store)\.ts$/.test(file))
|
|
1011
|
+
return true;
|
|
1012
|
+
if (/\.(Template|Unit|Util|View|Zone)\.tsx$/.test(file))
|
|
1013
|
+
return true;
|
|
1014
|
+
const segments = file.split("/");
|
|
1015
|
+
if (segments.includes("ui") || segments.includes("webkit"))
|
|
1016
|
+
return /\.tsx?$/.test(file);
|
|
1017
|
+
if (segments.includes("common"))
|
|
1018
|
+
return file.endsWith(".ts");
|
|
1019
|
+
return segments.includes("page") && file.endsWith(".tsx");
|
|
911
1020
|
}
|
|
912
1021
|
function getPlaceholderExportWarnings(sourceFile) {
|
|
913
1022
|
if (!sourceFile.file.endsWith("/index.ts") && !sourceFile.file.endsWith("/index.tsx"))
|
|
@@ -937,7 +1046,7 @@ function getDictionaryTextWarnings(sourceFile) {
|
|
|
937
1046
|
function getGlobalMutationWarnings(sourceFile) {
|
|
938
1047
|
const warnings = [];
|
|
939
1048
|
for (const statement of sourceFile.sourceFile.statements) {
|
|
940
|
-
if (
|
|
1049
|
+
if (ts3.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
|
|
941
1050
|
warnings.push({
|
|
942
1051
|
rule: "akan.file.global-declaration",
|
|
943
1052
|
scope: "file",
|
|
@@ -947,7 +1056,7 @@ function getGlobalMutationWarnings(sourceFile) {
|
|
|
947
1056
|
message: "Global declarations require an explicit low-level integration allowlist."
|
|
948
1057
|
});
|
|
949
1058
|
}
|
|
950
|
-
if (
|
|
1059
|
+
if (ts3.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
|
|
951
1060
|
warnings.push({
|
|
952
1061
|
rule: "akan.file.window-augmentation",
|
|
953
1062
|
scope: "file",
|
|
@@ -957,7 +1066,7 @@ function getGlobalMutationWarnings(sourceFile) {
|
|
|
957
1066
|
message: "Window augmentation should be isolated to approved browser integration files."
|
|
958
1067
|
});
|
|
959
1068
|
}
|
|
960
|
-
if (
|
|
1069
|
+
if (ts3.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
|
|
961
1070
|
warnings.push({
|
|
962
1071
|
rule: "akan.file.prototype-mutation",
|
|
963
1072
|
scope: "file",
|
|
@@ -975,23 +1084,23 @@ function getTopLevelDeclarations(sourceFile) {
|
|
|
975
1084
|
}
|
|
976
1085
|
function getTopLevelDeclaration(sourceFile, statement) {
|
|
977
1086
|
const line = getLine(sourceFile, statement);
|
|
978
|
-
if (
|
|
1087
|
+
if (ts3.isClassDeclaration(statement) && statement.name) {
|
|
979
1088
|
return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
|
|
980
1089
|
}
|
|
981
|
-
if (
|
|
1090
|
+
if (ts3.isFunctionDeclaration(statement) && statement.name) {
|
|
982
1091
|
return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
|
|
983
1092
|
}
|
|
984
|
-
if (
|
|
1093
|
+
if (ts3.isInterfaceDeclaration(statement)) {
|
|
985
1094
|
return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
|
|
986
1095
|
}
|
|
987
|
-
if (
|
|
1096
|
+
if (ts3.isTypeAliasDeclaration(statement)) {
|
|
988
1097
|
return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
|
|
989
1098
|
}
|
|
990
|
-
if (
|
|
1099
|
+
if (ts3.isEnumDeclaration(statement)) {
|
|
991
1100
|
return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
|
|
992
1101
|
}
|
|
993
|
-
if (
|
|
994
|
-
return statement.declarationList.declarations.filter((declaration) =>
|
|
1102
|
+
if (ts3.isVariableStatement(statement)) {
|
|
1103
|
+
return statement.declarationList.declarations.filter((declaration) => ts3.isIdentifier(declaration.name)).map((declaration) => ({
|
|
995
1104
|
name: declaration.name.text,
|
|
996
1105
|
kind: "variable",
|
|
997
1106
|
line: getLine(sourceFile, declaration),
|
|
@@ -999,7 +1108,7 @@ function getTopLevelDeclaration(sourceFile, statement) {
|
|
|
999
1108
|
node: statement
|
|
1000
1109
|
}));
|
|
1001
1110
|
}
|
|
1002
|
-
if (
|
|
1111
|
+
if (ts3.isExportDeclaration(statement)) {
|
|
1003
1112
|
return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
|
|
1004
1113
|
}
|
|
1005
1114
|
return [];
|
|
@@ -1024,9 +1133,9 @@ function isAllowedConstantDeclaration(modelName, declaration) {
|
|
|
1024
1133
|
return false;
|
|
1025
1134
|
if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
|
|
1026
1135
|
return true;
|
|
1027
|
-
if (!
|
|
1136
|
+
if (!ts3.isClassDeclaration(declaration.node))
|
|
1028
1137
|
return false;
|
|
1029
|
-
const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token ===
|
|
1138
|
+
const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
|
|
1030
1139
|
const expression = heritageClause?.types[0]?.expression;
|
|
1031
1140
|
return !!expression && expression.getText().startsWith("enumOf(");
|
|
1032
1141
|
}
|
|
@@ -1043,7 +1152,19 @@ function getConventionDescription(suffix, modelName) {
|
|
|
1043
1152
|
return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
|
|
1044
1153
|
return `${modelName}Store`;
|
|
1045
1154
|
}
|
|
1046
|
-
|
|
1155
|
+
var sysRootTypes = { apps: "app", libs: "lib" };
|
|
1156
|
+
function getSysRootEntry(file) {
|
|
1157
|
+
const segments = file.split("/");
|
|
1158
|
+
const root = segments[0];
|
|
1159
|
+
const name = segments[2];
|
|
1160
|
+
if (!root || !name || segments.length < 3)
|
|
1161
|
+
return null;
|
|
1162
|
+
const type = sysRootTypes[root];
|
|
1163
|
+
if (!type)
|
|
1164
|
+
return null;
|
|
1165
|
+
return { type, name, isDir: segments.length > 3 };
|
|
1166
|
+
}
|
|
1167
|
+
function getLibFacetRootFile(file) {
|
|
1047
1168
|
const segments = file.split("/");
|
|
1048
1169
|
if ((segments[0] === "libs" || segments[0] === "apps") && segments.length === 4 && segments[2] === "lib")
|
|
1049
1170
|
return segments[3];
|
|
@@ -1092,13 +1213,13 @@ function getModuleInfo(file) {
|
|
|
1092
1213
|
return { moduleName, fileName, kind: "database" };
|
|
1093
1214
|
}
|
|
1094
1215
|
function isExportedConst(declaration) {
|
|
1095
|
-
return declaration.exported &&
|
|
1216
|
+
return declaration.exported && ts3.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts3.NodeFlags.Const) !== 0;
|
|
1096
1217
|
}
|
|
1097
1218
|
function isExported(node) {
|
|
1098
|
-
return !!
|
|
1219
|
+
return !!ts3.getCombinedModifierFlags(node) && !!(ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Export);
|
|
1099
1220
|
}
|
|
1100
1221
|
function isFunctionLikeInitializer(node) {
|
|
1101
|
-
return !!node && (
|
|
1222
|
+
return !!node && (ts3.isArrowFunction(node) || ts3.isFunctionExpression(node));
|
|
1102
1223
|
}
|
|
1103
1224
|
function getBodyFingerprint(sourceFile, node) {
|
|
1104
1225
|
if (!node)
|
|
@@ -1120,7 +1241,7 @@ async function isDirectory(absolutePath) {
|
|
|
1120
1241
|
}
|
|
1121
1242
|
}
|
|
1122
1243
|
function getScriptKind(file) {
|
|
1123
|
-
return file.endsWith(".tsx") ?
|
|
1244
|
+
return file.endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS;
|
|
1124
1245
|
}
|
|
1125
1246
|
function getLine(sourceFile, node) {
|
|
1126
1247
|
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|