@geonosis/oxlint-plugin-biological-architecture 0.4.0 → 0.5.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/README.md +34 -0
- package/corpus/.oxlintrc.json +8 -0
- package/corpus/atoms/bad-aliased-import.tsx +4 -0
- package/corpus/atoms/nested/bad-relative-climb.tsx +4 -0
- package/corpus/features/invoice/cells/bad-aliased-cross-feature-cell.tsx +3 -0
- package/corpus/features/invoice/cells/bad-aliased-cross-feature-organelle.tsx +11 -0
- package/corpus/features/invoice/cells/bad-aliased-cross-feature-tissue.tsx +3 -0
- package/corpus/features/invoice/cells/bad-relative-cross-feature-cell.tsx +3 -0
- package/corpus/features/invoice/cells/bad-relative-cross-feature-organelle.tsx +12 -0
- package/corpus/features/invoice/cells/bad-relative-cross-feature-tissue.tsx +3 -0
- package/corpus/features/invoice/cells/good-own-organelle.tsx +10 -0
- package/corpus/features/invoice/cells/good-shared-organelle.tsx +10 -0
- package/corpus/features/invoice/compounds/bad-aliased-cross-feature-store.tsx +3 -0
- package/corpus/features/invoice/compounds/bad-relative-cross-feature-store.tsx +5 -0
- package/corpus/features/invoice/organelles/bad-aliased-cross-feature-organelle.tsx +10 -0
- package/corpus/features/invoice/tissues/bad-aliased-cross-feature-organelle.tsx +3 -0
- package/corpus/features/invoice/tissues/bad-aliased-cross-feature-store.tsx +3 -0
- package/corpus/features/invoice/tissues/bad-relative-cross-feature-organelle.tsx +3 -0
- package/corpus/features/invoice/tissues/bad-relative-cross-feature-store.tsx +3 -0
- package/corpus/manifest.json +1 -0
- package/corpus/molecules/bad-aliased-cell-import.tsx +3 -0
- package/corpus/molecules/nested/bad-relative-climb.tsx +3 -0
- package/dist/{chunk-55NK7NJN.js → chunk-RQQKQPIU.js} +1 -0
- package/dist/index.js +43 -3
- package/dist/presets.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ months. Carry these across, or turn the rule off:
|
|
|
135
135
|
| --- | --- | --- |
|
|
136
136
|
| `no-brand-names` | `{ brands, allowedSubstrings }` | refuses the run |
|
|
137
137
|
| `organelle-dependency` | `{ sharedFeatures }` | only `shared` crosses a feature boundary |
|
|
138
|
+
| `cell-no-cross-feature-organelles` | `{ sharedFeatures }` | only `shared` crosses a feature boundary |
|
|
138
139
|
| `documents-share-one-table` | `{ perKindTables }` | refuses the run |
|
|
139
140
|
| `document-sagas-are-generic` | `{ perKindPrefixes }` | refuses the run |
|
|
140
141
|
| `step-opens-its-own-cell` | `{ engine, within, wrapper }` | refuses the run |
|
|
@@ -153,6 +154,39 @@ Two more facts a fork's config will trip on:
|
|
|
153
154
|
namespace is the plugin's identity, not the package's, so renaming or re-scoping the package does
|
|
154
155
|
not rename a single rule id in a single consumer config.
|
|
155
156
|
|
|
157
|
+
### Last step of every bump — verify the loaded version, per workspace
|
|
158
|
+
|
|
159
|
+
**Declared is not loaded.** oxlint resolves a `jsPlugins` specifier from the CONFIG FILE's
|
|
160
|
+
directory, so a nested `apps/<ws>/node_modules/@geonosis/…` left behind at the old version means
|
|
161
|
+
oxlint runs the OLD plugin while every `package.json` and the lockfile say the new one. bun leaves
|
|
162
|
+
a nested copy when a per-workspace `bun add` runs and the root already has one; one consumer measured
|
|
163
|
+
a whole bug report against 0.3.0 that way, on a repo pinned to 0.4.0.
|
|
164
|
+
|
|
165
|
+
The check that is true on every package manager is the one that asks the resolver, from **each
|
|
166
|
+
workspace directory** — this is what oxlint would load:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
node --input-type=module -e \
|
|
170
|
+
'const p = (await import("@geonosis/oxlint-plugin-biological-architecture")).default; console.log(p.meta.version)'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
It must print the pinned version. Every published version of this plugin carries `meta.version`, so
|
|
174
|
+
there is no version of it this check cannot read.
|
|
175
|
+
|
|
176
|
+
Then find the stray copy, and mind that the answer is package-manager-specific:
|
|
177
|
+
|
|
178
|
+
- **bun / npm** — real directories, so counting them is meaningful:
|
|
179
|
+
```bash
|
|
180
|
+
find . -path "*@geonosis/oxlint-plugin-biological-architecture/package.json" -not -path "*/.git/*"
|
|
181
|
+
```
|
|
182
|
+
More than one is the bug. Fix with
|
|
183
|
+
`rm -rf apps/*/node_modules/@geonosis packages/*/node_modules/@geonosis && bun install` — no flags,
|
|
184
|
+
and never `--ignore-scripts` over the whole tree.
|
|
185
|
+
- **pnpm** — `node_modules/@geonosis/*` are SYMLINKS into `.pnpm/`, which keeps every version ever
|
|
186
|
+
installed. The same `find` answers 7 on a tree that is perfectly correct, and 0 if you exclude
|
|
187
|
+
`.pnpm`. Read the links instead: `ls -l node_modules/@geonosis` in each workspace, and check the
|
|
188
|
+
version in the target path.
|
|
189
|
+
|
|
156
190
|
## Where to read on
|
|
157
191
|
|
|
158
192
|
- `RULES.md` — every rule, its preset, which source tree it came from, and how each divergence was
|
package/corpus/.oxlintrc.json
CHANGED
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
"biological-architecture/atom-no-deps": "error",
|
|
12
12
|
"biological-architecture/cell-must-be-stateful": "error",
|
|
13
13
|
"biological-architecture/cell-must-not-compose-cell": "error",
|
|
14
|
+
"biological-architecture/cell-no-cross-feature-organelles": [
|
|
15
|
+
"error",
|
|
16
|
+
{
|
|
17
|
+
"sharedFeatures": [
|
|
18
|
+
"shared"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
14
22
|
"biological-architecture/cell-no-tissues": "error",
|
|
15
23
|
"biological-architecture/cells-folder-index-is-barrel": "error",
|
|
16
24
|
"biological-architecture/compound-must-be-stateless": "error",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The same breach spelled through the bundler alias. Where the import LANDS is the fact; the two
|
|
2
|
+
// spellings must read alike.
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import { BillingTotals } from '@/features/billing/organelles/billing-totals'
|
|
6
|
+
|
|
7
|
+
export function BadAliasedCrossFeatureOrganelle() {
|
|
8
|
+
const [open] = useState(false)
|
|
9
|
+
|
|
10
|
+
return <section>{open ? <BillingTotals /> : null}</section>
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// A cell climbing out of its feature into ANOTHER feature's organelle — the state compartment of a
|
|
2
|
+
// feature this one does not own. organelle-dependency polices organelles/ and no-cross-feature-stores
|
|
3
|
+
// polices stores, so until cell-no-cross-feature-organelles nothing here fired at all.
|
|
4
|
+
import { useState } from 'react'
|
|
5
|
+
|
|
6
|
+
import { BillingTotals } from '../../billing/organelles/billing-totals'
|
|
7
|
+
|
|
8
|
+
export function BadRelativeCrossFeatureOrganelle() {
|
|
9
|
+
const [open] = useState(false)
|
|
10
|
+
|
|
11
|
+
return <section>{open ? <BillingTotals /> : null}</section>
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// A cell hosting its OWN feature's organelle — the whole point of a cell.
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import { InvoiceLines } from '../organelles/invoice-lines'
|
|
5
|
+
|
|
6
|
+
export function GoodOwnOrganelle() {
|
|
7
|
+
const [open] = useState(false)
|
|
8
|
+
|
|
9
|
+
return <section>{open ? <InvoiceLines /> : null}</section>
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The shared feature is infrastructure every feature may read, which is what sharedFeatures names.
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import { SaveIndicator } from '@/features/shared/organelles/save-indicator'
|
|
5
|
+
|
|
6
|
+
export function GoodSharedOrganelle() {
|
|
7
|
+
const [open] = useState(false)
|
|
8
|
+
|
|
9
|
+
return <section>{open ? <SaveIndicator /> : null}</section>
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The aliased twin of bad-relative-cross-feature-organelle.tsx: one breach, two spellings.
|
|
2
|
+
import { BillingTotals } from '@/features/billing/organelles/billing-totals'
|
|
3
|
+
|
|
4
|
+
export function BadAliasedCrossFeatureOrganelle() {
|
|
5
|
+
return (
|
|
6
|
+
<section>
|
|
7
|
+
<BillingTotals />
|
|
8
|
+
</section>
|
|
9
|
+
)
|
|
10
|
+
}
|
package/corpus/manifest.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"biological-architecture/atom-no-deps",
|
|
5
5
|
"biological-architecture/cell-must-be-stateful",
|
|
6
6
|
"biological-architecture/cell-must-not-compose-cell",
|
|
7
|
+
"biological-architecture/cell-no-cross-feature-organelles",
|
|
7
8
|
"biological-architecture/cell-no-tissues",
|
|
8
9
|
"biological-architecture/cells-folder-index-is-barrel",
|
|
9
10
|
"biological-architecture/compound-must-be-stateless",
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS,
|
|
3
3
|
rulesOfPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RQQKQPIU.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -271,6 +271,45 @@ siblings is the shape \`cells-folder-index-is-barrel\` requires of them.`,
|
|
|
271
271
|
});
|
|
272
272
|
var cell_must_not_compose_cell_default = cellMustNotComposeCell;
|
|
273
273
|
|
|
274
|
+
// src/rules/cell-no-cross-feature-organelles.ts
|
|
275
|
+
var DEFAULT_SHARED_FEATURES = ["shared"];
|
|
276
|
+
var FEATURE_ORGANELLE = /features\/([^/]+)\/organelles\//;
|
|
277
|
+
var organelleFeatureOf = (path2) => FEATURE_ORGANELLE.exec(path2)?.[1];
|
|
278
|
+
var cellNoCrossFeatureOrganelles = directionRule({
|
|
279
|
+
data: (target, importer) => ({
|
|
280
|
+
file: importer.file,
|
|
281
|
+
importedFeature: organelleFeatureOf(target.path) ?? "",
|
|
282
|
+
ownFeature: importer.feature ?? "",
|
|
283
|
+
source: target.source
|
|
284
|
+
}),
|
|
285
|
+
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.",
|
|
286
|
+
fixShape: `A cell hosts its OWN feature's organelles. Another feature's organelle is that feature's state
|
|
287
|
+
compartment, and a cell that imports one couples two features through a hook instead of through
|
|
288
|
+
props \u2014 take the value as a prop and let the tissue above arrange both, or move the organelle into a
|
|
289
|
+
feature every cell may read (the rule's \`sharedFeatures\` option, default \`shared\`). Relative and
|
|
290
|
+
aliased sources are judged alike: \`../../billing/organelles/totals\` lands where
|
|
291
|
+
\`@/features/billing/organelles/totals\` lands. Type-only imports are exempt; \`export \u2026 from\` is not.`,
|
|
292
|
+
forbidden: (target, importer, options) => {
|
|
293
|
+
const importedFeature = organelleFeatureOf(target.path);
|
|
294
|
+
return importedFeature !== void 0 && importedFeature !== importer.feature && !new Set(options.sharedFeatures ?? DEFAULT_SHARED_FEATURES).has(importedFeature);
|
|
295
|
+
},
|
|
296
|
+
messages: {
|
|
297
|
+
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.`
|
|
298
|
+
},
|
|
299
|
+
optionsFrom: (raw) => raw ?? {},
|
|
300
|
+
scope: "cells",
|
|
301
|
+
schema: [
|
|
302
|
+
{
|
|
303
|
+
additionalProperties: false,
|
|
304
|
+
properties: {
|
|
305
|
+
sharedFeatures: { items: { type: "string" }, type: "array" }
|
|
306
|
+
},
|
|
307
|
+
type: "object"
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
});
|
|
311
|
+
var cell_no_cross_feature_organelles_default = cellNoCrossFeatureOrganelles;
|
|
312
|
+
|
|
274
313
|
// src/rules/cell-no-tissues.ts
|
|
275
314
|
var HIGHER_LEVEL_PATTERNS = [
|
|
276
315
|
/(?:features\/[^/]+\/)?tissues\//,
|
|
@@ -2622,7 +2661,7 @@ the engine that catches it is not a port and is unaffected.`,
|
|
|
2622
2661
|
var no_void_port_default = noVoidPort;
|
|
2623
2662
|
|
|
2624
2663
|
// src/rules/organelle-dependency.ts
|
|
2625
|
-
var
|
|
2664
|
+
var DEFAULT_SHARED_FEATURES2 = ["shared"];
|
|
2626
2665
|
var ABOVE_AN_ORGANELLE = [
|
|
2627
2666
|
/\/cells\//,
|
|
2628
2667
|
/\/tissues\//,
|
|
@@ -2640,7 +2679,7 @@ Cross-feature organelle imports are allowed only from the features named in the
|
|
|
2640
2679
|
forbidden: (target, importer, options) => {
|
|
2641
2680
|
if (ABOVE_AN_ORGANELLE.some((pattern) => pattern.test(target.path))) return true;
|
|
2642
2681
|
const importedFeature = CROSS_FEATURE_ORGANELLE.exec(target.path)?.[1];
|
|
2643
|
-
return importedFeature !== void 0 && importedFeature !== importer.feature && !new Set(options.sharedFeatures ??
|
|
2682
|
+
return importedFeature !== void 0 && importedFeature !== importer.feature && !new Set(options.sharedFeatures ?? DEFAULT_SHARED_FEATURES2).has(importedFeature);
|
|
2644
2683
|
},
|
|
2645
2684
|
messages: {
|
|
2646
2685
|
forbidden: 'Organelle "{{file}}" cannot import from "{{source}}". Organelles may depend on atoms, molecules, compounds, stores, domains, and sub-organelles (same feature or shared/document/charts).'
|
|
@@ -3871,6 +3910,7 @@ var rules = {
|
|
|
3871
3910
|
"atom-no-deps": atom_no_deps_default,
|
|
3872
3911
|
"cell-must-be-stateful": cell_must_be_stateful_default,
|
|
3873
3912
|
"cell-must-not-compose-cell": cell_must_not_compose_cell_default,
|
|
3913
|
+
"cell-no-cross-feature-organelles": cell_no_cross_feature_organelles_default,
|
|
3874
3914
|
"cell-no-tissues": cell_no_tissues_default,
|
|
3875
3915
|
"cells-folder-index-is-barrel": cells_folder_index_is_barrel_default,
|
|
3876
3916
|
"compound-must-be-stateless": compound_must_be_stateless_default,
|
package/dist/presets.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/oxlint-plugin-biological-architecture",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Biological tier architecture as lint — the union of the dielime and during.day rule sets, shipped as presets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oxlint",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"oxlint": ">=1.77"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@geonosis/lint-parity": "0.
|
|
36
|
+
"@geonosis/lint-parity": "0.5.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=22"
|