@akanjs/cli 3.0.0-alpha.8 → 3.0.0-alpha.80
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-pwc6d6yk.js} +3 -5
- package/{agent.command-pggr92jj.js → agent.command-180hh0ye.js} +11 -12
- package/{application.command-692yap1d.js → application.command-gmcgen9y.js} +59 -35
- package/{applicationBuildRunner-ha3y8ew8.js → applicationBuildRunner-tavpx3fv.js} +22 -20
- package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-ktjt0erc.js} +8 -9
- package/buildBatch.proc.js +12 -13
- package/capacitorApp-kwa67kzs.js +12 -0
- package/{cloud.command-wqfsyzv2.js → cloud.command-gpq7qxrc.js} +15 -17
- package/commandManifest.json +1 -1
- package/{context.command-9bxtdn0w.js → context.command-dk2m2sjx.js} +19 -22
- package/dependencyScanner-c64m6fxp.js +8 -0
- package/fleet.command-3bkyyd3a.js +836 -0
- package/getDirname-dr3jz1xg.js +7 -0
- package/{guideline.command-nfqahxex.js → guideline.command-w6ddv13r.js} +16 -17
- package/guidelines/agentRule/agentRule.generate.json +31 -0
- package/guidelines/agentRule/agentRule.instruction.md +525 -0
- package/guidelines/conventions/conventions.generate.json +25 -0
- package/guidelines/conventions/conventions.instruction.md +847 -0
- 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/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 +176 -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.generate.json +24 -0
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +263 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +402 -0
- package/incrementalBuilder.proc.js +21 -22
- package/{index-4hek879p.js → index-1kzsn94d.js} +1 -1
- package/{index-p3f4q532.js → index-3c9n3nt8.js} +31 -30
- package/index-3d9y066c.js +76 -0
- package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
- package/{index-gex7c8kd.js → index-5n0hf8r9.js} +65 -38
- package/{index-mssqn2sq.js → index-5y7qba1c.js} +79 -33
- package/index-6wwzs1bc.js +7 -0
- package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
- package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
- package/{index-azv0xjb5.js → index-a8h184wr.js} +15 -11
- package/{index-67546d0j.js → index-ayy5ecax.js} +1 -1
- package/{index-xhgtkekh.js → index-b2nt0w0k.js} +27 -27
- package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
- package/{index-bz2sfzzt.js → index-cd5x18qh.js} +32 -32
- package/{index-bjpxzr6s.js → index-ctphds8v.js} +1 -1
- package/{index-w6wbb612.js → index-dq6wq1tv.js} +94 -61
- package/{index-js368gk9.js → index-ee8z8adx.js} +13 -13
- package/{index-5dw57f0d.js → index-f7wewj2v.js} +155 -76
- package/{index-6rhabeev.js → index-ft1sfjz0.js} +36 -25
- package/{index-7myvn0f0.js → index-h7yw2z9n.js} +7 -7
- package/{index-5pmx7bkg.js → index-j9vnq8tr.js} +18 -18
- package/{index-vg0sj9nc.js → index-k1n0h7dk.js} +840 -201
- package/{index-vtj3zps1.js → index-k86zc21w.js} +57 -57
- package/{index-46tjzh6s.js → index-mfm35dhh.js} +29 -29
- package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
- package/{index-ztpw544k.js → index-qcracvc9.js} +8 -8
- package/{index-2c3e2xh8.js → index-saheq2bs.js} +4 -4
- package/{index-gmy45vz2.js → index-v0s24t8j.js} +14 -14
- package/{index-0cj2zxbm.js → index-wet2ytnr.js} +12 -11
- package/{index-j9cxndc0.js → index-x23688ar.js} +12 -7
- package/index-xnhf8tr9.js +109 -0
- package/{index-a1e0j63x.js → index-xxe90kqy.js} +58 -25
- package/{index-qb61h0k7.js → index-z3c9mmw4.js} +1098 -1360
- package/index.js +22 -24
- package/{library.command-md9715jz.js → library.command-wpedct6c.js} +11 -9
- package/{localRegistry.command-exkbamyj.js → localRegistry.command-pjdxa01g.js} +20 -22
- package/{module.command-w5aaddxk.js → module.command-8n4kct3e.js} +13 -15
- package/{package.command-yfkrcy0z.js → package.command-fcbd0b1d.js} +8 -9
- package/package.json +5 -7
- package/{page.command-echbavp4.js → page.command-x4379grq.js} +7 -8
- package/{primitive.command-9fbhspw2.js → primitive.command-8etmnqhq.js} +13 -15
- package/{quality.command-3pnt9yep.js → quality.command-hfx7z8yr.js} +226 -104
- package/recipeScanner-fpagtgc1.js +11 -0
- package/{repair.command-carj4vax.js → repair.command-j0fs5qkz.js} +12 -13
- package/{routeSourceValidator-e4jmbfcx.js → routeSourceValidator-ze6x894c.js} +0 -2
- package/{scalar.command-51ryzypc.js → scalar.command-2emk89eh.js} +11 -13
- 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 +6 -6
- package/templates/crudPages/[__model__Id]/page.tsx +10 -13
- package/templates/crudPages/new/page.tsx +6 -6
- 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__.Util.tsx +1 -1
- package/templates/module/__Model__.Zone.tsx +3 -5
- 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/AGENTS.md.template +4 -972
- package/templates/workspaceRoot/CLAUDE.md.template +19 -0
- package/templates/workspaceRoot/biome.json.template +4 -244
- 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-91wmvm8k.js → workflow.command-cvpmzwjg.js} +21 -23
- package/{workspace.command-wa10mfd9.js → workspace.command-mbb5s3wx.js} +108 -91
- package/capacitorApp-p0z5yab2.js +0 -60
- package/dependencyScanner-f6grrep3.js +0 -9
- package/index-mxvakhsm.js +0 -66
- package/index-r24hmh0q.js +0 -4
- package/recipeScanner-tghkzrrt.js +0 -12
- package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
- package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -34
- package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
- package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
- package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
- package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
- package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
- package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
- package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
- package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -19
- package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -62
- package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
- package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
- package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
- package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -99
- package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
- package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
- package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
- package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
- package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
- 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,20 +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-ft1sfjz0.js";
|
|
8
|
+
import {
|
|
9
|
+
rootAllowedFiles,
|
|
10
|
+
rootAllowedDirs,
|
|
11
|
+
isAllowedLibFacetRootFile
|
|
12
|
+
} from "./index-k1n0h7dk.js";
|
|
13
|
+
import"./index-ayy5ecax.js";
|
|
14
|
+
import"./index-6wwzs1bc.js";
|
|
11
15
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "./index-vg0sj9nc.js";
|
|
15
|
-
import"./index-mxvakhsm.js";
|
|
16
|
-
import"./index-67546d0j.js";
|
|
17
|
-
import"./index-r24hmh0q.js";
|
|
16
|
+
AbstractDoc
|
|
17
|
+
} from "./index-meh558jy.js";
|
|
18
18
|
|
|
19
19
|
// pkgs/@akanjs/devkit/qualityScanner.ts
|
|
20
20
|
import { createHash } from "crypto";
|
|
@@ -22,10 +22,79 @@ import { readdir, readFile, stat } from "fs/promises";
|
|
|
22
22
|
import path from "path";
|
|
23
23
|
import { RESERVED_ROUTE_CONFIG_EXPORTS } from "akanjs/common";
|
|
24
24
|
import ignore from "ignore";
|
|
25
|
-
import
|
|
25
|
+
import ts3 from "typescript";
|
|
26
26
|
|
|
27
|
-
// pkgs/@akanjs/devkit/
|
|
27
|
+
// pkgs/@akanjs/devkit/formSetterScanner.ts
|
|
28
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";
|
|
29
98
|
var STATIC_COMPONENT_MIN_MASS = 4;
|
|
30
99
|
var MIXED_COMPONENT_MIN_MASS = 10;
|
|
31
100
|
var MIXED_COMPONENT_MAX_TOUCHES = 2;
|
|
@@ -143,31 +212,31 @@ class SsrScanner {
|
|
|
143
212
|
});
|
|
144
213
|
}
|
|
145
214
|
}
|
|
146
|
-
|
|
215
|
+
ts2.forEachChild(node, visit);
|
|
147
216
|
};
|
|
148
|
-
|
|
217
|
+
ts2.forEachChild(sourceFile.sourceFile, visit);
|
|
149
218
|
return warnings;
|
|
150
219
|
}
|
|
151
220
|
#isMountEffect(sourceFile, node) {
|
|
152
|
-
if (!
|
|
221
|
+
if (!ts2.isCallExpression(node))
|
|
153
222
|
return false;
|
|
154
223
|
const callee = node.expression.getText(sourceFile);
|
|
155
224
|
if (callee !== "useEffect" && callee !== "useLayoutEffect")
|
|
156
225
|
return false;
|
|
157
226
|
const deps = node.arguments[1];
|
|
158
|
-
return !!deps &&
|
|
227
|
+
return !!deps && ts2.isArrayLiteralExpression(deps) && deps.elements.length === 0;
|
|
159
228
|
}
|
|
160
229
|
#getLoadCalls(sourceFile, node) {
|
|
161
230
|
const calls = [];
|
|
162
231
|
const visit = (child) => {
|
|
163
|
-
if (
|
|
232
|
+
if (ts2.isCallExpression(child)) {
|
|
164
233
|
const callee = child.expression.getText(sourceFile);
|
|
165
234
|
if (/^fetch\.[a-z]/.test(callee) || /^st\.do\.(init|get|view|load|list|count|insight)[A-Z]/.test(callee))
|
|
166
235
|
calls.push({ callee, node: child });
|
|
167
236
|
}
|
|
168
|
-
|
|
237
|
+
ts2.forEachChild(child, visit);
|
|
169
238
|
};
|
|
170
|
-
|
|
239
|
+
ts2.forEachChild(node, visit);
|
|
171
240
|
return calls;
|
|
172
241
|
}
|
|
173
242
|
#getTemplateStateWarnings(sourceFile) {
|
|
@@ -175,7 +244,7 @@ class SsrScanner {
|
|
|
175
244
|
return [];
|
|
176
245
|
const warnings = [];
|
|
177
246
|
const visit = (node) => {
|
|
178
|
-
if (
|
|
247
|
+
if (ts2.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
|
|
179
248
|
warnings.push({
|
|
180
249
|
rule: "akan.ssr.template-client-state",
|
|
181
250
|
scope: "ssr",
|
|
@@ -185,9 +254,9 @@ class SsrScanner {
|
|
|
185
254
|
message: "Template holds form state in useState. Templates are store-driven and carry no local state."
|
|
186
255
|
});
|
|
187
256
|
}
|
|
188
|
-
|
|
257
|
+
ts2.forEachChild(node, visit);
|
|
189
258
|
};
|
|
190
|
-
|
|
259
|
+
ts2.forEachChild(sourceFile.sourceFile, visit);
|
|
191
260
|
return warnings;
|
|
192
261
|
}
|
|
193
262
|
#measureBalance(sourceFiles) {
|
|
@@ -228,14 +297,14 @@ class SsrScanner {
|
|
|
228
297
|
return components;
|
|
229
298
|
}
|
|
230
299
|
#getComponentNodes(statement) {
|
|
231
|
-
if (
|
|
300
|
+
if (ts2.isFunctionDeclaration(statement) && statement.body)
|
|
232
301
|
return [{ name: statement.name?.text ?? "default", node: statement.body }];
|
|
233
|
-
if (!
|
|
302
|
+
if (!ts2.isVariableStatement(statement))
|
|
234
303
|
return [];
|
|
235
304
|
return statement.declarationList.declarations.flatMap((declaration) => {
|
|
236
|
-
if (!
|
|
305
|
+
if (!ts2.isIdentifier(declaration.name) || !declaration.initializer)
|
|
237
306
|
return [];
|
|
238
|
-
if (!
|
|
307
|
+
if (!ts2.isArrowFunction(declaration.initializer) && !ts2.isFunctionExpression(declaration.initializer))
|
|
239
308
|
return [];
|
|
240
309
|
return [{ name: declaration.name.text, node: declaration.initializer }];
|
|
241
310
|
});
|
|
@@ -243,7 +312,7 @@ class SsrScanner {
|
|
|
243
312
|
#getTouches(sourceFile, node) {
|
|
244
313
|
const touches = [];
|
|
245
314
|
const visit = (child) => {
|
|
246
|
-
if (
|
|
315
|
+
if (ts2.isCallExpression(child)) {
|
|
247
316
|
const callee = child.expression.getText(sourceFile);
|
|
248
317
|
const bareName = callee.split(".").pop() ?? callee;
|
|
249
318
|
if (callee === "createContext" || callee === "lazy")
|
|
@@ -251,60 +320,60 @@ class SsrScanner {
|
|
|
251
320
|
else if (/^use[A-Z]/.test(bareName) && !SsrScanner.#serverSafeCalls.has(bareName))
|
|
252
321
|
touches.push(bareName);
|
|
253
322
|
}
|
|
254
|
-
if (
|
|
323
|
+
if (ts2.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
|
|
255
324
|
touches.push(child.name.getText(sourceFile));
|
|
256
|
-
if (
|
|
325
|
+
if (ts2.isPropertyAccessExpression(child)) {
|
|
257
326
|
const root = getAccessRoot(child);
|
|
258
327
|
if (root === "st")
|
|
259
328
|
touches.push("st");
|
|
260
329
|
else if (SsrScanner.#clientGlobals.has(root))
|
|
261
330
|
touches.push(root);
|
|
262
331
|
}
|
|
263
|
-
|
|
332
|
+
ts2.forEachChild(child, visit);
|
|
264
333
|
};
|
|
265
|
-
|
|
334
|
+
ts2.forEachChild(node, visit);
|
|
266
335
|
return touches;
|
|
267
336
|
}
|
|
268
337
|
#getTagNames(sourceFile, node) {
|
|
269
338
|
const tags = new Set;
|
|
270
339
|
const visit = (child) => {
|
|
271
|
-
if (
|
|
340
|
+
if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
|
|
272
341
|
tags.add(child.tagName.getText(sourceFile).split(".")[0]);
|
|
273
|
-
|
|
342
|
+
ts2.forEachChild(child, visit);
|
|
274
343
|
};
|
|
275
|
-
|
|
344
|
+
ts2.forEachChild(node, visit);
|
|
276
345
|
return tags;
|
|
277
346
|
}
|
|
278
347
|
#getMass(node) {
|
|
279
348
|
let mass = 0;
|
|
280
349
|
const visit = (child) => {
|
|
281
|
-
if (
|
|
350
|
+
if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
|
|
282
351
|
mass += 1;
|
|
283
|
-
|
|
352
|
+
ts2.forEachChild(child, visit);
|
|
284
353
|
};
|
|
285
|
-
|
|
354
|
+
ts2.forEachChild(node, visit);
|
|
286
355
|
return mass;
|
|
287
356
|
}
|
|
288
357
|
#hasUseClient(sourceFile) {
|
|
289
358
|
const first = sourceFile.statements[0];
|
|
290
|
-
if (!first || !
|
|
359
|
+
if (!first || !ts2.isExpressionStatement(first) || !ts2.isStringLiteral(first.expression))
|
|
291
360
|
return false;
|
|
292
361
|
return first.expression.text === "use client";
|
|
293
362
|
}
|
|
294
363
|
#hasVendorImport(sourceFile) {
|
|
295
|
-
return sourceFile.statements.some((statement) =>
|
|
364
|
+
return sourceFile.statements.some((statement) => ts2.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
|
|
296
365
|
}
|
|
297
366
|
#getVendorNames(sourceFile) {
|
|
298
367
|
const names = new Set;
|
|
299
368
|
for (const statement of sourceFile.statements) {
|
|
300
|
-
if (!
|
|
369
|
+
if (!ts2.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
|
|
301
370
|
continue;
|
|
302
371
|
const clause = statement.importClause;
|
|
303
372
|
if (clause?.name)
|
|
304
373
|
names.add(clause.name.text);
|
|
305
|
-
if (clause?.namedBindings &&
|
|
374
|
+
if (clause?.namedBindings && ts2.isNamespaceImport(clause.namedBindings))
|
|
306
375
|
names.add(clause.namedBindings.name.text);
|
|
307
|
-
if (clause?.namedBindings &&
|
|
376
|
+
if (clause?.namedBindings && ts2.isNamedImports(clause.namedBindings))
|
|
308
377
|
for (const element of clause.namedBindings.elements)
|
|
309
378
|
names.add(element.name.text);
|
|
310
379
|
}
|
|
@@ -312,10 +381,10 @@ class SsrScanner {
|
|
|
312
381
|
}
|
|
313
382
|
#importsClientRuntime(sourceFile) {
|
|
314
383
|
for (const statement of sourceFile.statements) {
|
|
315
|
-
if (!
|
|
384
|
+
if (!ts2.isImportDeclaration(statement))
|
|
316
385
|
continue;
|
|
317
386
|
const bindings = statement.importClause?.namedBindings;
|
|
318
|
-
if (!bindings || !
|
|
387
|
+
if (!bindings || !ts2.isNamedImports(bindings))
|
|
319
388
|
continue;
|
|
320
389
|
if (bindings.elements.some((element) => SsrScanner.#clientRuntimeImports.has(element.name.text)))
|
|
321
390
|
return true;
|
|
@@ -366,7 +435,7 @@ function getShare(serverMass, clientMass) {
|
|
|
366
435
|
return total === 0 ? 1 : serverMass / total;
|
|
367
436
|
}
|
|
368
437
|
function getSpecifier(statement) {
|
|
369
|
-
return
|
|
438
|
+
return ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
|
|
370
439
|
}
|
|
371
440
|
function isVendorSpecifier(specifier) {
|
|
372
441
|
if (specifier === "" || specifier.startsWith(".") || specifier.startsWith("/"))
|
|
@@ -379,9 +448,9 @@ function isVendorSpecifier(specifier) {
|
|
|
379
448
|
}
|
|
380
449
|
function getAccessRoot(node) {
|
|
381
450
|
let current = node;
|
|
382
|
-
while (
|
|
451
|
+
while (ts2.isPropertyAccessExpression(current))
|
|
383
452
|
current = current.expression;
|
|
384
|
-
return
|
|
453
|
+
return ts2.isIdentifier(current) ? current.text : "";
|
|
385
454
|
}
|
|
386
455
|
|
|
387
456
|
// pkgs/@akanjs/devkit/qualityScanner.ts
|
|
@@ -405,7 +474,8 @@ var SUGGESTED_RULES = [
|
|
|
405
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.",
|
|
406
475
|
"Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
|
|
407
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.",
|
|
408
|
-
"
|
|
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.",
|
|
409
479
|
"Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
|
|
410
480
|
];
|
|
411
481
|
var CONVENTION_SUFFIXES = [
|
|
@@ -423,15 +493,20 @@ var RULE_FIXES = {
|
|
|
423
493
|
"akan.file.max-lines": "Break the file into smaller focused modules; keep one primary responsibility per file.",
|
|
424
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.",
|
|
425
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.",
|
|
426
497
|
"akan.file.dictionary-stale-text": "Replace the scaffold text with real localized copy for this dictionary entry.",
|
|
427
498
|
"akan.file.global-declaration": "Move the global declaration into an approved low-level integration file (e.g. webkit) and keep it isolated.",
|
|
428
499
|
"akan.file.window-augmentation": "Move the Window augmentation into an approved browser integration file and keep it isolated.",
|
|
429
500
|
"akan.file.prototype-mutation": "Avoid prototype mutation, or isolate it in an approved low-level integration file.",
|
|
430
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.",
|
|
431
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`.",
|
|
432
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.",
|
|
433
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).",
|
|
434
|
-
"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.",
|
|
435
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.",
|
|
436
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.',
|
|
437
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.",
|
|
@@ -459,6 +534,7 @@ class AkanQualityScanner {
|
|
|
459
534
|
...sourceFiles.flatMap((sourceFile) => this.#scanConventionQuality(sourceFile)),
|
|
460
535
|
...sourceFiles.flatMap((sourceFile) => this.#scanLayoutQuality(sourceFile)),
|
|
461
536
|
...abstractFiles.flatMap((abstractFile) => this.#scanAbstractQuality(abstractFile)),
|
|
537
|
+
...new FormSetterScanner().scan(sourceFiles),
|
|
462
538
|
...ssr.warnings
|
|
463
539
|
];
|
|
464
540
|
return {
|
|
@@ -511,7 +587,7 @@ class AkanQualityScanner {
|
|
|
511
587
|
file,
|
|
512
588
|
absolutePath,
|
|
513
589
|
content,
|
|
514
|
-
sourceFile:
|
|
590
|
+
sourceFile: ts3.createSourceFile(file, content, ts3.ScriptTarget.Latest, true, getScriptKind(file))
|
|
515
591
|
};
|
|
516
592
|
}
|
|
517
593
|
async#readTextFile(workspaceRoot, file) {
|
|
@@ -569,6 +645,7 @@ class AkanQualityScanner {
|
|
|
569
645
|
message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
|
|
570
646
|
});
|
|
571
647
|
}
|
|
648
|
+
warnings.push(...getBangCommentWarnings(sourceFile));
|
|
572
649
|
warnings.push(...getPlaceholderExportWarnings(sourceFile));
|
|
573
650
|
warnings.push(...getDictionaryTextWarnings(sourceFile));
|
|
574
651
|
warnings.push(...getGlobalMutationWarnings(sourceFile));
|
|
@@ -670,25 +747,30 @@ class AkanQualityScanner {
|
|
|
670
747
|
];
|
|
671
748
|
}
|
|
672
749
|
#scanLayoutQuality(sourceFile) {
|
|
673
|
-
const segments = sourceFile.file.split("/");
|
|
674
750
|
const warnings = [];
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
+
}
|
|
683
765
|
}
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
766
|
+
const libFacetFile = getLibFacetRootFile(sourceFile.file);
|
|
767
|
+
if (libFacetFile && !isAllowedLibFacetRootFile(libFacetFile)) {
|
|
686
768
|
warnings.push({
|
|
687
|
-
rule: "akan.layout.lib-
|
|
769
|
+
rule: "akan.layout.lib-facet-file",
|
|
688
770
|
scope: "layout",
|
|
689
771
|
severity: "warning",
|
|
690
772
|
file: sourceFile.file,
|
|
691
|
-
message: `Unexpected lib root file "${
|
|
773
|
+
message: `Unexpected lib facet root file "${libFacetFile}". Keep direct lib/ root files limited to generated support facets.`
|
|
692
774
|
});
|
|
693
775
|
}
|
|
694
776
|
const moduleUiWarning = getModuleUiWarning(sourceFile.file);
|
|
@@ -758,7 +840,7 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
758
840
|
const declarations = [];
|
|
759
841
|
const nameExempt = isPageRouteFile(sourceFile.file) || isUiComponentFile(sourceFile.file);
|
|
760
842
|
for (const statement of sourceFile.sourceFile.statements) {
|
|
761
|
-
if (
|
|
843
|
+
if (ts3.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
|
|
762
844
|
declarations.push({
|
|
763
845
|
name: statement.name.text,
|
|
764
846
|
kind: "function",
|
|
@@ -768,7 +850,7 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
768
850
|
duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, false)
|
|
769
851
|
});
|
|
770
852
|
}
|
|
771
|
-
if (
|
|
853
|
+
if (ts3.isClassDeclaration(statement) && statement.name && isExported(statement)) {
|
|
772
854
|
declarations.push({
|
|
773
855
|
name: statement.name.text,
|
|
774
856
|
kind: "class",
|
|
@@ -778,9 +860,9 @@ function getExportedFunctionLikes(sourceFile) {
|
|
|
778
860
|
duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, isEnumClassStatement(sourceFile.sourceFile, statement))
|
|
779
861
|
});
|
|
780
862
|
}
|
|
781
|
-
if (
|
|
863
|
+
if (ts3.isVariableStatement(statement) && isExported(statement)) {
|
|
782
864
|
for (const declaration of statement.declarationList.declarations) {
|
|
783
|
-
if (!
|
|
865
|
+
if (!ts3.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
|
|
784
866
|
continue;
|
|
785
867
|
declarations.push({
|
|
786
868
|
name: declaration.name.text,
|
|
@@ -819,14 +901,14 @@ function isInLibModule(file) {
|
|
|
819
901
|
return (segments[0] === "apps" || segments[0] === "libs") && segments.includes("lib");
|
|
820
902
|
}
|
|
821
903
|
function isEnumClassStatement(sourceFile, statement) {
|
|
822
|
-
if (!
|
|
904
|
+
if (!ts3.isClassDeclaration(statement))
|
|
823
905
|
return false;
|
|
824
|
-
const heritageClause = statement.heritageClauses?.find((clause) => clause.token ===
|
|
906
|
+
const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
|
|
825
907
|
const expression = heritageClause?.types[0]?.expression;
|
|
826
908
|
return !!expression && expression.getText(sourceFile).startsWith("enumOf(");
|
|
827
909
|
}
|
|
828
910
|
function getExportedClassNames(sourceFile) {
|
|
829
|
-
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);
|
|
830
912
|
}
|
|
831
913
|
function isComponentDeclarationFile(file) {
|
|
832
914
|
if (!file.endsWith(".tsx"))
|
|
@@ -842,7 +924,7 @@ function isComponentDeclarationFile(file) {
|
|
|
842
924
|
function getComponentFileDeclarations(sourceFile) {
|
|
843
925
|
const reExportedNames = new Set;
|
|
844
926
|
for (const statement of sourceFile.statements) {
|
|
845
|
-
if (
|
|
927
|
+
if (ts3.isExportDeclaration(statement) && statement.exportClause && ts3.isNamedExports(statement.exportClause)) {
|
|
846
928
|
for (const element of statement.exportClause.elements)
|
|
847
929
|
reExportedNames.add((element.propertyName ?? element.name).text);
|
|
848
930
|
}
|
|
@@ -852,19 +934,19 @@ function getComponentFileDeclarations(sourceFile) {
|
|
|
852
934
|
const isDefaultExport = isDefaultExportStatement(statement);
|
|
853
935
|
const inlineExported = isExported(statement);
|
|
854
936
|
const add = (name, kind, line) => declarations.push({ name, kind, line, exported: inlineExported || reExportedNames.has(name), isDefaultExport });
|
|
855
|
-
if (
|
|
937
|
+
if (ts3.isInterfaceDeclaration(statement))
|
|
856
938
|
add(statement.name.text, "interface", getLine(sourceFile, statement));
|
|
857
|
-
else if (
|
|
939
|
+
else if (ts3.isTypeAliasDeclaration(statement))
|
|
858
940
|
add(statement.name.text, "type", getLine(sourceFile, statement));
|
|
859
|
-
else if (
|
|
941
|
+
else if (ts3.isEnumDeclaration(statement))
|
|
860
942
|
add(statement.name.text, "enum", getLine(sourceFile, statement));
|
|
861
|
-
else if (
|
|
943
|
+
else if (ts3.isFunctionDeclaration(statement) && statement.name)
|
|
862
944
|
add(statement.name.text, "function", getLine(sourceFile, statement));
|
|
863
|
-
else if (
|
|
945
|
+
else if (ts3.isClassDeclaration(statement) && statement.name)
|
|
864
946
|
add(statement.name.text, "class", getLine(sourceFile, statement));
|
|
865
|
-
else if (
|
|
947
|
+
else if (ts3.isVariableStatement(statement)) {
|
|
866
948
|
for (const declaration of statement.declarationList.declarations) {
|
|
867
|
-
if (!
|
|
949
|
+
if (!ts3.isIdentifier(declaration.name))
|
|
868
950
|
continue;
|
|
869
951
|
const kind = isFunctionLikeInitializer(declaration.initializer) ? "function" : "variable";
|
|
870
952
|
add(declaration.name.text, kind, getLine(sourceFile, declaration));
|
|
@@ -876,15 +958,15 @@ function getComponentFileDeclarations(sourceFile) {
|
|
|
876
958
|
function getCompoundComponentNames(sourceFile) {
|
|
877
959
|
const names = new Set;
|
|
878
960
|
for (const statement of sourceFile.statements) {
|
|
879
|
-
if (!
|
|
961
|
+
if (!ts3.isExpressionStatement(statement))
|
|
880
962
|
continue;
|
|
881
963
|
const { expression } = statement;
|
|
882
|
-
if (!
|
|
964
|
+
if (!ts3.isBinaryExpression(expression) || expression.operatorToken.kind !== ts3.SyntaxKind.EqualsToken)
|
|
883
965
|
continue;
|
|
884
|
-
if (!
|
|
966
|
+
if (!ts3.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
|
|
885
967
|
continue;
|
|
886
968
|
names.add(expression.left.name.text);
|
|
887
|
-
if (
|
|
969
|
+
if (ts3.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
|
|
888
970
|
names.add(expression.right.text);
|
|
889
971
|
}
|
|
890
972
|
return names;
|
|
@@ -904,9 +986,37 @@ function isPascalCaseName(name) {
|
|
|
904
986
|
return /^[A-Z]/.test(name) && !/^[A-Z0-9_]+$/.test(name);
|
|
905
987
|
}
|
|
906
988
|
function isDefaultExportStatement(statement) {
|
|
907
|
-
if (
|
|
989
|
+
if (ts3.isExportAssignment(statement))
|
|
908
990
|
return !statement.isExportEquals;
|
|
909
|
-
|
|
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");
|
|
910
1020
|
}
|
|
911
1021
|
function getPlaceholderExportWarnings(sourceFile) {
|
|
912
1022
|
if (!sourceFile.file.endsWith("/index.ts") && !sourceFile.file.endsWith("/index.tsx"))
|
|
@@ -936,7 +1046,7 @@ function getDictionaryTextWarnings(sourceFile) {
|
|
|
936
1046
|
function getGlobalMutationWarnings(sourceFile) {
|
|
937
1047
|
const warnings = [];
|
|
938
1048
|
for (const statement of sourceFile.sourceFile.statements) {
|
|
939
|
-
if (
|
|
1049
|
+
if (ts3.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
|
|
940
1050
|
warnings.push({
|
|
941
1051
|
rule: "akan.file.global-declaration",
|
|
942
1052
|
scope: "file",
|
|
@@ -946,7 +1056,7 @@ function getGlobalMutationWarnings(sourceFile) {
|
|
|
946
1056
|
message: "Global declarations require an explicit low-level integration allowlist."
|
|
947
1057
|
});
|
|
948
1058
|
}
|
|
949
|
-
if (
|
|
1059
|
+
if (ts3.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
|
|
950
1060
|
warnings.push({
|
|
951
1061
|
rule: "akan.file.window-augmentation",
|
|
952
1062
|
scope: "file",
|
|
@@ -956,7 +1066,7 @@ function getGlobalMutationWarnings(sourceFile) {
|
|
|
956
1066
|
message: "Window augmentation should be isolated to approved browser integration files."
|
|
957
1067
|
});
|
|
958
1068
|
}
|
|
959
|
-
if (
|
|
1069
|
+
if (ts3.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
|
|
960
1070
|
warnings.push({
|
|
961
1071
|
rule: "akan.file.prototype-mutation",
|
|
962
1072
|
scope: "file",
|
|
@@ -974,23 +1084,23 @@ function getTopLevelDeclarations(sourceFile) {
|
|
|
974
1084
|
}
|
|
975
1085
|
function getTopLevelDeclaration(sourceFile, statement) {
|
|
976
1086
|
const line = getLine(sourceFile, statement);
|
|
977
|
-
if (
|
|
1087
|
+
if (ts3.isClassDeclaration(statement) && statement.name) {
|
|
978
1088
|
return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
|
|
979
1089
|
}
|
|
980
|
-
if (
|
|
1090
|
+
if (ts3.isFunctionDeclaration(statement) && statement.name) {
|
|
981
1091
|
return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
|
|
982
1092
|
}
|
|
983
|
-
if (
|
|
1093
|
+
if (ts3.isInterfaceDeclaration(statement)) {
|
|
984
1094
|
return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
|
|
985
1095
|
}
|
|
986
|
-
if (
|
|
1096
|
+
if (ts3.isTypeAliasDeclaration(statement)) {
|
|
987
1097
|
return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
|
|
988
1098
|
}
|
|
989
|
-
if (
|
|
1099
|
+
if (ts3.isEnumDeclaration(statement)) {
|
|
990
1100
|
return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
|
|
991
1101
|
}
|
|
992
|
-
if (
|
|
993
|
-
return statement.declarationList.declarations.filter((declaration) =>
|
|
1102
|
+
if (ts3.isVariableStatement(statement)) {
|
|
1103
|
+
return statement.declarationList.declarations.filter((declaration) => ts3.isIdentifier(declaration.name)).map((declaration) => ({
|
|
994
1104
|
name: declaration.name.text,
|
|
995
1105
|
kind: "variable",
|
|
996
1106
|
line: getLine(sourceFile, declaration),
|
|
@@ -998,7 +1108,7 @@ function getTopLevelDeclaration(sourceFile, statement) {
|
|
|
998
1108
|
node: statement
|
|
999
1109
|
}));
|
|
1000
1110
|
}
|
|
1001
|
-
if (
|
|
1111
|
+
if (ts3.isExportDeclaration(statement)) {
|
|
1002
1112
|
return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
|
|
1003
1113
|
}
|
|
1004
1114
|
return [];
|
|
@@ -1023,9 +1133,9 @@ function isAllowedConstantDeclaration(modelName, declaration) {
|
|
|
1023
1133
|
return false;
|
|
1024
1134
|
if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
|
|
1025
1135
|
return true;
|
|
1026
|
-
if (!
|
|
1136
|
+
if (!ts3.isClassDeclaration(declaration.node))
|
|
1027
1137
|
return false;
|
|
1028
|
-
const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token ===
|
|
1138
|
+
const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
|
|
1029
1139
|
const expression = heritageClause?.types[0]?.expression;
|
|
1030
1140
|
return !!expression && expression.getText().startsWith("enumOf(");
|
|
1031
1141
|
}
|
|
@@ -1042,7 +1152,19 @@ function getConventionDescription(suffix, modelName) {
|
|
|
1042
1152
|
return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
|
|
1043
1153
|
return `${modelName}Store`;
|
|
1044
1154
|
}
|
|
1045
|
-
|
|
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) {
|
|
1046
1168
|
const segments = file.split("/");
|
|
1047
1169
|
if ((segments[0] === "libs" || segments[0] === "apps") && segments.length === 4 && segments[2] === "lib")
|
|
1048
1170
|
return segments[3];
|
|
@@ -1091,13 +1213,13 @@ function getModuleInfo(file) {
|
|
|
1091
1213
|
return { moduleName, fileName, kind: "database" };
|
|
1092
1214
|
}
|
|
1093
1215
|
function isExportedConst(declaration) {
|
|
1094
|
-
return declaration.exported &&
|
|
1216
|
+
return declaration.exported && ts3.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts3.NodeFlags.Const) !== 0;
|
|
1095
1217
|
}
|
|
1096
1218
|
function isExported(node) {
|
|
1097
|
-
return !!
|
|
1219
|
+
return !!ts3.getCombinedModifierFlags(node) && !!(ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Export);
|
|
1098
1220
|
}
|
|
1099
1221
|
function isFunctionLikeInitializer(node) {
|
|
1100
|
-
return !!node && (
|
|
1222
|
+
return !!node && (ts3.isArrowFunction(node) || ts3.isFunctionExpression(node));
|
|
1101
1223
|
}
|
|
1102
1224
|
function getBodyFingerprint(sourceFile, node) {
|
|
1103
1225
|
if (!node)
|
|
@@ -1119,7 +1241,7 @@ async function isDirectory(absolutePath) {
|
|
|
1119
1241
|
}
|
|
1120
1242
|
}
|
|
1121
1243
|
function getScriptKind(file) {
|
|
1122
|
-
return file.endsWith(".tsx") ?
|
|
1244
|
+
return file.endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS;
|
|
1123
1245
|
}
|
|
1124
1246
|
function getLine(sourceFile, node) {
|
|
1125
1247
|
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|