@geonosis/oxlint-plugin-biological-architecture 2.13.0 → 3.0.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 +6 -0
- package/RULES.md +1 -2
- package/dist/discover-atoms.d.ts +5 -6
- package/dist/discover-atoms.js +33 -24
- package/dist/index.d.ts +7 -54
- package/dist/index.js +180 -7973
- package/dist/presets.d.ts +6 -8
- package/dist/presets.js +137 -6
- package/dist/rules/atom-no-deps.d.ts +21 -0
- package/dist/rules/atom-no-deps.js +29 -0
- package/dist/rules/boundary-time-is-primitive.d.ts +56 -0
- package/dist/rules/boundary-time-is-primitive.js +134 -0
- package/dist/rules/cell-must-be-stateful.d.ts +22 -0
- package/dist/rules/cell-must-be-stateful.js +75 -0
- package/dist/rules/cell-must-not-compose-cell.d.ts +21 -0
- package/dist/rules/cell-must-not-compose-cell.js +29 -0
- package/dist/rules/cell-no-cross-feature-organelles.d.ts +21 -0
- package/dist/rules/cell-no-cross-feature-organelles.js +37 -0
- package/dist/rules/cell-no-tissues.d.ts +21 -0
- package/dist/rules/cell-no-tissues.js +23 -0
- package/dist/rules/cells-folder-index-is-barrel.d.ts +35 -0
- package/dist/rules/cells-folder-index-is-barrel.js +66 -0
- package/dist/rules/compound-must-be-stateless.d.ts +39 -0
- package/dist/rules/compound-must-be-stateless.js +86 -0
- package/dist/rules/compound-no-stores.d.ts +21 -0
- package/dist/rules/compound-no-stores.js +25 -0
- package/dist/rules/constants-in-constants-file.d.ts +41 -0
- package/dist/rules/constants-in-constants-file.js +41 -0
- package/dist/rules/dialect-through-the-seam.d.ts +63 -0
- package/dist/rules/dialect-through-the-seam.js +102 -0
- package/dist/rules/durable-body-reads-through-steps.d.ts +78 -0
- package/dist/rules/durable-body-reads-through-steps.js +122 -0
- package/dist/rules/effect-hook-naming.d.ts +48 -0
- package/dist/rules/effect-hook-naming.js +82 -0
- package/dist/rules/emit-declares-attempts.d.ts +62 -0
- package/dist/rules/emit-declares-attempts.js +102 -0
- package/dist/rules/font-roles-only.d.ts +78 -0
- package/dist/rules/font-roles-only.js +88 -0
- package/dist/rules/layer-walls.d.ts +97 -0
- package/dist/rules/layer-walls.js +138 -0
- package/dist/rules/lib/cells-barrel.d.ts +2 -0
- package/dist/rules/lib/cells-barrel.js +6 -0
- package/dist/rules/lib/colors.d.ts +24 -0
- package/dist/rules/lib/colors.js +261 -0
- package/dist/rules/lib/context.d.ts +26 -0
- package/dist/rules/lib/context.js +0 -0
- package/dist/rules/lib/direction-rule.d.ts +65 -0
- package/dist/rules/lib/direction-rule.js +125 -0
- package/dist/rules/lib/doors.d.ts +26 -0
- package/dist/rules/lib/doors.js +89 -0
- package/dist/rules/lib/hooks.d.ts +20 -0
- package/dist/rules/lib/hooks.js +21 -0
- package/dist/rules/lib/imports.d.ts +52 -0
- package/dist/rules/lib/imports.js +25 -0
- package/dist/rules/lib/landing.d.ts +50 -0
- package/dist/rules/lib/landing.js +111 -0
- package/dist/rules/lib/literal-call.d.ts +11 -0
- package/dist/rules/lib/literal-call.js +10 -0
- package/dist/rules/lib/module-text.d.ts +43 -0
- package/dist/{chunk-H7UQNDFE.js → rules/lib/module-text.js} +44 -30
- package/dist/rules/lib/mutations.d.ts +19 -0
- package/dist/rules/lib/mutations.js +27 -0
- package/dist/rules/lib/nodes.d.ts +25 -0
- package/dist/rules/lib/nodes.js +71 -0
- package/dist/rules/lib/objects.d.ts +17 -0
- package/dist/rules/lib/objects.js +35 -0
- package/dist/rules/lib/options.d.ts +74 -0
- package/dist/rules/lib/options.js +64 -0
- package/dist/rules/lib/package-imports.d.ts +6 -0
- package/dist/rules/lib/package-imports.js +43 -0
- package/dist/rules/lib/paths.d.ts +47 -0
- package/dist/rules/lib/paths.js +28 -0
- package/dist/rules/lib/pattern-match.d.ts +16 -0
- package/dist/rules/lib/pattern-match.js +29 -0
- package/dist/rules/lib/probe.d.ts +55 -0
- package/dist/rules/lib/probe.js +39 -0
- package/dist/rules/lib/reexports.d.ts +20 -0
- package/dist/rules/lib/reexports.js +40 -0
- package/dist/rules/lib/render-tracking.d.ts +54 -0
- package/dist/rules/lib/render-tracking.js +27 -0
- package/dist/rules/lib/schema.d.ts +14 -0
- package/dist/rules/lib/schema.js +11 -0
- package/dist/rules/lib/shared-features.d.ts +24 -0
- package/dist/rules/lib/shared-features.js +16 -0
- package/dist/rules/lib/steps.d.ts +19 -0
- package/dist/rules/lib/steps.js +37 -0
- package/dist/rules/lib/styling.d.ts +65 -0
- package/dist/rules/lib/styling.js +90 -0
- package/dist/rules/lib/tiers.d.ts +31 -0
- package/dist/rules/lib/tiers.js +32 -0
- package/dist/rules/lib/tissue-entrypoint.d.ts +5 -0
- package/dist/rules/lib/tissue-entrypoint.js +15 -0
- package/dist/rules/lib/tsconfig-paths.d.ts +5 -0
- package/dist/rules/lib/tsconfig-paths.js +109 -0
- package/dist/rules/lib/visitors.d.ts +31 -0
- package/dist/rules/lib/visitors.js +38 -0
- package/dist/rules/list-take-without-order.d.ts +36 -0
- package/dist/rules/list-take-without-order.js +84 -0
- package/dist/rules/max-comment-density.d.ts +49 -0
- package/dist/rules/max-comment-density.js +72 -0
- package/dist/rules/molecule-atoms-only.d.ts +21 -0
- package/dist/rules/molecule-atoms-only.js +28 -0
- package/dist/rules/molecule-must-compose.d.ts +25 -0
- package/dist/rules/molecule-must-compose.js +73 -0
- package/dist/rules/mutating-step-declares-retry.d.ts +57 -0
- package/dist/rules/mutating-step-declares-retry.js +92 -0
- package/dist/rules/named-when-block.d.ts +37 -0
- package/dist/rules/named-when-block.js +59 -0
- package/dist/rules/next-route-segment-is-thin-delegate.d.ts +56 -0
- package/dist/rules/next-route-segment-is-thin-delegate.js +194 -0
- package/dist/rules/no-brand-names.d.ts +64 -0
- package/dist/rules/no-brand-names.js +80 -0
- package/dist/rules/no-card-shaped-div.d.ts +67 -0
- package/dist/rules/no-card-shaped-div.js +73 -0
- package/dist/rules/no-cross-feature-stores.d.ts +21 -0
- package/dist/rules/no-cross-feature-stores.js +31 -0
- package/dist/rules/no-d1-transaction.d.ts +69 -0
- package/dist/rules/no-d1-transaction.js +90 -0
- package/dist/rules/no-date-string-compare.d.ts +28 -0
- package/dist/rules/no-date-string-compare.js +100 -0
- package/dist/rules/no-duplicate-jsx-patterns.d.ts +55 -0
- package/dist/rules/no-duplicate-jsx-patterns.js +103 -0
- package/dist/rules/no-generic-utility-package.d.ts +38 -0
- package/dist/rules/no-generic-utility-package.js +86 -0
- package/dist/rules/no-hook-in-component-disguise.d.ts +96 -0
- package/dist/rules/no-hook-in-component-disguise.js +229 -0
- package/dist/rules/no-index-count-as-exact.d.ts +42 -0
- package/dist/rules/no-index-count-as-exact.js +65 -0
- package/dist/rules/no-inert-hidden-jsx.d.ts +40 -0
- package/dist/rules/no-inert-hidden-jsx.js +75 -0
- package/dist/rules/no-inline-data-in-jsx.d.ts +24 -0
- package/dist/rules/no-inline-data-in-jsx.js +71 -0
- package/dist/rules/no-invalid-feature-folders.d.ts +36 -0
- package/dist/rules/no-invalid-feature-folders.js +88 -0
- package/dist/rules/no-loader-side-effect-in-index-file.d.ts +55 -0
- package/dist/rules/no-loader-side-effect-in-index-file.js +77 -0
- package/dist/rules/no-logic-in-component-files.d.ts +49 -0
- package/dist/rules/no-logic-in-component-files.js +105 -0
- package/dist/rules/no-mock-db-in-integration.d.ts +88 -0
- package/dist/rules/no-mock-db-in-integration.js +105 -0
- package/dist/rules/no-orm-outside-db.d.ts +21 -0
- package/dist/rules/no-orm-outside-db.js +37 -0
- package/dist/rules/no-raw-html-atoms.d.ts +82 -0
- package/dist/rules/no-raw-html-atoms.js +91 -0
- package/dist/rules/no-raw-sql-outside-allowed.d.ts +61 -0
- package/dist/rules/no-raw-sql-outside-allowed.js +57 -0
- package/dist/rules/no-react-namespace.d.ts +39 -0
- package/dist/rules/no-react-namespace.js +46 -0
- package/dist/rules/no-renamed-html-props.d.ts +38 -0
- package/dist/rules/no-renamed-html-props.js +80 -0
- package/dist/rules/no-render-prop-reader.d.ts +67 -0
- package/dist/rules/no-render-prop-reader.js +59 -0
- package/dist/rules/no-runtime-dead-affordance.d.ts +69 -0
- package/dist/rules/no-runtime-dead-affordance.js +86 -0
- package/dist/rules/no-swallowed-catch.d.ts +36 -0
- package/dist/rules/no-swallowed-catch.js +92 -0
- package/dist/rules/no-trivial-wrapper-component.d.ts +61 -0
- package/dist/rules/no-trivial-wrapper-component.js +147 -0
- package/dist/rules/no-ts-in-bio-folders.d.ts +52 -0
- package/dist/rules/no-ts-in-bio-folders.js +58 -0
- package/dist/rules/no-type-definitions-in-components.d.ts +33 -0
- package/dist/rules/no-type-definitions-in-components.js +48 -0
- package/dist/rules/no-unregistered-plugin-dir.d.ts +116 -0
- package/dist/rules/no-unregistered-plugin-dir.js +138 -0
- package/dist/rules/no-void-port.d.ts +65 -0
- package/dist/rules/no-void-port.js +53 -0
- package/dist/rules/organelle-dependency.d.ts +21 -0
- package/dist/rules/organelle-dependency.js +37 -0
- package/dist/rules/organelle-single-source.d.ts +19 -0
- package/dist/rules/organelle-single-source.js +97 -0
- package/dist/rules/plugin-route-namespaced.d.ts +79 -0
- package/dist/rules/plugin-route-namespaced.js +137 -0
- package/dist/rules/queries-require-org-scope.d.ts +88 -0
- package/dist/rules/queries-require-org-scope.js +83 -0
- package/dist/rules/queue-loop-is-the-library.d.ts +28 -0
- package/dist/rules/queue-loop-is-the-library.js +35 -0
- package/dist/rules/route-no-inline-mutation.d.ts +81 -0
- package/dist/rules/route-no-inline-mutation.js +125 -0
- package/dist/rules/router-schema-parity.d.ts +60 -0
- package/dist/rules/router-schema-parity.js +95 -0
- package/dist/rules/service-name-not-reserved.d.ts +47 -0
- package/dist/rules/service-name-not-reserved.js +112 -0
- package/dist/rules/ssot-no-inline-facts.d.ts +68 -0
- package/dist/rules/ssot-no-inline-facts.js +221 -0
- package/dist/rules/ssot-no-process-env.d.ts +55 -0
- package/dist/rules/ssot-no-process-env.js +51 -0
- package/dist/rules/step-declares-compensation-or-none.d.ts +34 -0
- package/dist/rules/step-declares-compensation-or-none.js +52 -0
- package/dist/rules/step-opens-its-own-cell.d.ts +82 -0
- package/dist/rules/step-opens-its-own-cell.js +163 -0
- package/dist/rules/store-route-scopes-tenant-data.d.ts +52 -0
- package/dist/rules/store-route-scopes-tenant-data.js +150 -0
- package/dist/rules/subpath-over-barrel.d.ts +67 -0
- package/dist/rules/subpath-over-barrel.js +153 -0
- package/dist/rules/subscriber-declares-id.d.ts +39 -0
- package/dist/rules/subscriber-declares-id.js +79 -0
- package/dist/rules/tables-declare-their-plane.d.ts +44 -0
- package/dist/rules/tables-declare-their-plane.js +64 -0
- package/dist/rules/tenant-tables-carry-org-id.d.ts +66 -0
- package/dist/rules/tenant-tables-carry-org-id.js +64 -0
- package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +42 -0
- package/dist/rules/test-no-clock-polling-for-promise-flips.js +130 -0
- package/dist/rules/time-through-the-door.d.ts +86 -0
- package/dist/rules/time-through-the-door.js +138 -0
- package/dist/rules/tissue-must-compose.d.ts +41 -0
- package/dist/rules/tissue-must-compose.js +113 -0
- package/dist/rules/tissue-no-data-props.d.ts +27 -0
- package/dist/rules/tissue-no-data-props.js +148 -0
- package/dist/rules/tissue-no-hooks.d.ts +43 -0
- package/dist/rules/tissue-no-hooks.js +72 -0
- package/dist/rules/tissue-no-organelles.d.ts +21 -0
- package/dist/rules/tissue-no-organelles.js +17 -0
- package/dist/rules/tissue-no-stores.d.ts +21 -0
- package/dist/rules/tissue-no-stores.js +17 -0
- package/dist/rules/todo-names-its-plan.d.ts +75 -0
- package/dist/rules/todo-names-its-plan.js +68 -0
- package/dist/rules/tokens-only-colors.d.ts +100 -0
- package/dist/rules/tokens-only-colors.js +129 -0
- package/dist/rules/unique-query-step-name.d.ts +50 -0
- package/dist/rules/unique-query-step-name.js +130 -0
- package/dist/rules/worker-handles-are-scoped.d.ts +65 -0
- package/dist/rules/worker-handles-are-scoped.js +97 -0
- package/dist/rules/zustand-v5-best-practices.d.ts +65 -0
- package/dist/rules/zustand-v5-best-practices.js +107 -0
- package/package.json +4 -4
- package/dist/chunk-ZY3XIRRE.js +0 -145
package/dist/presets.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* consumer's config the day it ships is a rename nobody adopts — and the reading says so ONCE per
|
|
5
5
|
* process, whatever door read it and however many rules it resolved.
|
|
6
6
|
*/
|
|
7
|
-
declare const RENAMED_TO: Record<string, string>;
|
|
8
|
-
type Preset = {
|
|
7
|
+
export declare const RENAMED_TO: Record<string, string>;
|
|
8
|
+
export type Preset = {
|
|
9
9
|
/** Rule ids that belong to this preset but are not ours — enabled alongside, never instead. */
|
|
10
10
|
rules: Record<string, string>;
|
|
11
11
|
};
|
|
@@ -20,12 +20,12 @@ type Preset = {
|
|
|
20
20
|
* The two step rules that appear in `backend-workflows` as well are deliberately NOT here: 026-W2
|
|
21
21
|
* measured them reaching both engines' spellings, and a rule that reaches both presumes neither.
|
|
22
22
|
*/
|
|
23
|
-
declare const PRESUMED_ENGINES: readonly {
|
|
23
|
+
export declare const PRESUMED_ENGINES: readonly {
|
|
24
24
|
engine: string;
|
|
25
25
|
packages: readonly string[];
|
|
26
26
|
rules: readonly string[];
|
|
27
27
|
}[];
|
|
28
|
-
declare const PRESETS: {
|
|
28
|
+
export declare const PRESETS: {
|
|
29
29
|
'backend-d1': {
|
|
30
30
|
rules: Record<string, "error">;
|
|
31
31
|
};
|
|
@@ -73,8 +73,6 @@ declare const PRESETS: {
|
|
|
73
73
|
rules: Record<string, "error">;
|
|
74
74
|
};
|
|
75
75
|
};
|
|
76
|
-
type PresetName = keyof typeof PRESETS;
|
|
76
|
+
export type PresetName = keyof typeof PRESETS;
|
|
77
77
|
/** The rule names a preset carries, stripped of the plugin namespace. */
|
|
78
|
-
declare const rulesOfPreset: (preset: PresetName) => string[];
|
|
79
|
-
|
|
80
|
-
export { PRESETS, PRESUMED_ENGINES, type Preset, type PresetName, RENAMED_TO, rulesOfPreset };
|
|
78
|
+
export declare const rulesOfPreset: (preset: PresetName) => string[];
|
package/dist/presets.js
CHANGED
|
@@ -1,9 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// src/presets.ts
|
|
2
|
+
var FRONTEND = [
|
|
3
|
+
"atom-no-deps",
|
|
4
|
+
"cell-must-be-stateful",
|
|
5
|
+
"cell-must-not-compose-cell",
|
|
6
|
+
"cell-no-cross-feature-organelles",
|
|
7
|
+
"cell-no-tissues",
|
|
8
|
+
"cells-folder-index-is-barrel",
|
|
9
|
+
"compound-must-be-stateless",
|
|
10
|
+
"compound-no-stores",
|
|
11
|
+
"effect-hook-naming",
|
|
12
|
+
"molecule-atoms-only",
|
|
13
|
+
"molecule-must-compose",
|
|
14
|
+
"next-route-segment-is-thin-delegate",
|
|
15
|
+
"no-brand-names",
|
|
16
|
+
"no-card-shaped-div",
|
|
17
|
+
"no-cross-feature-stores",
|
|
18
|
+
"no-duplicate-jsx-patterns",
|
|
19
|
+
"no-hook-in-component-disguise",
|
|
20
|
+
"no-inert-hidden-jsx",
|
|
21
|
+
"no-inline-data-in-jsx",
|
|
22
|
+
"no-invalid-feature-folders",
|
|
23
|
+
"no-logic-in-component-files",
|
|
24
|
+
"no-raw-html-atoms",
|
|
25
|
+
"no-react-namespace",
|
|
26
|
+
"no-renamed-html-props",
|
|
27
|
+
"no-render-prop-reader",
|
|
28
|
+
"no-runtime-dead-affordance",
|
|
29
|
+
"no-trivial-wrapper-component",
|
|
30
|
+
"no-ts-in-bio-folders",
|
|
31
|
+
"no-type-definitions-in-components",
|
|
32
|
+
"organelle-dependency",
|
|
33
|
+
"organelle-single-source",
|
|
34
|
+
"tissue-must-compose",
|
|
35
|
+
"tissue-no-data-props",
|
|
36
|
+
"tissue-no-hooks",
|
|
37
|
+
"tissue-no-organelles",
|
|
38
|
+
"tissue-no-stores",
|
|
39
|
+
"zustand-v5-best-practices"
|
|
40
|
+
];
|
|
41
|
+
var BACKEND_WORKFLOWS = [
|
|
42
|
+
"boundary-time-is-primitive",
|
|
43
|
+
"constants-in-constants-file",
|
|
44
|
+
"dialect-through-the-seam",
|
|
45
|
+
"durable-body-reads-through-steps",
|
|
46
|
+
"mutating-step-declares-retry",
|
|
47
|
+
"no-date-string-compare",
|
|
48
|
+
"no-orm-outside-db",
|
|
49
|
+
"no-raw-sql-outside-allowed",
|
|
50
|
+
"no-void-port",
|
|
51
|
+
"queue-loop-is-the-library",
|
|
52
|
+
"ssot-no-inline-facts",
|
|
53
|
+
"ssot-no-process-env",
|
|
54
|
+
"step-declares-compensation-or-none",
|
|
55
|
+
"tables-declare-their-plane",
|
|
56
|
+
"time-through-the-door"
|
|
57
|
+
];
|
|
58
|
+
var BACKEND_D1 = ["no-d1-transaction", "worker-handles-are-scoped"];
|
|
59
|
+
var BACKEND_WORKFLOWS_CF = ["step-opens-its-own-cell"];
|
|
60
|
+
var RENAMED_TO = {
|
|
61
|
+
"backend-sagaflow-cf": "backend-workflows-cf"
|
|
62
|
+
};
|
|
63
|
+
var announced = new Set;
|
|
64
|
+
var announceRename = (from) => {
|
|
65
|
+
if (announced.has(from))
|
|
66
|
+
return;
|
|
67
|
+
announced.add(from);
|
|
68
|
+
process.stderr.write(`biological-architecture: preset "${from}" is now "${RENAMED_TO[from]}" — the old name resolves to the same rules until the next major, then goes.
|
|
69
|
+
`);
|
|
70
|
+
};
|
|
71
|
+
var BACKEND_MEDUSA = [
|
|
72
|
+
"emit-declares-attempts",
|
|
73
|
+
"list-take-without-order",
|
|
74
|
+
"mutating-step-declares-retry",
|
|
75
|
+
"named-when-block",
|
|
76
|
+
"no-index-count-as-exact",
|
|
77
|
+
"no-loader-side-effect-in-index-file",
|
|
78
|
+
"plugin-route-namespaced",
|
|
79
|
+
"route-no-inline-mutation",
|
|
80
|
+
"service-name-not-reserved",
|
|
81
|
+
"step-declares-compensation-or-none",
|
|
82
|
+
"store-route-scopes-tenant-data",
|
|
83
|
+
"subscriber-declares-id",
|
|
84
|
+
"unique-query-step-name"
|
|
85
|
+
];
|
|
86
|
+
var FAILURE_PATHS = ["no-swallowed-catch"];
|
|
87
|
+
var INTEGRATIONS = ["no-unregistered-plugin-dir"];
|
|
88
|
+
var BACKEND_TRPC = ["router-schema-parity"];
|
|
89
|
+
var TENANT_SCOPING = [
|
|
90
|
+
"queries-require-org-scope",
|
|
91
|
+
"store-route-scopes-tenant-data",
|
|
92
|
+
"tables-declare-their-plane",
|
|
93
|
+
"tenant-tables-carry-org-id"
|
|
94
|
+
];
|
|
95
|
+
var TOOLING = ["layer-walls", "no-generic-utility-package", "todo-names-its-plan"];
|
|
96
|
+
var THEMEKIT = ["font-roles-only", "tokens-only-colors"];
|
|
97
|
+
var TESTBED = ["no-mock-db-in-integration", "test-no-clock-polling-for-promise-flips"];
|
|
98
|
+
var PACKAGE_SUBPATHS = ["subpath-over-barrel"];
|
|
99
|
+
var RULE_PREFIX = "biological-architecture";
|
|
100
|
+
var asRules = (names) => Object.fromEntries(names.map((name) => [`${RULE_PREFIX}/${name}`, "error"]));
|
|
101
|
+
var COMMENT_CEILING = {
|
|
102
|
+
rules: {
|
|
103
|
+
...asRules(["max-comment-density"]),
|
|
104
|
+
"capitalized-comments": "error"
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var PRESUMED_ENGINES = [
|
|
108
|
+
{
|
|
109
|
+
engine: "Medusa",
|
|
110
|
+
packages: ["@medusajs/framework", "@medusajs/medusa", "@medusajs/utils"],
|
|
111
|
+
rules: BACKEND_MEDUSA.filter((name) => !BACKEND_WORKFLOWS.includes(name))
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
var PRESETS = {
|
|
115
|
+
"backend-d1": { rules: asRules(BACKEND_D1) },
|
|
116
|
+
"backend-medusa": { rules: asRules(BACKEND_MEDUSA) },
|
|
117
|
+
"backend-sagaflow-cf": {
|
|
118
|
+
get rules() {
|
|
119
|
+
announceRename("backend-sagaflow-cf");
|
|
120
|
+
return asRules(BACKEND_WORKFLOWS_CF);
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"backend-trpc": { rules: asRules(BACKEND_TRPC) },
|
|
124
|
+
"backend-workflows": { rules: asRules(BACKEND_WORKFLOWS) },
|
|
125
|
+
"backend-workflows-cf": { rules: asRules(BACKEND_WORKFLOWS_CF) },
|
|
126
|
+
"comment-ceiling": COMMENT_CEILING,
|
|
127
|
+
"failure-paths": { rules: asRules(FAILURE_PATHS) },
|
|
128
|
+
frontend: { rules: asRules(FRONTEND) },
|
|
129
|
+
integrations: { rules: asRules(INTEGRATIONS) },
|
|
130
|
+
"package-subpaths": { rules: asRules(PACKAGE_SUBPATHS) },
|
|
131
|
+
recommended: { rules: asRules(FRONTEND) },
|
|
132
|
+
"tenant-scoping": { rules: asRules(TENANT_SCOPING) },
|
|
133
|
+
themekit: { rules: asRules(THEMEKIT) },
|
|
134
|
+
testbed: { rules: asRules(TESTBED) },
|
|
135
|
+
tooling: { rules: asRules(TOOLING) }
|
|
136
|
+
};
|
|
137
|
+
var rulesOfPreset = (preset) => Object.keys(PRESETS[preset].rules).filter((id) => id.startsWith(`${RULE_PREFIX}/`)).map((id) => id.slice(RULE_PREFIX.length + 1));
|
|
7
138
|
export {
|
|
8
139
|
PRESETS,
|
|
9
140
|
PRESUMED_ENGINES,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const atomNoDeps: {
|
|
2
|
+
create(context: {
|
|
3
|
+
filename: string;
|
|
4
|
+
options?: readonly unknown[];
|
|
5
|
+
report: (descriptor: {
|
|
6
|
+
data: Record<string, string>;
|
|
7
|
+
messageId: string;
|
|
8
|
+
node: unknown;
|
|
9
|
+
}) => void;
|
|
10
|
+
}): import('./lib/visitors.js').Visitor;
|
|
11
|
+
fixShape: string;
|
|
12
|
+
meta: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
messages: Record<string, string>;
|
|
17
|
+
schema: unknown[];
|
|
18
|
+
type: 'problem';
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default atomNoDeps;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/rules/atom-no-deps.ts
|
|
2
|
+
import { directionRule } from "./lib/direction-rule.js";
|
|
3
|
+
var FORBIDDEN_PATTERNS = [
|
|
4
|
+
/(?:^|\/)molecules\//,
|
|
5
|
+
/(?:^|\/)compounds\//,
|
|
6
|
+
/(?:^|\/)organelles\//,
|
|
7
|
+
/(?:^|\/)cells\//,
|
|
8
|
+
/(?:^|\/)tissues\//,
|
|
9
|
+
/(?:^|\/)stores\//,
|
|
10
|
+
/(?:^|[/@])domains(?:\/|$)/,
|
|
11
|
+
/\/app\//
|
|
12
|
+
];
|
|
13
|
+
var atomNoDeps = directionRule({
|
|
14
|
+
description: "Atoms cannot import from molecules, compounds, organelles, cells, stores, or domains",
|
|
15
|
+
fixShape: `Raw \`<button>\`, \`<input>\`, or a \`<div>\` with border + background in levels 2-6 belongs in an atom.
|
|
16
|
+
An atom imports nothing above itself — no molecule, compound, organelle, cell, tissue, store or domain,
|
|
17
|
+
and that includes \`export … from\`. If an atom needs a shared constant, the constant moves down to the atom
|
|
18
|
+
tier or into a plain \`lib/\` module, never the other way round.`,
|
|
19
|
+
forbidden: (target) => FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
|
|
20
|
+
messages: {
|
|
21
|
+
forbidden: "Atoms cannot import from {{source}}. Atoms are the lowest level and must not depend on higher-level components."
|
|
22
|
+
},
|
|
23
|
+
scope: "atoms",
|
|
24
|
+
typeOnly: "visit"
|
|
25
|
+
});
|
|
26
|
+
var atom_no_deps_default = atomNoDeps;
|
|
27
|
+
export {
|
|
28
|
+
atom_no_deps_default as default
|
|
29
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type RuleContextWithOptions } from './lib/context.js';
|
|
2
|
+
import { type Probe } from './lib/probe.js';
|
|
3
|
+
interface Options {
|
|
4
|
+
/** The modules that may name these types in an exported signature, each written out in full. */
|
|
5
|
+
doors?: string[];
|
|
6
|
+
/** The type names that must not cross an exported boundary. A namespace covers its members. */
|
|
7
|
+
types?: string[];
|
|
8
|
+
}
|
|
9
|
+
declare const boundaryTimeIsPrimitive: {
|
|
10
|
+
create(context: RuleContextWithOptions<Options>): {
|
|
11
|
+
ExportNamedDeclaration?: undefined;
|
|
12
|
+
ExportDefaultDeclaration?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
ExportNamedDeclaration: (node: {
|
|
15
|
+
declaration?: unknown;
|
|
16
|
+
}) => void;
|
|
17
|
+
ExportDefaultDeclaration: (node: {
|
|
18
|
+
declaration?: unknown;
|
|
19
|
+
}) => void;
|
|
20
|
+
};
|
|
21
|
+
fixShape: string;
|
|
22
|
+
meta: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
messages: {
|
|
27
|
+
libraryTypeAtBoundary: string;
|
|
28
|
+
};
|
|
29
|
+
schema: {
|
|
30
|
+
additionalProperties: boolean;
|
|
31
|
+
properties: {
|
|
32
|
+
doors: {
|
|
33
|
+
items: {
|
|
34
|
+
type: 'string';
|
|
35
|
+
};
|
|
36
|
+
type: 'array';
|
|
37
|
+
};
|
|
38
|
+
types: {
|
|
39
|
+
items: {
|
|
40
|
+
type: 'string';
|
|
41
|
+
};
|
|
42
|
+
type: 'array';
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type: 'object';
|
|
46
|
+
}[];
|
|
47
|
+
type: 'problem';
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A file that is not one of the declared doors, exporting the repo's own first forbidden type.
|
|
51
|
+
* `String(at)` rather than a second annotation: one finding is the claim, and a probe that fired
|
|
52
|
+
* twice would pass a count nobody asked for.
|
|
53
|
+
*/
|
|
54
|
+
probe(options: readonly unknown[]): Probe;
|
|
55
|
+
};
|
|
56
|
+
export default boundaryTimeIsPrimitive;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/rules/boundary-time-is-primitive.ts
|
|
2
|
+
import { walkNode } from "./lib/nodes.js";
|
|
3
|
+
import { requireOption } from "./lib/options.js";
|
|
4
|
+
import { isDoor } from "./lib/paths.js";
|
|
5
|
+
import { bagOf, firstOf } from "./lib/probe.js";
|
|
6
|
+
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
7
|
+
var RULE = "boundary-time-is-primitive";
|
|
8
|
+
var ACCEPTS = "{ types: string[] (type or namespace names), doors: string[] (a path, a specifier, or a regex over the filename; default none) }";
|
|
9
|
+
var asNode = (value) => typeof value === "object" && value !== null ? value : undefined;
|
|
10
|
+
var nameOf = (value) => {
|
|
11
|
+
const name = asNode(value)?.["name"];
|
|
12
|
+
return typeof name === "string" ? name : undefined;
|
|
13
|
+
};
|
|
14
|
+
var referencedType = (reference) => {
|
|
15
|
+
const typeName = asNode(reference["typeName"]);
|
|
16
|
+
if (typeName === undefined)
|
|
17
|
+
return;
|
|
18
|
+
if (typeName.type === "Identifier")
|
|
19
|
+
return nameOf(typeName);
|
|
20
|
+
if (typeName.type !== "TSQualifiedName")
|
|
21
|
+
return;
|
|
22
|
+
const namespace = nameOf(typeName["left"]);
|
|
23
|
+
const member = nameOf(typeName["right"]);
|
|
24
|
+
return namespace === undefined || member === undefined ? undefined : `${namespace}.${member}`;
|
|
25
|
+
};
|
|
26
|
+
var forbids = (types, referenced) => types.includes(referenced) || types.includes(referenced.split(".")[0] ?? referenced);
|
|
27
|
+
var functionSignature = (value) => {
|
|
28
|
+
const fn = asNode(value);
|
|
29
|
+
if (fn?.type !== "ArrowFunctionExpression" && fn?.type !== "FunctionExpression")
|
|
30
|
+
return [];
|
|
31
|
+
return [fn["params"], fn["returnType"]];
|
|
32
|
+
};
|
|
33
|
+
var signatureOf = (declaration) => {
|
|
34
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") {
|
|
35
|
+
return [declaration["params"], declaration["returnType"]];
|
|
36
|
+
}
|
|
37
|
+
if (declaration.type === "VariableDeclaration") {
|
|
38
|
+
const declarators = Array.isArray(declaration["declarations"]) ? declaration["declarations"] : [];
|
|
39
|
+
return declarators.flatMap((one) => [
|
|
40
|
+
asNode(one)?.["id"] === undefined ? undefined : asNode(asNode(one)?.["id"])?.["typeAnnotation"],
|
|
41
|
+
...functionSignature(asNode(one)?.["init"])
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
if (declaration.type === "ArrowFunctionExpression" || declaration.type === "FunctionExpression") {
|
|
45
|
+
return functionSignature(declaration);
|
|
46
|
+
}
|
|
47
|
+
if (declaration.type === "TSTypeAliasDeclaration")
|
|
48
|
+
return [declaration["typeAnnotation"]];
|
|
49
|
+
if (declaration.type === "TSInterfaceDeclaration")
|
|
50
|
+
return [declaration["body"]];
|
|
51
|
+
if (declaration.type === "ClassDeclaration") {
|
|
52
|
+
const members = asNode(declaration["body"])?.["body"];
|
|
53
|
+
return (Array.isArray(members) ? members : []).flatMap((member) => [
|
|
54
|
+
asNode(member)?.["typeAnnotation"],
|
|
55
|
+
...functionSignature(asNode(member)?.["value"])
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
};
|
|
60
|
+
var declaredName = (declaration) => {
|
|
61
|
+
const own = nameOf(declaration["id"]);
|
|
62
|
+
if (own !== undefined)
|
|
63
|
+
return own;
|
|
64
|
+
const declarators = Array.isArray(declaration["declarations"]) ? declaration["declarations"] : [];
|
|
65
|
+
return nameOf(asNode(declarators[0])?.["id"]) ?? "the export";
|
|
66
|
+
};
|
|
67
|
+
var behindADoor = (filename, doors) => doors.some((door) => isDoor(filename, door));
|
|
68
|
+
var boundaryTimeIsPrimitive = {
|
|
69
|
+
create(context) {
|
|
70
|
+
const types = requireOption(RULE, context.options?.[0]?.types, "types", ACCEPTS);
|
|
71
|
+
const doors = context.options?.[0]?.doors ?? [];
|
|
72
|
+
if (behindADoor(context.filename, doors))
|
|
73
|
+
return {};
|
|
74
|
+
const behind = doors.length === 0 ? "the module that owns the library" : doors.join(", ");
|
|
75
|
+
const report = (node) => {
|
|
76
|
+
const declaration = asNode(node.declaration);
|
|
77
|
+
if (declaration === undefined)
|
|
78
|
+
return;
|
|
79
|
+
for (const root of signatureOf(declaration)) {
|
|
80
|
+
walkNode(root, (found) => {
|
|
81
|
+
if (found.type !== "TSTypeReference")
|
|
82
|
+
return;
|
|
83
|
+
const referenced = referencedType(found);
|
|
84
|
+
if (referenced === undefined || !forbids(types, referenced))
|
|
85
|
+
return;
|
|
86
|
+
context.report({
|
|
87
|
+
data: { doors: behind, name: declaredName(declaration), type: referenced },
|
|
88
|
+
messageId: "libraryTypeAtBoundary",
|
|
89
|
+
node: found
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
ExportNamedDeclaration: report,
|
|
96
|
+
ExportDefaultDeclaration: report
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
fixShape: `An exported signature carries time as a PRIMITIVE: epoch milliseconds for an instant, an ISO date
|
|
100
|
+
string (\`YYYY-MM-DD\`) for a business date. A \`Date\`, a \`Dayjs\` or a \`Temporal\` type in a parameter,
|
|
101
|
+
a return or an exported shape hands every caller the library and its configuration, and no database
|
|
102
|
+
column, wire message or contract can hold one at all — the boundary is where the conversion belongs.
|
|
103
|
+
Convert at the door the \`doors\` option names; that module is the one place allowed to say the
|
|
104
|
+
library's type out loud, and a display string is produced at the edge from the primitive rather than
|
|
105
|
+
carried across as a formatted value. Only the signature is judged: a \`Date\` a function builds and
|
|
106
|
+
keeps inside its own body is its own business.`,
|
|
107
|
+
meta: {
|
|
108
|
+
docs: {
|
|
109
|
+
description: "A date-library type in an exported signature couples every caller to the library and its configuration. Time crosses a boundary as a primitive — epoch milliseconds or an ISO date string — and the library type stays inside the module the `doors` option names. Enabled without `types` it reports that once a run and judges nothing."
|
|
110
|
+
},
|
|
111
|
+
messages: {
|
|
112
|
+
libraryTypeAtBoundary: '"{{name}}" puts `{{type}}` in an exported signature. Time crosses a boundary as a primitive — epoch milliseconds for an instant, an ISO date string for a business date — and `{{type}}` stays behind {{doors}}.'
|
|
113
|
+
},
|
|
114
|
+
schema: optionsBag({
|
|
115
|
+
doors: STRINGS,
|
|
116
|
+
types: STRINGS
|
|
117
|
+
}),
|
|
118
|
+
type: "problem"
|
|
119
|
+
},
|
|
120
|
+
probe(options) {
|
|
121
|
+
const { doors = [], types = [] } = bagOf(options);
|
|
122
|
+
const type = firstOf(types, []);
|
|
123
|
+
const path = ["geonosis-probe-boundary.ts", "src/geonosis-probe-boundary.ts"].find((candidate) => !behindADoor(candidate, doors));
|
|
124
|
+
if (path === undefined) {
|
|
125
|
+
throw new Error(`biological-architecture/${RULE}: every path a probe could be written at is itself one of the declared \`doors\` (${doors.join(", ")}), so there is nowhere left for this rule to speak.`);
|
|
126
|
+
}
|
|
127
|
+
return { path, source: `export const boundary = (at: ${type}): string => String(at)
|
|
128
|
+
` };
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var boundary_time_is_primitive_default = boundaryTimeIsPrimitive;
|
|
132
|
+
export {
|
|
133
|
+
boundary_time_is_primitive_default as default
|
|
134
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type RuleContext } from './lib/context.js';
|
|
2
|
+
declare const cellMustBeStateful: {
|
|
3
|
+
create(context: RuleContext): {
|
|
4
|
+
Program?: undefined;
|
|
5
|
+
'Program:exit'?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
Program(node: unknown): void;
|
|
8
|
+
'Program:exit'(): void;
|
|
9
|
+
};
|
|
10
|
+
fixShape: string;
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
messages: {
|
|
16
|
+
shouldBeCompound: string;
|
|
17
|
+
};
|
|
18
|
+
schema: never[];
|
|
19
|
+
type: 'problem';
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export default cellMustBeStateful;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/rules/cell-must-be-stateful.ts
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import { normalise } from "./lib/paths.js";
|
|
4
|
+
import { launderedHomesOf } from "./lib/reexports.js";
|
|
5
|
+
var STATE_SIGNATURE = /\buse[A-Z]\w*\s*\(|from\s+['"][^'"]*\/organelles(?:\/|['"])|from\s+['"][^'"]*\/stores?(?:\/|['"])|createContext\s*\(/;
|
|
6
|
+
var SIBLING_SUFFIXES = [
|
|
7
|
+
".tsx",
|
|
8
|
+
".parts.tsx",
|
|
9
|
+
".sections.tsx",
|
|
10
|
+
".layouts.tsx",
|
|
11
|
+
".icons.tsx",
|
|
12
|
+
".sidebar.tsx",
|
|
13
|
+
".summary.tsx",
|
|
14
|
+
".form.tsx",
|
|
15
|
+
".data.tsx",
|
|
16
|
+
".steps.tsx"
|
|
17
|
+
];
|
|
18
|
+
var PRIVATE_OR_NON_ENTRY = /\.(parts|sections|layouts|types|icons|sidebar|summary|form|data|steps|stories|test|spec)\.tsx$/;
|
|
19
|
+
function familyHasState(mainFile) {
|
|
20
|
+
const stem = mainFile.replace(/\.tsx$/, "");
|
|
21
|
+
for (const file of [mainFile, ...SIBLING_SUFFIXES.map((suffix) => stem + suffix)]) {
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(file) && STATE_SIGNATURE.test(fs.readFileSync(file, "utf8"))) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
var cellMustBeStateful = {
|
|
31
|
+
create(context) {
|
|
32
|
+
const normalized = normalise(context.filename);
|
|
33
|
+
const base = normalized.split("/").pop() || "";
|
|
34
|
+
const isCellEntrypoint = /(?:^|\/)cells\//.test(normalized) && base.endsWith(".tsx") && base !== "index.tsx" && !PRIVATE_OR_NON_ENTRY.test(base);
|
|
35
|
+
if (!isCellEntrypoint)
|
|
36
|
+
return {};
|
|
37
|
+
const laundered = launderedHomesOf(context.filename);
|
|
38
|
+
if (laundered !== undefined && laundered.length === 0)
|
|
39
|
+
return {};
|
|
40
|
+
const judged = laundered ?? [normalized];
|
|
41
|
+
let programNode = null;
|
|
42
|
+
return {
|
|
43
|
+
Program(node) {
|
|
44
|
+
programNode = node;
|
|
45
|
+
},
|
|
46
|
+
"Program:exit"() {
|
|
47
|
+
if (!judged.every((file) => familyHasState(normalise(file)))) {
|
|
48
|
+
context.report({
|
|
49
|
+
data: { file: base },
|
|
50
|
+
messageId: "shouldBeCompound",
|
|
51
|
+
node: programNode
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
fixShape: `A cell OWNS state: a \`use*()\` call, an organelle import, or a store read, somewhere in its family
|
|
58
|
+
(\`x.tsx\` + \`x.parts.tsx\` + \`x.sections.tsx\` + …). A file in \`cells/\` that is props-in / JSX-out with
|
|
59
|
+
no state anywhere in that family is inert chemistry — move it to \`compounds/\`. If it should be
|
|
60
|
+
stateful, put the store read or the organelle in it rather than taking the data as a prop.`,
|
|
61
|
+
meta: {
|
|
62
|
+
docs: {
|
|
63
|
+
description: "A cell must own state (a hook, a state-bearing organelle, or a store) somewhere in its family. A cell whose whole family is stateless props-in/JSX-out is a compound — move it to compounds/."
|
|
64
|
+
},
|
|
65
|
+
messages: {
|
|
66
|
+
shouldBeCompound: 'Cell "{{file}}" holds no state (no hooks, no organelle, no store) anywhere in its family — it is a stateless display and belongs in compounds/, not cells/.'
|
|
67
|
+
},
|
|
68
|
+
schema: [],
|
|
69
|
+
type: "problem"
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var cell_must_be_stateful_default = cellMustBeStateful;
|
|
73
|
+
export {
|
|
74
|
+
cell_must_be_stateful_default as default
|
|
75
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const cellMustNotComposeCell: {
|
|
2
|
+
create(context: {
|
|
3
|
+
filename: string;
|
|
4
|
+
options?: readonly unknown[];
|
|
5
|
+
report: (descriptor: {
|
|
6
|
+
data: Record<string, string>;
|
|
7
|
+
messageId: string;
|
|
8
|
+
node: unknown;
|
|
9
|
+
}) => void;
|
|
10
|
+
}): import('./lib/visitors.js').Visitor;
|
|
11
|
+
fixShape: string;
|
|
12
|
+
meta: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
messages: Record<string, string>;
|
|
17
|
+
schema: unknown[];
|
|
18
|
+
type: 'problem';
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default cellMustNotComposeCell;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/rules/cell-must-not-compose-cell.ts
|
|
2
|
+
import { isCellsBarrel } from "./lib/cells-barrel.js";
|
|
3
|
+
import { directionRule } from "./lib/direction-rule.js";
|
|
4
|
+
var cellStem = (filePath) => filePath.replace(/\.[jt]sx?$/, "").replace(/\.(parts|sections|summary|layouts|icons|types|hooks|utils|context|store|sidebar|data|config)$/i, "");
|
|
5
|
+
var IN_CELLS = /(?:^|\/)cells\//;
|
|
6
|
+
var cellMustNotComposeCell = directionRule({
|
|
7
|
+
data: (target, importer) => ({ currentFile: importer.file, importedFile: target.file }),
|
|
8
|
+
description: "Cells must not import from any other cell anywhere in the repo. No same-feature exception, no shared/document/charts exception. Cells compose only atoms, molecules, compounds, and organelles. Type-only imports are allowed.",
|
|
9
|
+
fixShape: `A cell never composes another cell — not same-feature, not \`shared/\`, no exception. If you want to,
|
|
10
|
+
one of the two is misclassified: arrangement goes up to a \`tissues/\` file, a stateless display goes
|
|
11
|
+
down to \`compounds/\`, a store adapter goes sideways to \`organelles/\`. A parent that holds state AND
|
|
12
|
+
hosts a cell (a summary card that opens a modal-hosted form) is two cells side by side: the child
|
|
13
|
+
hosts its own modal and reads whether it is open from the store, the parent writes it, and a
|
|
14
|
+
\`tissues/\` file arranges both. Cells coordinate through a store, never through props from one to
|
|
15
|
+
the other. A stateless adapter between cells is a tissue — it may take the data it hands down
|
|
16
|
+
(\`{ collections: Collection[] }\`); it may not take a handler. Private siblings of the SAME
|
|
17
|
+
component (\`invoice-editor.parts.tsx\` from \`invoice-editor.tsx\`) are one cell and are fine. The
|
|
18
|
+
barrels \`cells/index.ts\`, \`cells/dynamic.ts\` and \`cells/dynamic.tsx\` are exempt — re-exporting
|
|
19
|
+
siblings is the shape \`cells-folder-index-is-barrel\` requires of them.`,
|
|
20
|
+
forbidden: (target, importer) => IN_CELLS.test(target.path) && cellStem(target.file) !== cellStem(importer.file),
|
|
21
|
+
messages: {
|
|
22
|
+
forbidden: 'Cell "{{currentFile}}" imports from another cell "{{importedFile}}". Cells never compose other cells. Decompose: if this is arrangement, move the parent to tissues/. If the import is a stateless display, move the child to compounds/. If the child is a store adapter, move it to organelles/.'
|
|
23
|
+
},
|
|
24
|
+
scope: (importer) => IN_CELLS.test(importer.path) && !isCellsBarrel(importer.path)
|
|
25
|
+
});
|
|
26
|
+
var cell_must_not_compose_cell_default = cellMustNotComposeCell;
|
|
27
|
+
export {
|
|
28
|
+
cell_must_not_compose_cell_default as default
|
|
29
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const cellNoCrossFeatureOrganelles: {
|
|
2
|
+
create(context: {
|
|
3
|
+
filename: string;
|
|
4
|
+
options?: readonly unknown[];
|
|
5
|
+
report: (descriptor: {
|
|
6
|
+
data: Record<string, string>;
|
|
7
|
+
messageId: string;
|
|
8
|
+
node: unknown;
|
|
9
|
+
}) => void;
|
|
10
|
+
}): import('./lib/visitors.js').Visitor;
|
|
11
|
+
fixShape: string;
|
|
12
|
+
meta: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
messages: Record<string, string>;
|
|
17
|
+
schema: unknown[];
|
|
18
|
+
type: 'problem';
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default cellNoCrossFeatureOrganelles;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/rules/cell-no-cross-feature-organelles.ts
|
|
2
|
+
import { directionRule } from "./lib/direction-rule.js";
|
|
3
|
+
import {
|
|
4
|
+
crossesFeatureBoundary,
|
|
5
|
+
SHARED_FEATURES_SCHEMA
|
|
6
|
+
} from "./lib/shared-features.js";
|
|
7
|
+
var FEATURE_ORGANELLE = /(?:^|[/@])features\/([^/]+)\/organelles\//;
|
|
8
|
+
var organelleFeatureOf = (path) => FEATURE_ORGANELLE.exec(path)?.[1];
|
|
9
|
+
var cellNoCrossFeatureOrganelles = directionRule({
|
|
10
|
+
data: (target, importer) => ({
|
|
11
|
+
file: importer.file,
|
|
12
|
+
importedFeature: organelleFeatureOf(target.path) ?? "",
|
|
13
|
+
ownFeature: importer.feature ?? "",
|
|
14
|
+
source: target.source
|
|
15
|
+
}),
|
|
16
|
+
description: "Cells must not import another feature's organelles. An organelle is a feature's own state compartment; a cell in features/X/ hosts features/X/organelles/* and the features named in sharedFeatures (default: shared). Another feature's state arrives as props from the tissue above.",
|
|
17
|
+
fixShape: `A cell hosts its OWN feature's organelles. Another feature's organelle is that feature's state
|
|
18
|
+
compartment, and a cell that imports one couples two features through a hook instead of through
|
|
19
|
+
props — take the value as a prop and let the tissue above arrange both, or move the organelle into a
|
|
20
|
+
feature every cell may read (the rule's \`sharedFeatures\` option, default \`shared\`). Relative and
|
|
21
|
+
aliased sources are judged alike: \`../../billing/organelles/totals\` lands where
|
|
22
|
+
\`@/features/billing/organelles/totals\` lands. Type-only imports are exempt; \`export … from\` is not.`,
|
|
23
|
+
forbidden: (target, importer, options) => {
|
|
24
|
+
const importedFeature = organelleFeatureOf(target.path);
|
|
25
|
+
return crossesFeatureBoundary(importedFeature, importer.feature, options);
|
|
26
|
+
},
|
|
27
|
+
messages: {
|
|
28
|
+
forbidden: `Cell "{{file}}" in feature "{{ownFeature}}" imports "{{source}}", an organelle of feature "{{importedFeature}}". A cell hosts its own feature's organelles; another feature's state arrives as props from the tissue above. Only features/{{ownFeature}}/organelles/* and the shared features are allowed here.`
|
|
29
|
+
},
|
|
30
|
+
optionsFrom: (raw) => raw ?? {},
|
|
31
|
+
scope: "cells",
|
|
32
|
+
schema: SHARED_FEATURES_SCHEMA
|
|
33
|
+
});
|
|
34
|
+
var cell_no_cross_feature_organelles_default = cellNoCrossFeatureOrganelles;
|
|
35
|
+
export {
|
|
36
|
+
cell_no_cross_feature_organelles_default as default
|
|
37
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const cellNoTissues: {
|
|
2
|
+
create(context: {
|
|
3
|
+
filename: string;
|
|
4
|
+
options?: readonly unknown[];
|
|
5
|
+
report: (descriptor: {
|
|
6
|
+
data: Record<string, string>;
|
|
7
|
+
messageId: string;
|
|
8
|
+
node: unknown;
|
|
9
|
+
}) => void;
|
|
10
|
+
}): import('./lib/visitors.js').Visitor;
|
|
11
|
+
fixShape: string;
|
|
12
|
+
meta: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
messages: Record<string, string>;
|
|
17
|
+
schema: unknown[];
|
|
18
|
+
type: 'problem';
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default cellNoTissues;
|