@geonosis/oxlint-plugin-biological-architecture 3.1.0 → 3.3.0
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/CHANGELOG.md +57 -0
- package/README.md +75 -6
- package/RULES.md +202 -101
- package/corpus/.oxlintrc.json +5 -0
- package/corpus/data/bad-dynamic-orm.ts +5 -0
- package/corpus/data/bad-type-only-orm.ts +3 -0
- package/corpus/e2e/bad-list-response.spec.ts +5 -0
- package/corpus/e2e/bad-timeout.spec.ts +5 -0
- package/corpus/e2e/good-detail-response.spec.ts +5 -0
- package/corpus/e2e/good-state.spec.ts +5 -0
- package/corpus/manifest.json +2 -0
- package/corpus/medusa/queries/bad-silent-cap.ts +11 -0
- package/corpus/medusa/queries/good-paged-through.ts +19 -0
- package/corpus/medusa/queries/integration-tests/any-row.ts +10 -0
- package/corpus/tissues/bad-awaits-data.tsx +8 -0
- package/corpus/tissues/bad-fetches-data.tsx +7 -0
- package/corpus/tissues/good-takes-products.tsx +5 -0
- package/dist/discover-atoms.d.ts +2 -37
- package/dist/discover-atoms.js +1 -104
- package/dist/index.d.ts +3 -12
- package/dist/index.js +21 -176
- package/dist/presets.d.ts +11 -19
- package/dist/presets.js +94 -4
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +172 -0
- package/dist/rules/atom-no-deps.d.ts +1 -0
- package/dist/rules/atom-no-deps.js +10 -11
- package/dist/rules/boundary-time-is-primitive.d.ts +0 -7
- package/dist/rules/cell-must-be-stateful.d.ts +2 -2
- package/dist/rules/cell-must-be-stateful.js +24 -23
- package/dist/rules/cell-must-not-compose-cell.d.ts +1 -0
- package/dist/rules/cell-must-not-compose-cell.js +10 -3
- package/dist/rules/cell-no-cross-feature-organelles.d.ts +1 -0
- package/dist/rules/cell-no-cross-feature-organelles.js +7 -0
- package/dist/rules/cell-no-tissues.d.ts +1 -0
- package/dist/rules/cell-no-tissues.js +11 -7
- package/dist/rules/cells-folder-index-is-barrel.d.ts +12 -10
- package/dist/rules/cells-folder-index-is-barrel.js +66 -34
- package/dist/rules/compound-must-be-stateless.d.ts +2 -0
- package/dist/rules/compound-must-be-stateless.js +24 -26
- package/dist/rules/compound-no-stores.d.ts +1 -0
- package/dist/rules/compound-no-stores.js +12 -10
- package/dist/rules/constants-in-constants-file.d.ts +2 -8
- package/dist/rules/constants-in-constants-file.js +5 -6
- package/dist/rules/dialect-through-the-seam.d.ts +0 -13
- package/dist/rules/durable-body-reads-through-steps.d.ts +0 -14
- package/dist/rules/durable-body-reads-through-steps.js +8 -3
- package/dist/rules/effect-hook-naming.d.ts +9 -11
- package/dist/rules/effect-hook-naming.js +33 -31
- package/dist/rules/emit-declares-attempts.d.ts +0 -17
- package/dist/rules/emit-declares-attempts.js +5 -10
- package/dist/rules/font-roles-only.d.ts +0 -18
- package/dist/rules/font-roles-only.js +19 -22
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.d.ts +53 -0
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.js +100 -0
- package/dist/rules/icon-control-has-name.js +7 -1
- package/dist/rules/layer-walls.d.ts +0 -25
- package/dist/rules/layer-walls.js +7 -13
- package/dist/rules/lib/atom-map.d.ts +24 -0
- package/dist/rules/lib/atom-map.js +16 -0
- package/dist/rules/lib/cells-barrel.d.ts +0 -1
- package/dist/rules/lib/colors.d.ts +0 -19
- package/dist/rules/lib/colors.js +10 -243
- package/dist/rules/lib/comments.d.ts +19 -0
- package/dist/rules/lib/comments.js +5 -0
- package/dist/rules/lib/context.d.ts +0 -10
- package/dist/rules/lib/direction-rule.d.ts +4 -21
- package/dist/rules/lib/direction-rule.js +25 -4
- package/dist/rules/lib/doors.d.ts +2 -18
- package/dist/rules/lib/doors.js +81 -41
- package/dist/rules/lib/fresh.d.ts +5 -0
- package/dist/rules/lib/fresh.js +15 -0
- package/dist/rules/lib/hooks.d.ts +2 -17
- package/dist/rules/lib/hooks.js +14 -9
- package/dist/rules/lib/imports.d.ts +2 -23
- package/dist/rules/lib/imports.js +7 -2
- package/dist/rules/lib/jsx.d.ts +31 -0
- package/dist/rules/lib/jsx.js +11 -0
- package/dist/rules/lib/landing.d.ts +0 -40
- package/dist/rules/lib/literal-call.d.ts +1 -0
- package/dist/rules/lib/literal-call.js +2 -0
- package/dist/rules/lib/module-text.d.ts +3 -27
- package/dist/rules/lib/module-text.js +19 -4
- package/dist/rules/lib/mutations.d.ts +0 -14
- package/dist/rules/lib/nodes.d.ts +1 -18
- package/dist/rules/lib/nodes.js +1 -0
- package/dist/rules/lib/objects.d.ts +2 -11
- package/dist/rules/lib/objects.js +21 -0
- package/dist/rules/lib/options.d.ts +3 -52
- package/dist/rules/lib/options.js +49 -18
- package/dist/rules/lib/package-imports.d.ts +0 -5
- package/dist/rules/lib/package-imports.js +4 -10
- package/dist/rules/lib/paths.d.ts +15 -41
- package/dist/rules/lib/paths.js +34 -1
- package/dist/rules/lib/pattern-match.d.ts +2 -12
- package/dist/rules/lib/pattern-match.js +5 -1
- package/dist/rules/lib/probe.d.ts +0 -43
- package/dist/rules/lib/reexports.d.ts +0 -18
- package/dist/rules/lib/render-tracking.d.ts +0 -8
- package/dist/rules/lib/run-files.d.ts +18 -0
- package/dist/rules/lib/run-files.js +144 -0
- package/dist/rules/lib/shared-features.d.ts +0 -8
- package/dist/rules/lib/shared-features.js +2 -9
- package/dist/rules/lib/state-sources.d.ts +8 -0
- package/dist/rules/lib/state-sources.js +55 -0
- package/dist/rules/lib/steps.d.ts +3 -5
- package/dist/rules/lib/steps.js +6 -1
- package/dist/rules/lib/styling.d.ts +9 -42
- package/dist/rules/lib/styling.js +36 -10
- package/dist/rules/lib/tiers.d.ts +11 -24
- package/dist/rules/lib/tiers.js +41 -1
- package/dist/rules/lib/tissue-entrypoint.d.ts +0 -1
- package/dist/rules/lib/tissue-entrypoint.js +4 -5
- package/dist/rules/lib/token-layer.d.ts +19 -0
- package/dist/rules/lib/token-layer.js +170 -0
- package/dist/rules/lib/tsconfig-paths.d.ts +0 -4
- package/dist/rules/lib/tsconfig-paths.js +47 -15
- package/dist/rules/lib/type-members.d.ts +6 -0
- package/dist/rules/lib/type-members.js +165 -0
- package/dist/rules/lib/visitors.d.ts +0 -22
- package/dist/rules/lib/workspace-members.d.ts +2 -0
- package/dist/rules/lib/workspace-members.js +113 -0
- package/dist/rules/list-take-without-order.d.ts +24 -0
- package/dist/rules/list-take-without-order.js +293 -16
- package/dist/rules/max-comment-density.d.ts +3 -18
- package/dist/rules/max-comment-density.js +3 -3
- package/dist/rules/molecule-atoms-only.d.ts +1 -0
- package/dist/rules/molecule-atoms-only.js +11 -7
- package/dist/rules/molecule-must-compose.d.ts +2 -0
- package/dist/rules/molecule-must-compose.js +17 -21
- package/dist/rules/mutating-step-declares-retry.d.ts +2 -7
- package/dist/rules/mutating-step-declares-retry.js +29 -11
- package/dist/rules/named-when-block.d.ts +0 -2
- package/dist/rules/named-when-block.js +2 -8
- package/dist/rules/next-route-segment-is-thin-delegate.d.ts +4 -19
- package/dist/rules/next-route-segment-is-thin-delegate.js +47 -71
- package/dist/rules/no-brand-names.d.ts +0 -9
- package/dist/rules/no-brand-names.js +1 -1
- package/dist/rules/no-card-shaped-div.d.ts +4 -42
- package/dist/rules/no-card-shaped-div.js +12 -4
- package/dist/rules/no-cross-feature-stores.d.ts +1 -0
- package/dist/rules/no-cross-feature-stores.js +9 -5
- package/dist/rules/no-d1-transaction.d.ts +2 -5
- package/dist/rules/no-d1-transaction.js +7 -0
- package/dist/rules/no-date-string-compare.d.ts +2 -0
- package/dist/rules/no-date-string-compare.js +14 -8
- package/dist/rules/no-duplicate-jsx-patterns.d.ts +3 -1
- package/dist/rules/no-duplicate-jsx-patterns.js +16 -1
- package/dist/rules/no-generic-utility-package.d.ts +48 -2
- package/dist/rules/no-generic-utility-package.js +48 -9
- package/dist/rules/no-hook-in-component-disguise.d.ts +27 -13
- package/dist/rules/no-hook-in-component-disguise.js +38 -30
- package/dist/rules/no-index-count-as-exact.d.ts +0 -3
- package/dist/rules/no-inert-hidden-jsx.d.ts +4 -21
- package/dist/rules/no-inert-hidden-jsx.js +13 -12
- package/dist/rules/no-inline-data-in-jsx.d.ts +2 -0
- package/dist/rules/no-inline-data-in-jsx.js +17 -2
- package/dist/rules/no-invalid-feature-folders.d.ts +2 -0
- package/dist/rules/no-invalid-feature-folders.js +12 -4
- package/dist/rules/no-loader-side-effect-in-index-file.d.ts +2 -9
- package/dist/rules/no-logic-in-component-files.d.ts +6 -5
- package/dist/rules/no-logic-in-component-files.js +52 -31
- package/dist/rules/no-mock-db-in-integration.d.ts +0 -15
- package/dist/rules/no-mock-db-in-integration.js +3 -4
- package/dist/rules/no-orm-outside-db.d.ts +1 -0
- package/dist/rules/no-orm-outside-db.js +10 -7
- package/dist/rules/no-raw-html-atoms.d.ts +3 -22
- package/dist/rules/no-raw-html-atoms.js +5 -17
- package/dist/rules/no-raw-sql-outside-allowed.d.ts +2 -0
- package/dist/rules/no-raw-sql-outside-allowed.js +10 -15
- package/dist/rules/no-react-namespace.d.ts +2 -0
- package/dist/rules/no-react-namespace.js +13 -0
- package/dist/rules/no-renamed-html-props.d.ts +2 -0
- package/dist/rules/no-renamed-html-props.js +11 -2
- package/dist/rules/no-render-prop-reader.d.ts +4 -20
- package/dist/rules/no-render-prop-reader.js +13 -5
- package/dist/rules/no-runtime-dead-affordance.d.ts +3 -26
- package/dist/rules/no-runtime-dead-affordance.js +7 -20
- package/dist/rules/no-swallowed-catch.js +2 -8
- package/dist/rules/no-trivial-wrapper-component.d.ts +4 -19
- package/dist/rules/no-trivial-wrapper-component.js +24 -33
- package/dist/rules/no-ts-in-bio-folders.d.ts +2 -13
- package/dist/rules/no-ts-in-bio-folders.js +9 -7
- package/dist/rules/no-type-definitions-in-components.d.ts +2 -0
- package/dist/rules/no-type-definitions-in-components.js +12 -1
- package/dist/rules/no-unregistered-plugin-dir.d.ts +0 -41
- package/dist/rules/no-unregistered-plugin-dir.js +1 -9
- package/dist/rules/no-void-port.d.ts +2 -4
- package/dist/rules/no-void-port.js +7 -0
- package/dist/rules/organelle-dependency.d.ts +1 -0
- package/dist/rules/organelle-dependency.js +10 -8
- package/dist/rules/organelle-single-source.d.ts +2 -0
- package/dist/rules/organelle-single-source.js +58 -9
- package/dist/rules/plugin-route-namespaced.d.ts +0 -11
- package/dist/rules/plugin-route-namespaced.js +2 -2
- package/dist/rules/queries-require-org-scope.d.ts +0 -3
- package/dist/rules/queries-require-org-scope.js +3 -10
- package/dist/rules/queue-loop-is-the-library.js +3 -2
- package/dist/rules/route-no-inline-mutation.d.ts +1 -25
- package/dist/rules/route-no-inline-mutation.js +2 -7
- package/dist/rules/router-schema-parity.d.ts +0 -9
- package/dist/rules/router-schema-parity.js +5 -9
- package/dist/rules/service-name-not-reserved.d.ts +0 -3
- package/dist/rules/service-name-not-reserved.js +1 -34
- package/dist/rules/ssot-no-inline-facts.js +2 -5
- package/dist/rules/ssot-no-process-env.js +4 -12
- package/dist/rules/step-declares-compensation-or-none.d.ts +2 -1
- package/dist/rules/step-declares-compensation-or-none.js +17 -1
- package/dist/rules/step-opens-its-own-cell.d.ts +3 -26
- package/dist/rules/step-opens-its-own-cell.js +8 -13
- package/dist/rules/store-route-scopes-tenant-data.js +8 -26
- package/dist/rules/subpath-over-barrel.d.ts +3 -10
- package/dist/rules/subpath-over-barrel.js +55 -10
- package/dist/rules/subscriber-declares-id.d.ts +0 -2
- package/dist/rules/subscriber-declares-id.js +2 -8
- package/dist/rules/tables-declare-their-plane.d.ts +0 -2
- package/dist/rules/tables-declare-their-plane.js +4 -10
- package/dist/rules/tenant-tables-carry-org-id.d.ts +1 -4
- package/dist/rules/tenant-tables-carry-org-id.js +5 -12
- package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +7 -1
- package/dist/rules/test-no-clock-polling-for-promise-flips.js +21 -1
- package/dist/rules/time-through-the-door.d.ts +0 -24
- package/dist/rules/time-through-the-door.js +5 -5
- package/dist/rules/tissue-must-compose.d.ts +4 -2
- package/dist/rules/tissue-must-compose.js +25 -20
- package/dist/rules/tissue-no-data-props.d.ts +57 -1
- package/dist/rules/tissue-no-data-props.js +114 -32
- package/dist/rules/tissue-no-fetch.d.ts +26 -0
- package/dist/rules/tissue-no-fetch.js +57 -0
- package/dist/rules/tissue-no-hooks.d.ts +21 -23
- package/dist/rules/tissue-no-hooks.js +22 -4
- package/dist/rules/tissue-no-organelles.d.ts +1 -0
- package/dist/rules/tissue-no-organelles.js +9 -1
- package/dist/rules/tissue-no-stores.d.ts +1 -0
- package/dist/rules/tissue-no-stores.js +9 -1
- package/dist/rules/todo-names-its-plan.d.ts +1 -28
- package/dist/rules/todo-names-its-plan.js +2 -2
- package/dist/rules/tokens-only-colors.d.ts +8 -46
- package/dist/rules/tokens-only-colors.js +64 -20
- package/dist/rules/unique-query-step-name.d.ts +0 -7
- package/dist/rules/unique-query-step-name.js +6 -10
- package/dist/rules/worker-handles-are-scoped.d.ts +11 -8
- package/dist/rules/worker-handles-are-scoped.js +12 -5
- package/dist/rules/zustand-v5-best-practices.d.ts +8 -18
- package/dist/rules/zustand-v5-best-practices.js +32 -56
- package/dist/timing.d.ts +18 -0
- package/dist/timing.js +75 -0
- package/package.json +2 -2
|
@@ -1,46 +1,68 @@
|
|
|
1
1
|
// src/rules/cells-folder-index-is-barrel.ts
|
|
2
2
|
import { isCellsBarrel } from "./lib/cells-barrel.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
3
|
+
import { landingsOf } from "./lib/landing.js";
|
|
4
|
+
import { normalise, fileOf } from "./lib/paths.js";
|
|
5
|
+
import { inTier } from "./lib/tiers.js";
|
|
6
|
+
var DESCRIPTIONS = {
|
|
7
|
+
ClassDeclaration: "a class declaration",
|
|
8
|
+
ExportDefaultDeclaration: "a default export declaration",
|
|
9
|
+
ExportNamedDeclaration: "a named export declaration (not a re-export)",
|
|
10
|
+
ExpressionStatement: "a top-level expression statement",
|
|
11
|
+
FunctionDeclaration: "a function declaration",
|
|
12
|
+
ImportDeclaration: "a side-effect import",
|
|
13
|
+
VariableDeclaration: "a variable declaration"
|
|
14
|
+
};
|
|
15
|
+
var reexportedFrom = (statement) => (statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") && typeof statement.source?.value === "string" ? statement.source.value : undefined;
|
|
16
|
+
var namesOf = (statement) => statement.type === "ExportAllDeclaration" ? [] : (statement.specifiers ?? []).flatMap((one) => typeof one.exported?.name === "string" ? [one.exported.name] : []);
|
|
17
|
+
var landsInOrganelles = (filename, statement, source) => {
|
|
18
|
+
const landed = landingsOf(filename, source, namesOf(statement));
|
|
19
|
+
if (landed.door === undefined)
|
|
20
|
+
return inTier(`${source}/`, "organelles");
|
|
21
|
+
return [landed.door, ...landed.homes.values()].some((file) => inTier(file, "organelles"));
|
|
22
|
+
};
|
|
24
23
|
var cellsFolderIndexIsBarrel = {
|
|
25
24
|
create(context) {
|
|
26
25
|
const filename = context.filename;
|
|
27
26
|
const normalized = normalise(filename);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const shortName = fileOf(normalized);
|
|
28
|
+
if (!isCellsBarrel(normalized)) {
|
|
29
|
+
if (!inTier(normalized, "cells"))
|
|
30
|
+
return {};
|
|
31
|
+
return {
|
|
32
|
+
Program(node) {
|
|
33
|
+
const sources = node.body.map(reexportedFrom);
|
|
34
|
+
if (sources.length === 0 || sources.some((one) => one === undefined))
|
|
35
|
+
return;
|
|
36
|
+
const names = node.body.flatMap(namesOf);
|
|
37
|
+
const organelle = node.body.find((statement) => landsInOrganelles(filename, statement, reexportedFrom(statement) ?? ""));
|
|
38
|
+
if (organelle === undefined)
|
|
39
|
+
return;
|
|
40
|
+
context.report({
|
|
41
|
+
data: {
|
|
42
|
+
file: shortName,
|
|
43
|
+
names: names.length > 0 ? names.join(", ") : "*",
|
|
44
|
+
source: reexportedFrom(organelle) ?? ""
|
|
45
|
+
},
|
|
46
|
+
messageId: "organelleWearingCells",
|
|
47
|
+
node: organelle
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
31
52
|
return {
|
|
32
53
|
Program(node) {
|
|
33
|
-
for (const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (stmt.type === "ExportNamedDeclaration" && stmt.source)
|
|
37
|
-
continue;
|
|
38
|
-
if (stmt.type === "ImportDeclaration" && stmt.importKind === "type")
|
|
54
|
+
for (const statement of node.body) {
|
|
55
|
+
const isReexport = reexportedFrom(statement) !== undefined;
|
|
56
|
+
if (isReexport || statement.type === "ImportDeclaration" && statement.importKind === "type") {
|
|
39
57
|
continue;
|
|
58
|
+
}
|
|
40
59
|
context.report({
|
|
41
|
-
data: {
|
|
60
|
+
data: {
|
|
61
|
+
file: shortName,
|
|
62
|
+
kind: DESCRIPTIONS[statement.type] ?? `a ${statement.type} statement`
|
|
63
|
+
},
|
|
42
64
|
messageId: "notBarrel",
|
|
43
|
-
node:
|
|
65
|
+
node: statement
|
|
44
66
|
});
|
|
45
67
|
}
|
|
46
68
|
}
|
|
@@ -48,16 +70,26 @@ var cellsFolderIndexIsBarrel = {
|
|
|
48
70
|
},
|
|
49
71
|
fixShape: `\`cells/index.ts\` and \`cells/dynamic.ts\` are barrels: \`export … from\` lines and nothing else. No
|
|
50
72
|
function, class or variable declaration, no side-effect import. Logic that wants to live in the
|
|
51
|
-
barrel belongs in a cell file the barrel re-exports
|
|
73
|
+
barrel belongs in a cell file the barrel re-exports. A file in \`cells/\` that is not a barrel and only
|
|
74
|
+
re-exports an organelle is that organelle wearing the cell folder, and every parent that composes it
|
|
75
|
+
becomes a cell composing a cell: import the organelle where it is used, or write the cell here.`,
|
|
52
76
|
meta: {
|
|
53
77
|
docs: {
|
|
54
|
-
description: "Barrel files under cells/ (index.ts, dynamic.ts, dynamic.tsx) must contain ONLY re-export statements. Component definitions belong in their own file."
|
|
78
|
+
description: "Barrel files under cells/ (index.ts, dynamic.ts, dynamic.tsx) must contain ONLY re-export statements. Component definitions belong in their own file. Any other file in cells/ that only re-exports an organelle is an organelle wearing the cell folder."
|
|
55
79
|
},
|
|
56
80
|
messages: {
|
|
81
|
+
organelleWearingCells: 'File "{{file}}" only re-exports {{names}} from "{{source}}", which is an organelle. A file in cells/ that holds no cell is an organelle wearing the cell folder, and every parent that composes it becomes a cell composing a cell. Import the organelle where it is used, or write the cell here.',
|
|
57
82
|
notBarrel: 'Barrel file "{{file}}" contains {{kind}}. Barrel files under cells/ must be pure re-export manifests (only `export * from` or `export { X } from`). Component definitions belong in their own file.'
|
|
58
83
|
},
|
|
59
84
|
schema: [],
|
|
60
85
|
type: "problem"
|
|
86
|
+
},
|
|
87
|
+
probe() {
|
|
88
|
+
return {
|
|
89
|
+
path: "src/cells/index.ts",
|
|
90
|
+
source: `export const geonosisProbe = 1
|
|
91
|
+
`
|
|
92
|
+
};
|
|
61
93
|
}
|
|
62
94
|
};
|
|
63
95
|
var cells_folder_index_is_barrel_default = cellsFolderIndexIsBarrel;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type RuleContext } from './lib/context.js';
|
|
2
2
|
import type { AnyNode } from './lib/nodes.js';
|
|
3
|
+
import { type Probe } from './lib/probe.js';
|
|
3
4
|
interface ImportNode {
|
|
4
5
|
importKind?: string;
|
|
5
6
|
source: {
|
|
@@ -35,5 +36,6 @@ declare const compoundMustBeStateless: {
|
|
|
35
36
|
schema: never[];
|
|
36
37
|
type: 'problem';
|
|
37
38
|
};
|
|
39
|
+
probe(): Probe;
|
|
38
40
|
};
|
|
39
41
|
export default compoundMustBeStateless;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/rules/compound-must-be-stateless.ts
|
|
2
2
|
import { hookCalledBy } from "./lib/hooks.js";
|
|
3
|
-
import { normalise } from "./lib/paths.js";
|
|
3
|
+
import { normalise, fileOf } from "./lib/paths.js";
|
|
4
|
+
import { inTier } from "./lib/tiers.js";
|
|
4
5
|
var reactForbiddenHooks = new Set([
|
|
5
6
|
"useCallback",
|
|
6
7
|
"useEffect",
|
|
@@ -12,49 +13,35 @@ var reactForbiddenHooks = new Set([
|
|
|
12
13
|
"useState"
|
|
13
14
|
]);
|
|
14
15
|
var CONTEXT_MODULE = /(?:^|\/)(?:providers|contexts)\//;
|
|
15
|
-
var COMPOUND_FILE_PATTERN = /(?:^|\/)(?:features\/[^/]+\/)?compounds\//;
|
|
16
16
|
var compoundMustBeStateless = {
|
|
17
17
|
create(context) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const isCompound = COMPOUND_FILE_PATTERN.test(normalized);
|
|
21
|
-
if (!isCompound)
|
|
18
|
+
const normalized = normalise(context.filename);
|
|
19
|
+
if (!inTier(normalized, "compounds"))
|
|
22
20
|
return {};
|
|
23
|
-
const shortName = normalized
|
|
21
|
+
const shortName = fileOf(normalized);
|
|
24
22
|
return {
|
|
25
23
|
CallExpression(node) {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
24
|
+
const hook = hookCalledBy(node);
|
|
25
|
+
if (hook === null)
|
|
28
26
|
return;
|
|
29
|
-
context.report({
|
|
30
|
-
data: { file: shortName, hook: name },
|
|
31
|
-
messageId: "forbiddenHookCall",
|
|
32
|
-
node
|
|
33
|
-
});
|
|
27
|
+
context.report({ data: { file: shortName, hook }, messageId: "forbiddenHookCall", node });
|
|
34
28
|
},
|
|
35
29
|
ImportDeclaration(node) {
|
|
36
30
|
if (node.importKind === "type")
|
|
37
31
|
return;
|
|
38
32
|
const source = node.source.value;
|
|
39
33
|
if (source === "react") {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
continue;
|
|
44
|
-
const importedName = spec.imported?.name;
|
|
45
|
-
if (!importedName)
|
|
46
|
-
continue;
|
|
47
|
-
if (reactForbiddenHooks.has(importedName)) {
|
|
34
|
+
for (const specifier of node.specifiers ?? []) {
|
|
35
|
+
const hook = specifier.type === "ImportSpecifier" ? specifier.imported?.name : undefined;
|
|
36
|
+
if (hook !== undefined && reactForbiddenHooks.has(hook)) {
|
|
48
37
|
context.report({
|
|
49
|
-
data: { file: shortName, hook
|
|
38
|
+
data: { file: shortName, hook },
|
|
50
39
|
messageId: "forbiddenReactHook",
|
|
51
40
|
node
|
|
52
41
|
});
|
|
53
42
|
}
|
|
54
43
|
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
if (CONTEXT_MODULE.test(source)) {
|
|
44
|
+
} else if (CONTEXT_MODULE.test(source)) {
|
|
58
45
|
context.report({
|
|
59
46
|
data: { file: shortName, source },
|
|
60
47
|
messageId: "forbiddenContextImport",
|
|
@@ -78,6 +65,17 @@ organelle and holds it; if the file itself needs state, it is not a compound.`,
|
|
|
78
65
|
},
|
|
79
66
|
schema: [],
|
|
80
67
|
type: "problem"
|
|
68
|
+
},
|
|
69
|
+
probe() {
|
|
70
|
+
return {
|
|
71
|
+
path: "src/compounds/geonosis-probe.tsx",
|
|
72
|
+
source: `declare const useGeonosisThing: () => boolean
|
|
73
|
+
|
|
74
|
+
export function GeonosisProbe() {
|
|
75
|
+
return useGeonosisThing() ? 'a' : 'b'
|
|
76
|
+
}
|
|
77
|
+
`
|
|
78
|
+
};
|
|
81
79
|
}
|
|
82
80
|
};
|
|
83
81
|
var compound_must_be_stateless_default = compoundMustBeStateless;
|
|
@@ -9,6 +9,7 @@ declare const compoundNoStores: {
|
|
|
9
9
|
}) => void;
|
|
10
10
|
}): import('./lib/visitors.js').Visitor;
|
|
11
11
|
fixShape: string;
|
|
12
|
+
probe?: ((options: readonly unknown[]) => import('./lib/probe.js').Probe | import('./lib/probe.js').ProbeTree) | undefined;
|
|
12
13
|
meta: {
|
|
13
14
|
docs: {
|
|
14
15
|
description: string;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
// src/rules/compound-no-stores.ts
|
|
2
2
|
import { directionRule } from "./lib/direction-rule.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/(?:^|\/)cells\//,
|
|
7
|
-
/(?:^|\/)tissues\//,
|
|
8
|
-
/(?:^|\/)layouts\//,
|
|
9
|
-
/\/app\//
|
|
10
|
-
];
|
|
3
|
+
import { isTestFile } from "./lib/paths.js";
|
|
4
|
+
import { inTiersAbove, APP_FOLDER, LAYOUTS_FOLDER, STORES_FOLDER } from "./lib/tiers.js";
|
|
5
|
+
var FORBIDDEN_PATTERNS = [STORES_FOLDER, LAYOUTS_FOLDER, APP_FOLDER];
|
|
11
6
|
var compoundNoStores = directionRule({
|
|
12
7
|
description: "Compounds may only import atoms, molecules, and other compounds. They cannot import organelles, cells, tissues, stores, or app routes — direction rule: a parent may only compose children at levels <= its own.",
|
|
13
8
|
fixShape: `A compound may import atoms, molecules and other compounds. Stores, organelles, cells, tissues,
|
|
14
9
|
layouts and app routes are all above it. Take the data as a prop from the cell that reads the store.
|
|
15
10
|
Type-only imports are exempt; \`export … from\` is not.`,
|
|
16
|
-
forbidden: (target) => FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
|
|
11
|
+
forbidden: (target) => inTiersAbove(target.path, "compounds") || FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
|
|
17
12
|
messages: {
|
|
18
13
|
forbidden: "Compounds cannot import from {{source}}. Direction violation — compounds are level 3 and can only depend on atoms, molecules, other compounds, and shared libs/domains/types."
|
|
19
14
|
},
|
|
20
|
-
|
|
15
|
+
probe: () => ({
|
|
16
|
+
path: "src/compounds/geonosis-probe.tsx",
|
|
17
|
+
source: `import { up } from '../stores/geonosis-up'
|
|
18
|
+
|
|
19
|
+
export const GeonosisProbe = up
|
|
20
|
+
`
|
|
21
|
+
}),
|
|
22
|
+
scope: (importer) => importer.tier === "compounds" && !isTestFile(importer.path)
|
|
21
23
|
});
|
|
22
24
|
var compound_no_stores_default = compoundNoStores;
|
|
23
25
|
export {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RuleContext } from './lib/context.js';
|
|
1
2
|
type Declarator = {
|
|
2
3
|
type: string;
|
|
3
4
|
id: {
|
|
@@ -14,14 +15,7 @@ type ExportNamedDeclaration = {
|
|
|
14
15
|
} | null;
|
|
15
16
|
};
|
|
16
17
|
declare const constantsInConstantsFile: {
|
|
17
|
-
create(context: {
|
|
18
|
-
filename: string;
|
|
19
|
-
report: (d: {
|
|
20
|
-
messageId: string;
|
|
21
|
-
node: unknown;
|
|
22
|
-
data: Record<string, string>;
|
|
23
|
-
}) => void;
|
|
24
|
-
}): {
|
|
18
|
+
create(context: RuleContext): {
|
|
25
19
|
ExportNamedDeclaration?: undefined;
|
|
26
20
|
} | {
|
|
27
21
|
ExportNamedDeclaration(node: ExportNamedDeclaration): void;
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
// src/rules/constants-in-constants-file.ts
|
|
2
|
-
import { normalise } from "./lib/paths.js";
|
|
2
|
+
import { matchesAny, normalise } from "./lib/paths.js";
|
|
3
3
|
var exemptFiles = [/\/constants\.ts$/, /\/__tests__\//, /\/scripts\//, /\/testing\//, /\.d\.ts$/];
|
|
4
4
|
var screamingSnake = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/;
|
|
5
5
|
var constantsInConstantsFile = {
|
|
6
6
|
create(context) {
|
|
7
|
-
|
|
8
|
-
if (exemptFiles.some((file) => file.test(normalized)))
|
|
7
|
+
if (matchesAny(exemptFiles, normalise(context.filename)))
|
|
9
8
|
return {};
|
|
10
9
|
return {
|
|
11
10
|
ExportNamedDeclaration(node) {
|
|
12
|
-
const declaration = node
|
|
13
|
-
if (
|
|
11
|
+
const { declaration } = node;
|
|
12
|
+
if (declaration?.type !== "VariableDeclaration")
|
|
14
13
|
return;
|
|
15
14
|
for (const declarator of declaration.declarations ?? []) {
|
|
16
15
|
const name = declarator.id.type === "Identifier" ? declarator.id.name : undefined;
|
|
17
16
|
if (name !== undefined && name.length >= 3 && screamingSnake.test(name)) {
|
|
18
|
-
context.report({ messageId: "outsideConstantsFile", node
|
|
17
|
+
context.report({ data: { name }, messageId: "outsideConstantsFile", node });
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
}
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { type RuleContextWithOptions } from './lib/context.js';
|
|
2
2
|
import { type Probe } from './lib/probe.js';
|
|
3
3
|
interface Options {
|
|
4
|
-
/**
|
|
5
|
-
* Regex sources over the normalised filename: the files the Postgres-word check leaves alone —
|
|
6
|
-
* test helpers, fixtures. Naming any REPLACES the test-path default.
|
|
7
|
-
*/
|
|
8
4
|
exempt?: string | string[];
|
|
9
|
-
/**
|
|
10
|
-
* Regex sources over the normalised filename: the only files that may name a dialect or driver
|
|
11
|
-
* module — the schema, the dialect module, the client, the drivers, drizzle-kit's configs.
|
|
12
|
-
*/
|
|
13
5
|
seam?: string | string[];
|
|
14
6
|
}
|
|
15
7
|
declare const dialectThroughTheSeam: {
|
|
@@ -53,11 +45,6 @@ declare const dialectThroughTheSeam: {
|
|
|
53
45
|
}[];
|
|
54
46
|
type: 'problem';
|
|
55
47
|
};
|
|
56
|
-
/**
|
|
57
|
-
* A file OUTSIDE the seam importing the dialect — the inverse of a probe placed where its option
|
|
58
|
-
* points. Tried against the repo's own `seam`: a seam broad enough to swallow every candidate has
|
|
59
|
-
* configured a rule that can never fire, and the probe says so rather than calling it green.
|
|
60
|
-
*/
|
|
61
48
|
probe(options: readonly unknown[]): Probe;
|
|
62
49
|
};
|
|
63
50
|
export default dialectThroughTheSeam;
|
|
@@ -2,21 +2,11 @@ import { type RuleContextWithOptions } from './lib/context.js';
|
|
|
2
2
|
import { type AnyNode } from './lib/nodes.js';
|
|
3
3
|
import { type Probe } from './lib/probe.js';
|
|
4
4
|
interface Options {
|
|
5
|
-
/** The config key that marks a workflow durable. Default: the engine's `execution`. */
|
|
6
5
|
durableKey?: string;
|
|
7
|
-
/** What that key must say. Default: the engine's `'durable'`. */
|
|
8
6
|
durableValue?: boolean | string;
|
|
9
|
-
/** The identifiers a body reaches the outside world through — this repo's ports. */
|
|
10
7
|
ports?: string[];
|
|
11
|
-
/**
|
|
12
|
-
* The receivers whose calls the ENGINE has already made steps of — `stepsOf`' proxy over a module
|
|
13
|
-
* of bound queries. A call through one is journalled under its own name and answered from the
|
|
14
|
-
* record on a replay, so it is exactly what this rule asks for rather than what it forbids.
|
|
15
|
-
*/
|
|
16
8
|
stepProxies?: string[];
|
|
17
|
-
/** The call names that define a step. Default: `createStep`. */
|
|
18
9
|
stepFactories?: string[];
|
|
19
|
-
/** The call names that define a workflow. Default: `createWorkflow`. */
|
|
20
10
|
workflowFactories?: string[];
|
|
21
11
|
}
|
|
22
12
|
declare const durableBodyReadsThroughSteps: {
|
|
@@ -69,10 +59,6 @@ declare const durableBodyReadsThroughSteps: {
|
|
|
69
59
|
}[];
|
|
70
60
|
type: 'problem';
|
|
71
61
|
};
|
|
72
|
-
/**
|
|
73
|
-
* A durable body with one free read and no step at all: a step would be the remedy, and a probe
|
|
74
|
-
* that carried one would be claiming a finding from the shape the rule accepts.
|
|
75
|
-
*/
|
|
76
62
|
probe(options: readonly unknown[]): Probe;
|
|
77
63
|
};
|
|
78
64
|
export default durableBodyReadsThroughSteps;
|
|
@@ -4,10 +4,15 @@ import { propertiesOf, stringOf, valueOf } from "./lib/objects.js";
|
|
|
4
4
|
import { requireOption } from "./lib/options.js";
|
|
5
5
|
import { bagOf, firstOf } from "./lib/probe.js";
|
|
6
6
|
import { optionsBag, STRING, STRINGS } from "./lib/schema.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
argumentsOf,
|
|
9
|
+
DEFAULT_WORKFLOW_FACTORIES,
|
|
10
|
+
isStepCall,
|
|
11
|
+
stepFactoriesOf,
|
|
12
|
+
workflowFactoriesOf
|
|
13
|
+
} from "./lib/steps.js";
|
|
8
14
|
var RULE = "durable-body-reads-through-steps";
|
|
9
15
|
var ACCEPTS = "{ ports: string[] (required), durableKey: string, durableValue: boolean | string, workflowFactories: string[], stepFactories: string[], stepProxies: string[] }";
|
|
10
|
-
var DEFAULT_WORKFLOW_FACTORIES = ["createWorkflow"];
|
|
11
16
|
var DEFAULT_DURABLE_KEY = "execution";
|
|
12
17
|
var DEFAULT_DURABLE_VALUE = "durable";
|
|
13
18
|
var declaresDurable = (node, key, value) => argumentsOf(node).some((argument) => {
|
|
@@ -44,7 +49,7 @@ var durableBodyReadsThroughSteps = {
|
|
|
44
49
|
const ports = new Set(requireOption(RULE, bag.ports, "ports", ACCEPTS));
|
|
45
50
|
const proxies = new Set(bag.stepProxies ?? []);
|
|
46
51
|
const steps = stepFactoriesOf(RULE, bag.stepFactories);
|
|
47
|
-
const workflows =
|
|
52
|
+
const workflows = workflowFactoriesOf(RULE, bag.workflowFactories);
|
|
48
53
|
const durableKey = requireOption(RULE, bag.durableKey ?? DEFAULT_DURABLE_KEY, "durableKey");
|
|
49
54
|
const durableValue = bag.durableValue ?? DEFAULT_DURABLE_VALUE;
|
|
50
55
|
return {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type RuleContext } from './lib/context.js';
|
|
2
|
+
import type { AnyNode } from './lib/nodes.js';
|
|
3
|
+
import { type Probe } from './lib/probe.js';
|
|
2
4
|
type Declared = {
|
|
3
5
|
declaration?: {
|
|
4
6
|
declarations?: {
|
|
@@ -17,20 +19,15 @@ type Declared = {
|
|
|
17
19
|
};
|
|
18
20
|
declare const effectHookNaming: {
|
|
19
21
|
create(context: RuleContext): {
|
|
20
|
-
CallExpression?: undefined;
|
|
21
22
|
ExportNamedDeclaration?: undefined;
|
|
22
|
-
|
|
23
|
+
ExportDefaultDeclaration?: undefined;
|
|
24
|
+
CallExpression?: undefined;
|
|
23
25
|
'Program:exit'?: undefined;
|
|
24
26
|
} | {
|
|
25
|
-
CallExpression(node:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
}): void;
|
|
31
|
-
ExportNamedDeclaration(node: Declared): void;
|
|
32
|
-
Program(node: unknown): void;
|
|
33
|
-
'Program:exit'(): void;
|
|
27
|
+
CallExpression(node: AnyNode): void;
|
|
28
|
+
ExportDefaultDeclaration: (node: Declared) => void;
|
|
29
|
+
ExportNamedDeclaration: (node: Declared) => void;
|
|
30
|
+
'Program:exit'(program: unknown): void;
|
|
34
31
|
};
|
|
35
32
|
fixShape: string;
|
|
36
33
|
meta: {
|
|
@@ -44,5 +41,6 @@ declare const effectHookNaming: {
|
|
|
44
41
|
schema: never[];
|
|
45
42
|
type: 'problem';
|
|
46
43
|
};
|
|
44
|
+
probe(): Probe;
|
|
47
45
|
};
|
|
48
46
|
export default effectHookNaming;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/rules/effect-hook-naming.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { hookNameOf } from "./lib/hooks.js";
|
|
3
|
+
import { normalise, fileOf } from "./lib/paths.js";
|
|
4
|
+
import { inTier } from "./lib/tiers.js";
|
|
4
5
|
var FUNCTION_VALUES = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
|
|
5
6
|
var exportedFunctionsOf = (node) => {
|
|
6
7
|
const declaration = node.declaration;
|
|
@@ -13,48 +14,38 @@ var exportedFunctionsOf = (node) => {
|
|
|
13
14
|
var effectHookNaming = {
|
|
14
15
|
create(context) {
|
|
15
16
|
const normalized = normalise(context.filename);
|
|
16
|
-
if (!
|
|
17
|
+
if (!inTier(normalized, "organelles") || !normalized.endsWith(".ts"))
|
|
17
18
|
return {};
|
|
18
|
-
const file = normalized
|
|
19
|
-
let
|
|
20
|
-
let effectHookName = "";
|
|
21
|
-
let programNode = null;
|
|
19
|
+
const file = fileOf(normalized);
|
|
20
|
+
let effectHook;
|
|
22
21
|
const exportedFunctions = [];
|
|
22
|
+
const collect = (node) => {
|
|
23
|
+
exportedFunctions.push(...exportedFunctionsOf(node));
|
|
24
|
+
};
|
|
23
25
|
return {
|
|
24
26
|
CallExpression(node) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
usesEffect = true;
|
|
29
|
-
effectHookName = node.callee.name;
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
ExportNamedDeclaration(node) {
|
|
33
|
-
exportedFunctions.push(...exportedFunctionsOf(node));
|
|
27
|
+
const hook = hookNameOf(node);
|
|
28
|
+
if (hook === "useEffect" || hook === "useLayoutEffect")
|
|
29
|
+
effectHook = hook;
|
|
34
30
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!usesEffect)
|
|
31
|
+
ExportDefaultDeclaration: collect,
|
|
32
|
+
ExportNamedDeclaration: collect,
|
|
33
|
+
"Program:exit"(program) {
|
|
34
|
+
if (effectHook === undefined)
|
|
40
35
|
return;
|
|
41
36
|
if (!file.endsWith("-effect.ts")) {
|
|
42
37
|
context.report({
|
|
43
|
-
data: { file, hook:
|
|
38
|
+
data: { file, hook: effectHook, suggested: file.replace(/\.ts$/, "-effect.ts") },
|
|
44
39
|
messageId: "filenameMissingEffect",
|
|
45
|
-
node:
|
|
40
|
+
node: program
|
|
46
41
|
});
|
|
47
42
|
}
|
|
48
|
-
for (const
|
|
49
|
-
if (!
|
|
43
|
+
for (const { name, node } of exportedFunctions) {
|
|
44
|
+
if (!name.endsWith("Effect")) {
|
|
50
45
|
context.report({
|
|
51
|
-
data: {
|
|
52
|
-
hook: effectHookName,
|
|
53
|
-
name: fn.name,
|
|
54
|
-
suggested: fn.name + "Effect"
|
|
55
|
-
},
|
|
46
|
+
data: { hook: effectHook, name, suggested: `${name}Effect` },
|
|
56
47
|
messageId: "functionMissingEffect",
|
|
57
|
-
node
|
|
48
|
+
node
|
|
58
49
|
});
|
|
59
50
|
}
|
|
60
51
|
}
|
|
@@ -74,6 +65,17 @@ file and the export together — half the pair still fires.`,
|
|
|
74
65
|
},
|
|
75
66
|
schema: [],
|
|
76
67
|
type: "problem"
|
|
68
|
+
},
|
|
69
|
+
probe() {
|
|
70
|
+
return {
|
|
71
|
+
path: "src/organelles/use-geonosis-probe-effect.ts",
|
|
72
|
+
source: `import { useEffect } from 'react'
|
|
73
|
+
|
|
74
|
+
export function useGeonosisProbe() {
|
|
75
|
+
useEffect(() => undefined, [])
|
|
76
|
+
}
|
|
77
|
+
`
|
|
78
|
+
};
|
|
77
79
|
}
|
|
78
80
|
};
|
|
79
81
|
var effect_hook_naming_default = effectHookNaming;
|
|
@@ -2,18 +2,7 @@ import { type RuleContextWithOptions } from './lib/context.js';
|
|
|
2
2
|
import { type AnyNode } from './lib/nodes.js';
|
|
3
3
|
import { type Probe } from './lib/probe.js';
|
|
4
4
|
interface Options {
|
|
5
|
-
/**
|
|
6
|
-
* The functions this repo builds a retry budget with — a Medusa storefront's `retryableEvent`,
|
|
7
|
-
* which throws on `attempts <= 1` so a call to it IS a budget.
|
|
8
|
-
*
|
|
9
|
-
* Empty by default, and naming one changes what an unreadable options bag means. Measured on a
|
|
10
|
-
* Medusa storefront: all 54 `emitEventStep` sites spell the bag `options: retryableEvent(…)`,
|
|
11
|
-
* zero literals and zero missing, so the rule said nothing about any of them and "0 findings"
|
|
12
|
-
* read as coverage while proving only that no bag was absent. Named, the helper passes and any
|
|
13
|
-
* other call is a budget nobody can check.
|
|
14
|
-
*/
|
|
15
5
|
budgetHelpers?: string[];
|
|
16
|
-
/** The call names that emit an event. Default: Medusa's `emitEventStep`. */
|
|
17
6
|
emitters?: string[];
|
|
18
7
|
}
|
|
19
8
|
declare const emitDeclaresAttempts: {
|
|
@@ -51,12 +40,6 @@ declare const emitDeclaresAttempts: {
|
|
|
51
40
|
}[];
|
|
52
41
|
type: 'problem';
|
|
53
42
|
};
|
|
54
|
-
/**
|
|
55
|
-
* The emit with no budget at all — the shape the rule speaks about first. `emitters` defaults to
|
|
56
|
-
* Medusa's spelling, so the probe reads the option the way `create` does, default included: a
|
|
57
|
-
* repo that wraps the emit under its own name gets a probe naming ITS wrapper, and a repo that
|
|
58
|
-
* does not gets `emitEventStep`.
|
|
59
|
-
*/
|
|
60
43
|
probe(options: readonly unknown[]): Probe;
|
|
61
44
|
};
|
|
62
45
|
export default emitDeclaresAttempts;
|
|
@@ -6,10 +6,6 @@ import { bagOf, firstOf } from "./lib/probe.js";
|
|
|
6
6
|
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
7
7
|
var RULE = "emit-declares-attempts";
|
|
8
8
|
var DEFAULT_EMITTERS = ["emitEventStep"];
|
|
9
|
-
var OPTIONS = "options";
|
|
10
|
-
var ATTEMPTS = "attempts";
|
|
11
|
-
var EVENT_NAME = "eventName";
|
|
12
|
-
var NO_RETRY = 1;
|
|
13
9
|
var emitDeclaresAttempts = {
|
|
14
10
|
create(context) {
|
|
15
11
|
const emitters = new Set(requireOption(RULE, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters"));
|
|
@@ -22,8 +18,8 @@ var emitDeclaresAttempts = {
|
|
|
22
18
|
const properties = propertiesOf(node.arguments?.[0]);
|
|
23
19
|
if (properties === null)
|
|
24
20
|
return;
|
|
25
|
-
const site = stringOf(valueOf(properties,
|
|
26
|
-
const options = valueOf(properties,
|
|
21
|
+
const site = stringOf(valueOf(properties, "eventName")) ?? emitter;
|
|
22
|
+
const options = valueOf(properties, "options");
|
|
27
23
|
if (options !== undefined && options.type !== "ObjectExpression") {
|
|
28
24
|
if (budgetHelpers.size === 0) {
|
|
29
25
|
context.report({ data: { emitter, site }, messageId: "undeclaredHelpers", node });
|
|
@@ -42,15 +38,14 @@ var emitDeclaresAttempts = {
|
|
|
42
38
|
const bag = options === undefined ? [] : propertiesOf(options);
|
|
43
39
|
if (bag === null)
|
|
44
40
|
return;
|
|
45
|
-
const attempts = valueOf(bag,
|
|
41
|
+
const attempts = valueOf(bag, "attempts");
|
|
46
42
|
if (attempts === undefined) {
|
|
47
43
|
context.report({ data: { emitter, site }, messageId: "noBudget", node });
|
|
48
44
|
return;
|
|
49
45
|
}
|
|
50
|
-
if (attempts.type !== "Literal" || typeof attempts.value !== "number")
|
|
51
|
-
return;
|
|
52
|
-
if (attempts.value > NO_RETRY)
|
|
46
|
+
if (attempts.type !== "Literal" || typeof attempts.value !== "number" || attempts.value > 1) {
|
|
53
47
|
return;
|
|
48
|
+
}
|
|
54
49
|
context.report({
|
|
55
50
|
data: { attempts: String(attempts.value), emitter, site },
|
|
56
51
|
messageId: "tooFew",
|