@geonosis/oxlint-plugin-biological-architecture 0.1.0 → 0.1.2
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/README.md +107 -0
- package/bin/geonosis-lint-parity.mjs +4 -0
- package/corpus/.oxlintrc.json +114 -0
- package/corpus/api/store/catalog/route.ts +6 -0
- package/corpus/api/store/orders/route.ts +6 -0
- package/corpus/api/store/orders/scoped.route.ts +13 -0
- package/corpus/app/bad-route/page.tsx +3 -0
- package/corpus/apps/api/src/bad-unscoped-worker.ts +3 -0
- package/corpus/atoms/bad-import.tsx +5 -0
- package/corpus/atoms/good-atom.tsx +4 -0
- package/corpus/brand/bad-brand.tsx +2 -0
- package/corpus/brand/good-media-url.tsx +2 -0
- package/corpus/cells/bad-cross-cell.tsx +4 -0
- package/corpus/cells/bad-logic.ts +3 -0
- package/corpus/cells/bad-stateless-display.tsx +6 -0
- package/corpus/cells/bad-tissue-import.tsx +5 -0
- package/corpus/cells/bad-triplicate.tsx +9 -0
- package/corpus/cells/bad-two-exports.tsx +6 -0
- package/corpus/cells/bad-type-def.tsx +8 -0
- package/corpus/cells/dynamic.tsx +1 -0
- package/corpus/cells/good-iterated.tsx +9 -0
- package/corpus/cells/good-one-export.tsx +10 -0
- package/corpus/cells/good-own-sibling.sections.tsx +3 -0
- package/corpus/cells/good-own-sibling.tsx +4 -0
- package/corpus/cells/good-shape.types.ts +3 -0
- package/corpus/cells/good-stateful.tsx +12 -0
- package/corpus/cells/index.ts +2 -0
- package/corpus/cells/other-cell.tsx +3 -0
- package/corpus/comments/bad-comment-density.ts +5 -0
- package/corpus/compounds/bad-card-div.tsx +3 -0
- package/corpus/compounds/bad-inline-data.tsx +5 -0
- package/corpus/compounds/bad-stateful.tsx +8 -0
- package/corpus/compounds/bad-store-import.tsx +5 -0
- package/corpus/compounds/good-compound.tsx +6 -0
- package/corpus/config/bad-process-env.ts +1 -0
- package/corpus/data/bad-d1-transaction.ts +5 -0
- package/corpus/data/bad-dialect-leak.ts +3 -0
- package/corpus/data/bad-orm-outside-db.ts +3 -0
- package/corpus/data/bad-raw-sql.ts +3 -0
- package/corpus/features/invoice/cells/bad-cross-feature-store.tsx +6 -0
- package/corpus/features/invoice/widgets/bad-folder.ts +1 -0
- package/corpus/inline/uses-engine-step.ts +5 -0
- package/corpus/manifest.json +59 -0
- package/corpus/molecules/bad-cell-import.tsx +7 -0
- package/corpus/molecules/bad-passthrough.tsx +8 -0
- package/corpus/molecules/bad-renamed-props.tsx +9 -0
- package/corpus/molecules/good-molecule.tsx +6 -0
- package/corpus/organelles/bad-imports-cell.tsx +7 -0
- package/corpus/organelles/bad-two-sources.tsx +9 -0
- package/corpus/organelles/good-imports-atom.tsx +6 -0
- package/corpus/organelles/use-counter-effect.ts +11 -0
- package/corpus/organelles/use-counter.ts +12 -0
- package/corpus/packages/db/src/queries/bad-unscoped.ts +3 -0
- package/corpus/packages/db/src/schema/bad-undeclared-plane.ts +3 -0
- package/corpus/packages/db/src/schema/tenant/bad-no-org-id.ts +3 -0
- package/corpus/packages/db/src/schema/tenant/bad-pg-per-kind.ts +8 -0
- package/corpus/packages/db/src/schema/tenant/bad-sqlite-per-kind.ts +8 -0
- package/corpus/packages/db/src/schema/tenant/good-one-table.ts +9 -0
- package/corpus/packages/workflows/src/bad-per-kind-saga.ts +3 -0
- package/corpus/ports/bad-void-port.ts +1 -0
- package/corpus/queue/bad-hand-rolled-loop.ts +3 -0
- package/corpus/react/bad-hook-disguise.tsx +9 -0
- package/corpus/react/bad-inert-hidden.tsx +5 -0
- package/corpus/react/bad-react-namespace.tsx +8 -0
- package/corpus/react/bad-render-prop.tsx +5 -0
- package/corpus/react/bad-zustand.tsx +6 -0
- package/corpus/react/good-react-namespace.tsx +7 -0
- package/corpus/time/bad-raw-date.ts +1 -0
- package/corpus/tissues/bad-data-props.tsx +5 -0
- package/corpus/tissues/bad-hooks-entry.tsx +6 -0
- package/corpus/tissues/bad-organelle-import.tsx +5 -0
- package/corpus/tissues/bad-passthrough.tsx +3 -0
- package/corpus/tissues/bad-store-import.tsx +5 -0
- package/corpus/tissues/good-composes.tsx +4 -0
- package/corpus/tissues/good-hooks.parts.tsx +5 -0
- package/corpus/tissues/good-types.types.ts +1 -0
- package/corpus/workflows/bad-engine-step.ts +6 -0
- package/corpus/workflows/good-wrapper-step.ts +4 -0
- package/corpus/workflows/step.ts +6 -0
- package/dist/chunk-2VSZAP5M.js +240 -0
- package/dist/chunk-BFFWNFJM.js +6 -0
- package/dist/chunk-BGX2XYEL.js +153 -0
- package/dist/chunk-FGUOGKA5.js +228 -0
- package/dist/chunk-FJ3J7B5J.js +155 -0
- package/dist/chunk-NZVMV3Y5.js +339 -0
- package/dist/chunk-RNY5NDH5.js +155 -0
- package/dist/chunk-SUTNA6YD.js +268 -0
- package/dist/chunk-UM6UIUH3.js +250 -0
- package/dist/chunk-YIAUS4GG.js +298 -0
- package/dist/index.js +19 -6
- package/dist/parity-cli.js +84 -0
- package/dist/parity.js +32 -0
- package/package.json +16 -8
package/README.md
CHANGED
|
@@ -68,6 +68,113 @@ everything below the organelle is inert. A cell never composes another cell.
|
|
|
68
68
|
A rule that is false on another stack gets its own preset rather than riding along in a generic
|
|
69
69
|
one: a preset carrying a repo fact is a repo fact wearing a preset's name.
|
|
70
70
|
|
|
71
|
+
## Adopting
|
|
72
|
+
|
|
73
|
+
A repo that already has its own copy of these rules does not delete it because the version numbers
|
|
74
|
+
match. It deletes it when the **findings** match: same tree, same rule ids, two configs, and a diff.
|
|
75
|
+
`geonosis-lint-parity` is that diff.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
geonosis-lint-parity --a <config-a.json> --b <config-b.json> [--oxlint <path>] [--out <dir>] -- <paths…>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Write both configs inside the workspace being linted** — `<ws>/.oxlintrc.parity-a.json`, deleted
|
|
82
|
+
afterwards — or name the plugin by absolute path in `jsPlugins`. oxlint resolves a `jsPlugins`
|
|
83
|
+
specifier relative to the **config file's** directory, not the working directory, so a config
|
|
84
|
+
written to a temp dir cannot find a plugin installed in the workspace: it fails with
|
|
85
|
+
`Cannot find module '@…/plugin'`, and the tool correctly refuses with
|
|
86
|
+
`config A: oxlint exited 1 without reporting a single finding`.
|
|
87
|
+
|
|
88
|
+
### Step 0 — reach, before parity
|
|
89
|
+
|
|
90
|
+
Parity between two configs that both do nothing is perfect parity. In one source repo 34 of 51
|
|
91
|
+
configured rules fired nowhere in any corpus it had: 34 rules a consumer would inherit as claims
|
|
92
|
+
nothing could check. So this package ships the **reach corpus** — the fixture tree its own gates run
|
|
93
|
+
against, one deliberately broken file per rule — and answers the question directly:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
geonosis-lint-parity --corpus [<dir>] --a oxlintrc.fork.json --b oxlintrc.published.json [--out <dir>]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Both configs run over the corpus (copied to a temp root first, so neither a `.gitignore` nor
|
|
100
|
+
oxlint's `node_modules` skip can quietly turn "every rule fired nowhere" into the answer). It prints
|
|
101
|
+
a fired-under-A / fired-under-B row per in-scope rule and **exits 1 when one fires under neither**.
|
|
102
|
+
`--out` writes `CORPUS.md`.
|
|
103
|
+
|
|
104
|
+
**A corpus covers the rules its own `manifest.json` names, and only those.** The one this package
|
|
105
|
+
ships names `biological-architecture/*` — one deliberately broken file per rule, the list generated
|
|
106
|
+
from the plugin's own rules export at build time. A configured `eqeqeq`, `unicorn/*` or
|
|
107
|
+
`typescript/*` is listed under **outside this corpus** and gets no verdict: this tree is no evidence
|
|
108
|
+
either way about somebody else's rule, and judging one would fail every real config for saying
|
|
109
|
+
nothing. `--corpus <dir>` points the same run at another plugin's corpus; the scope moves with it.
|
|
110
|
+
|
|
111
|
+
Every rule this package exports has at least one corpus file that fires it, and a test fails if that
|
|
112
|
+
stops being true — so a rule cannot be added without evidence it reaches anything.
|
|
113
|
+
|
|
114
|
+
### Then parity
|
|
115
|
+
|
|
116
|
+
It runs oxlint twice with `--format=unix` over the same paths, strips the working-directory prefix,
|
|
117
|
+
sorts, and prints the two counts, `only in A`, `only in B` and a per-rule tally. **It exits 1 when
|
|
118
|
+
`only in A` is non-empty** — a finding the old config caught and the new one does not. New findings
|
|
119
|
+
are what an upgrade is for; lost ones stop the adoption. `--out` writes `a.txt`, `b.txt`,
|
|
120
|
+
`only-in-a.txt`, `only-in-b.txt`, `changed.txt` and `SUMMARY.md` for the proof file.
|
|
121
|
+
|
|
122
|
+
**A finding is identified by where it is and which rule found it — `path:line:col` plus
|
|
123
|
+
`plugin(rule)`.** The message is not part of it, and neither is the severity. A release that only
|
|
124
|
+
rewords a rule's message would otherwise show as one finding lost and one gained, and stop an
|
|
125
|
+
adoption over a sentence. Those pair up instead under **changed**, reported in full and never
|
|
126
|
+
reaching the exit code; a severity change lands there too, because that is a fact about the config
|
|
127
|
+
rather than a rule that stopped firing.
|
|
128
|
+
|
|
129
|
+
**Fork vs published**, before deleting a vendored copy — two configs identical but for `jsPlugins`:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# oxlintrc.fork.json → "jsPlugins": ["./packages/eslint-plugin-biological-architecture"]
|
|
133
|
+
# oxlintrc.published.json → "jsPlugins": ["@geonosis/oxlint-plugin-biological-architecture"]
|
|
134
|
+
geonosis-lint-parity \
|
|
135
|
+
--a oxlintrc.fork.json --b oxlintrc.published.json \
|
|
136
|
+
--out proofs/adoption-parity \
|
|
137
|
+
-- apps packages
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Version vs version**, as the kit's own release check — the same config, pointing first at the
|
|
141
|
+
installed version and then at a local build, by absolute path:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
geonosis-lint-parity \
|
|
145
|
+
--a oxlintrc.0.1.0.json --b oxlintrc.0.1.1.json \
|
|
146
|
+
--out proofs/parity-0.1.0-vs-0.1.1 \
|
|
147
|
+
-- apps packages
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`--oxlint` overrides the binary; by default it walks up from the working directory for
|
|
151
|
+
`node_modules/.bin/oxlint`, so the comparison runs under the consumer's own linter rather than one
|
|
152
|
+
`npx` might fetch. Every difference the run reports has to trace to a decision written down in
|
|
153
|
+
`RULES.md`; one that does not is a defect, not a difference.
|
|
154
|
+
|
|
155
|
+
### What the two configs must say for the findings to match
|
|
156
|
+
|
|
157
|
+
A fork usually had its repo's vocabulary compiled in. Here it is configuration, and a config that
|
|
158
|
+
omits it turns the rule off — which reads as a lost finding, correctly. Carry these across:
|
|
159
|
+
|
|
160
|
+
| Rule | Option | Without it |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `no-brand-names` | `{ brands, allowedSubstrings }` | does nothing |
|
|
163
|
+
| `organelle-dependency` | `{ sharedFeatures }` | only `shared` crosses a feature boundary |
|
|
164
|
+
| `documents-share-one-table` | `{ perKindTables }` | does nothing |
|
|
165
|
+
| `document-sagas-are-generic` | `{ perKindPrefixes }` | does nothing |
|
|
166
|
+
| `step-opens-its-own-cell` | `{ engine, within, wrapper }` | does nothing |
|
|
167
|
+
| `no-raw-html-atoms` | the positional element→atom map | does nothing |
|
|
168
|
+
|
|
169
|
+
Two more facts a fork's config will trip on:
|
|
170
|
+
|
|
171
|
+
- **`cell-handles-are-scoped` is `worker-handles-are-scoped` here.** Same rule; it gave up the word
|
|
172
|
+
"cell" because in this kit a cell is a tier, and a backend rule meaning a D1 database handle by it
|
|
173
|
+
would teach the wrong vocabulary at every diagnostic. Rename the key in the config.
|
|
174
|
+
- **Rule ids keep the `biological-architecture/` prefix whatever the package is called.** The
|
|
175
|
+
namespace is the plugin's identity, not the package's, so renaming or re-scoping the package does
|
|
176
|
+
not rename a single rule id in a single consumer config.
|
|
177
|
+
|
|
71
178
|
## Where to read on
|
|
72
179
|
|
|
73
180
|
- `RULES.md` — every rule, its preset, which source tree it came from, and how each divergence was
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"jsPlugins": [
|
|
4
|
+
"@geonosis/oxlint-plugin-biological-architecture"
|
|
5
|
+
],
|
|
6
|
+
"categories": {
|
|
7
|
+
"correctness": "off",
|
|
8
|
+
"suspicious": "off"
|
|
9
|
+
},
|
|
10
|
+
"rules": {
|
|
11
|
+
"biological-architecture/atom-no-deps": "error",
|
|
12
|
+
"biological-architecture/cell-must-be-stateful": "error",
|
|
13
|
+
"biological-architecture/cell-must-not-compose-cell": "error",
|
|
14
|
+
"biological-architecture/cell-no-tissues": "error",
|
|
15
|
+
"biological-architecture/cells-folder-index-is-barrel": "error",
|
|
16
|
+
"biological-architecture/compound-must-be-stateless": "error",
|
|
17
|
+
"biological-architecture/compound-no-stores": "error",
|
|
18
|
+
"biological-architecture/constants-in-constants-file": "error",
|
|
19
|
+
"biological-architecture/dialect-through-the-seam": "error",
|
|
20
|
+
"biological-architecture/document-sagas-are-generic": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
"perKindPrefixes": [
|
|
24
|
+
"invoice"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"biological-architecture/documents-share-one-table": [
|
|
29
|
+
"error",
|
|
30
|
+
{
|
|
31
|
+
"perKindTables": [
|
|
32
|
+
"invoices",
|
|
33
|
+
"quotes"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"biological-architecture/effect-hook-naming": "error",
|
|
38
|
+
"biological-architecture/max-comment-density": "error",
|
|
39
|
+
"biological-architecture/molecule-atoms-only": "error",
|
|
40
|
+
"biological-architecture/molecule-must-compose": "error",
|
|
41
|
+
"biological-architecture/next-route-segment-is-thin-delegate": "error",
|
|
42
|
+
"biological-architecture/no-brand-names": [
|
|
43
|
+
"error",
|
|
44
|
+
{
|
|
45
|
+
"brands": [
|
|
46
|
+
"acme"
|
|
47
|
+
],
|
|
48
|
+
"allowedSubstrings": [
|
|
49
|
+
"media.acme.com"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"biological-architecture/no-card-shaped-div": "error",
|
|
54
|
+
"biological-architecture/no-cross-feature-stores": "error",
|
|
55
|
+
"biological-architecture/no-d1-transaction": "error",
|
|
56
|
+
"biological-architecture/no-duplicate-jsx-patterns": "error",
|
|
57
|
+
"biological-architecture/no-hook-in-component-disguise": "error",
|
|
58
|
+
"biological-architecture/no-inert-hidden-jsx": "error",
|
|
59
|
+
"biological-architecture/no-inline-data-in-jsx": "error",
|
|
60
|
+
"biological-architecture/no-invalid-feature-folders": "error",
|
|
61
|
+
"biological-architecture/no-logic-in-component-files": "error",
|
|
62
|
+
"biological-architecture/no-orm-outside-db": "error",
|
|
63
|
+
"biological-architecture/no-raw-html-atoms": [
|
|
64
|
+
"error",
|
|
65
|
+
[
|
|
66
|
+
{
|
|
67
|
+
"element": "button",
|
|
68
|
+
"atom": "Button",
|
|
69
|
+
"importPath": "@kit/ui/atoms/button"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
],
|
|
73
|
+
"biological-architecture/no-raw-sql-outside-allowed": "error",
|
|
74
|
+
"biological-architecture/no-react-namespace": "error",
|
|
75
|
+
"biological-architecture/no-renamed-html-props": "error",
|
|
76
|
+
"biological-architecture/no-render-prop-reader": "error",
|
|
77
|
+
"biological-architecture/no-trivial-wrapper-component": "error",
|
|
78
|
+
"biological-architecture/no-ts-in-bio-folders": "error",
|
|
79
|
+
"biological-architecture/no-type-definitions-in-components": "error",
|
|
80
|
+
"biological-architecture/no-void-port": "error",
|
|
81
|
+
"biological-architecture/organelle-dependency": [
|
|
82
|
+
"error",
|
|
83
|
+
{
|
|
84
|
+
"sharedFeatures": [
|
|
85
|
+
"shared"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"biological-architecture/organelle-single-source": "error",
|
|
90
|
+
"biological-architecture/queries-require-org-scope": "error",
|
|
91
|
+
"biological-architecture/queue-loop-is-the-library": "error",
|
|
92
|
+
"biological-architecture/ssot-no-inline-facts": "error",
|
|
93
|
+
"biological-architecture/ssot-no-process-env": "error",
|
|
94
|
+
"biological-architecture/step-opens-its-own-cell": [
|
|
95
|
+
"error",
|
|
96
|
+
{
|
|
97
|
+
"engine": "sagaflow-js",
|
|
98
|
+
"within": "(?:__fixtures__|corpus)/workflows/",
|
|
99
|
+
"wrapper": "(?:__fixtures__|corpus)/workflows/step\\.ts$"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"biological-architecture/store-route-scopes-tenant-data": "error",
|
|
103
|
+
"biological-architecture/tables-declare-their-plane": "error",
|
|
104
|
+
"biological-architecture/tenant-tables-carry-org-id": "error",
|
|
105
|
+
"biological-architecture/time-through-the-door": "error",
|
|
106
|
+
"biological-architecture/tissue-must-compose": "error",
|
|
107
|
+
"biological-architecture/tissue-no-data-props": "error",
|
|
108
|
+
"biological-architecture/tissue-no-hooks": "error",
|
|
109
|
+
"biological-architecture/tissue-no-organelles": "error",
|
|
110
|
+
"biological-architecture/tissue-no-stores": "error",
|
|
111
|
+
"biological-architecture/worker-handles-are-scoped": "error",
|
|
112
|
+
"biological-architecture/zustand-v5-best-practices": "error"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ACCEPTED: global catalog data is the same for every caller — must NOT be flagged.
|
|
2
|
+
export async function GET(req: unknown, res: { json: (b: unknown) => void }): Promise<void> {
|
|
3
|
+
const query = { graph: async (_c: unknown) => ({ data: [] as unknown[] }) }
|
|
4
|
+
const { data } = await query.graph({ entity: 'product_variant', fields: ['id'] })
|
|
5
|
+
res.json({ variants: data })
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// BREACH: reads a customer-owned entity with no caller scoping at all.
|
|
2
|
+
export async function GET(req: unknown, res: { json: (b: unknown) => void }): Promise<void> {
|
|
3
|
+
const query = { graph: async (_c: unknown) => ({ data: [] as unknown[] }) }
|
|
4
|
+
const { data } = await query.graph({ entity: 'order', fields: ['id'] })
|
|
5
|
+
res.json({ orders: data })
|
|
6
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// ACCEPTED: the same read, narrowed to the authenticated caller.
|
|
2
|
+
export async function GET(
|
|
3
|
+
req: { auth_context?: { actor_id?: string } },
|
|
4
|
+
res: { json: (b: unknown) => void },
|
|
5
|
+
): Promise<void> {
|
|
6
|
+
const query = { graph: async (_c: unknown) => ({ data: [] as unknown[] }) }
|
|
7
|
+
const { data } = await query.graph({
|
|
8
|
+
entity: 'order',
|
|
9
|
+
fields: ['id'],
|
|
10
|
+
filters: { customer_id: req.auth_context?.actor_id },
|
|
11
|
+
})
|
|
12
|
+
res.json({ orders: data })
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// A stateless props-in/JSX-out display with no hook, no organelle, no store, and
|
|
2
|
+
// no state-bearing sibling. It is "inert chemistry" filed in the cell tier —
|
|
3
|
+
// cell-must-be-stateful must fire and push it down to compounds/.
|
|
4
|
+
export function BadStatelessDisplay({ label }: { label: string }) {
|
|
5
|
+
return <div className="badge">{label}</div>
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function BadTriplicate() {
|
|
2
|
+
return (
|
|
3
|
+
<div>
|
|
4
|
+
<p className="mb-[4px] text-[12px] leading-[16px] font-bold">a</p>
|
|
5
|
+
<p className="mb-[4px] text-[12px] leading-[16px] font-bold">b</p>
|
|
6
|
+
<p className="mb-[4px] text-[12px] leading-[16px] font-bold">c</p>
|
|
7
|
+
</div>
|
|
8
|
+
)
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Breaches no-type-definitions-in-components: a component .tsx must not declare a
|
|
2
|
+
// type/interface — inline the props in the signature, or move shared types to
|
|
3
|
+
// features/*/lib/types.ts.
|
|
4
|
+
type Label = string
|
|
5
|
+
|
|
6
|
+
export function BadTypeDef({ label }: { label: Label }) {
|
|
7
|
+
return <button type="button">{label}</button>
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LazyBadge = () => <span data-testid="lazy">lazy</span>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const COLOR_MAP = { a: 'x', b: 'y' }
|
|
2
|
+
function privateHelper(n: number) {
|
|
3
|
+
return n * 2
|
|
4
|
+
}
|
|
5
|
+
export function GoodOneExport() {
|
|
6
|
+
return <div data-testid="g">{privateHelper(COLOR_MAP.a.length)}</div>
|
|
7
|
+
}
|
|
8
|
+
export function GoodOneExportRed() {
|
|
9
|
+
return <GoodOneExport />
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
// A genuine cell: it OWNS state (a useState hook). cell-must-be-stateful must
|
|
4
|
+
// stay silent here — this is the living unit the cell tier is for.
|
|
5
|
+
export function GoodStateful() {
|
|
6
|
+
const [open, setOpen] = useState(false)
|
|
7
|
+
return (
|
|
8
|
+
<button onClick={() => setOpen(!open)} type="button">
|
|
9
|
+
{open ? 'on' : 'off'}
|
|
10
|
+
</button>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Breaches compound-must-be-stateless: a compound is pure props-in / JSX-out —
|
|
2
|
+
// no hooks. If you need state, this is a cell or an organelle.
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export function BadStateful() {
|
|
6
|
+
const [open] = useState(false)
|
|
7
|
+
return open ? 'a' : 'b'
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const apiUrl = process.env.API_URL ?? ''
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const widget = 'widgets/ is not a tier'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugin": "biological-architecture",
|
|
3
|
+
"rules": [
|
|
4
|
+
"biological-architecture/atom-no-deps",
|
|
5
|
+
"biological-architecture/cell-must-be-stateful",
|
|
6
|
+
"biological-architecture/cell-must-not-compose-cell",
|
|
7
|
+
"biological-architecture/cell-no-tissues",
|
|
8
|
+
"biological-architecture/cells-folder-index-is-barrel",
|
|
9
|
+
"biological-architecture/compound-must-be-stateless",
|
|
10
|
+
"biological-architecture/compound-no-stores",
|
|
11
|
+
"biological-architecture/constants-in-constants-file",
|
|
12
|
+
"biological-architecture/dialect-through-the-seam",
|
|
13
|
+
"biological-architecture/document-sagas-are-generic",
|
|
14
|
+
"biological-architecture/documents-share-one-table",
|
|
15
|
+
"biological-architecture/effect-hook-naming",
|
|
16
|
+
"biological-architecture/max-comment-density",
|
|
17
|
+
"biological-architecture/molecule-atoms-only",
|
|
18
|
+
"biological-architecture/molecule-must-compose",
|
|
19
|
+
"biological-architecture/next-route-segment-is-thin-delegate",
|
|
20
|
+
"biological-architecture/no-brand-names",
|
|
21
|
+
"biological-architecture/no-card-shaped-div",
|
|
22
|
+
"biological-architecture/no-cross-feature-stores",
|
|
23
|
+
"biological-architecture/no-d1-transaction",
|
|
24
|
+
"biological-architecture/no-duplicate-jsx-patterns",
|
|
25
|
+
"biological-architecture/no-hook-in-component-disguise",
|
|
26
|
+
"biological-architecture/no-inert-hidden-jsx",
|
|
27
|
+
"biological-architecture/no-inline-data-in-jsx",
|
|
28
|
+
"biological-architecture/no-invalid-feature-folders",
|
|
29
|
+
"biological-architecture/no-logic-in-component-files",
|
|
30
|
+
"biological-architecture/no-orm-outside-db",
|
|
31
|
+
"biological-architecture/no-raw-html-atoms",
|
|
32
|
+
"biological-architecture/no-raw-sql-outside-allowed",
|
|
33
|
+
"biological-architecture/no-react-namespace",
|
|
34
|
+
"biological-architecture/no-renamed-html-props",
|
|
35
|
+
"biological-architecture/no-render-prop-reader",
|
|
36
|
+
"biological-architecture/no-trivial-wrapper-component",
|
|
37
|
+
"biological-architecture/no-ts-in-bio-folders",
|
|
38
|
+
"biological-architecture/no-type-definitions-in-components",
|
|
39
|
+
"biological-architecture/no-void-port",
|
|
40
|
+
"biological-architecture/organelle-dependency",
|
|
41
|
+
"biological-architecture/organelle-single-source",
|
|
42
|
+
"biological-architecture/queries-require-org-scope",
|
|
43
|
+
"biological-architecture/queue-loop-is-the-library",
|
|
44
|
+
"biological-architecture/ssot-no-inline-facts",
|
|
45
|
+
"biological-architecture/ssot-no-process-env",
|
|
46
|
+
"biological-architecture/step-opens-its-own-cell",
|
|
47
|
+
"biological-architecture/store-route-scopes-tenant-data",
|
|
48
|
+
"biological-architecture/tables-declare-their-plane",
|
|
49
|
+
"biological-architecture/tenant-tables-carry-org-id",
|
|
50
|
+
"biological-architecture/time-through-the-door",
|
|
51
|
+
"biological-architecture/tissue-must-compose",
|
|
52
|
+
"biological-architecture/tissue-no-data-props",
|
|
53
|
+
"biological-architecture/tissue-no-hooks",
|
|
54
|
+
"biological-architecture/tissue-no-organelles",
|
|
55
|
+
"biological-architecture/tissue-no-stores",
|
|
56
|
+
"biological-architecture/worker-handles-are-scoped",
|
|
57
|
+
"biological-architecture/zustand-v5-best-practices"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Breaches molecule-atoms-only: a molecule is an inert combination of ATOMS —
|
|
2
|
+
// importing a higher-level cell makes it stateful (that's a cell, not a molecule).
|
|
3
|
+
import { ProductCell } from '../cells/product'
|
|
4
|
+
|
|
5
|
+
export function BadCellImport() {
|
|
6
|
+
return <ProductCell />
|
|
7
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Button } from '../atoms/button'
|
|
2
|
+
|
|
3
|
+
// Breaches molecule-must-compose: imports an atom but NEVER renders it (fake
|
|
4
|
+
// composition / lint-theater) — renders only a raw <div>. A molecule is by
|
|
5
|
+
// definition a combination; it must actually render an atom/molecule.
|
|
6
|
+
export function BadPassthrough() {
|
|
7
|
+
return <div>nothing composed</div>
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useForm } from 'react-hook-form'
|
|
2
|
+
|
|
3
|
+
import { useInvoiceStore } from '../stores/invoice-store'
|
|
4
|
+
|
|
5
|
+
export function BadTwoSources() {
|
|
6
|
+
const total = useInvoiceStore((state) => state.total)
|
|
7
|
+
const { register } = useForm()
|
|
8
|
+
return <input {...register('total')} defaultValue={total} />
|
|
9
|
+
}
|