@geonosis/oxlint-plugin-biological-architecture 2.5.0 → 2.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @geonosis/oxlint-plugin-biological-architecture
2
2
 
3
+ ## 2.5.2
4
+
5
+ ## 2.5.1
6
+
3
7
  ## 2.5.0
4
8
 
5
9
  ## 2.4.3
package/dist/index.js CHANGED
@@ -196,6 +196,33 @@ var walkNode = (node, visit) => {
196
196
  }
197
197
  }
198
198
  };
199
+ var FUNCTION_BOUNDARY_TYPES = /* @__PURE__ */ new Set([
200
+ "ArrowFunctionExpression",
201
+ "FunctionDeclaration",
202
+ "FunctionExpression"
203
+ ]);
204
+ var SKIPPED_KEYS = /* @__PURE__ */ new Set(["loc", "parent", "range", "scope"]);
205
+ var walkOwnFunctionBody = (body, visit) => {
206
+ const visited = /* @__PURE__ */ new WeakSet();
207
+ const walk = (node) => {
208
+ if (node === null || typeof node !== "object" || visited.has(node)) return;
209
+ visited.add(node);
210
+ const record2 = node;
211
+ if (node !== body && typeof record2.type === "string" && FUNCTION_BOUNDARY_TYPES.has(record2.type)) {
212
+ return;
213
+ }
214
+ if (typeof record2.type === "string") visit(record2);
215
+ for (const [key, value] of Object.entries(record2)) {
216
+ if (SKIPPED_KEYS.has(key) || value === null || typeof value !== "object") continue;
217
+ if (Array.isArray(value)) {
218
+ for (const item of value) walk(item);
219
+ } else {
220
+ walk(value);
221
+ }
222
+ }
223
+ };
224
+ walk(body);
225
+ };
199
226
  var propertyNameOf = (node) => {
200
227
  if (node.type !== "MemberExpression" || node.computed === true) return null;
201
228
  const property = node.property;
@@ -423,7 +450,22 @@ keeps inside its own body is its own business.`,
423
450
  var boundary_time_is_primitive_default = boundaryTimeIsPrimitive;
424
451
 
425
452
  // src/rules/cell-must-be-stateful.ts
426
- import * as fs from "fs";
453
+ import * as fs2 from "fs";
454
+
455
+ // src/rules/lib/reexports.ts
456
+ import fs from "fs";
457
+ function isReexportOnly(file) {
458
+ try {
459
+ const src = fs.readFileSync(file, "utf8").replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/\/\/.*$/gm, "");
460
+ const hasReexport = /\bexport\b[^;]*\bfrom\b\s*['"]/.test(src);
461
+ const hasComponent = /\bfunction\b|=>|\breturn\b|<[A-Za-z]/.test(src);
462
+ return hasReexport && !hasComponent;
463
+ } catch {
464
+ return false;
465
+ }
466
+ }
467
+
468
+ // src/rules/cell-must-be-stateful.ts
427
469
  var STATE_SIGNATURE = /\buse[A-Z]\w*\s*\(|from\s+['"][^'"]*\/organelles(?:\/|['"])|from\s+['"][^'"]*\/stores?(?:\/|['"])|createContext\s*\(/;
428
470
  var SIBLING_SUFFIXES = [
429
471
  ".tsx",
@@ -438,22 +480,12 @@ var SIBLING_SUFFIXES = [
438
480
  ".steps.tsx"
439
481
  ];
440
482
  var PRIVATE_OR_NON_ENTRY = /\.(parts|sections|layouts|types|icons|sidebar|summary|form|data|steps|stories|test|spec)\.tsx$/;
441
- function isReexportOnly(file) {
442
- try {
443
- const src = fs.readFileSync(file, "utf8").replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/\/\/.*$/gm, "");
444
- const hasReexport = /\bexport\b[^;]*\bfrom\b\s*['"]/.test(src);
445
- const hasComponent = /\bfunction\b|=>|\breturn\b|<[A-Za-z]/.test(src);
446
- return hasReexport && !hasComponent;
447
- } catch {
448
- return false;
449
- }
450
- }
451
483
  function familyHasState(mainFile) {
452
484
  const stem = mainFile.replace(/\.tsx$/, "");
453
485
  for (const suffix of SIBLING_SUFFIXES) {
454
486
  const file = stem + suffix;
455
487
  try {
456
- if (fs.existsSync(file) && STATE_SIGNATURE.test(fs.readFileSync(file, "utf8"))) {
488
+ if (fs2.existsSync(file) && STATE_SIGNATURE.test(fs2.readFileSync(file, "utf8"))) {
457
489
  return true;
458
490
  }
459
491
  } catch {
@@ -1832,18 +1864,24 @@ Composing a stateful primitive makes the file a cell. \`export \u2026 from\` is
1832
1864
  });
1833
1865
  var molecule_atoms_only_default = moleculeAtomsOnly;
1834
1866
 
1835
- // src/rules/molecule-must-compose.ts
1836
- import fs2 from "fs";
1837
- function isReexportOnly2(file) {
1838
- try {
1839
- const src = fs2.readFileSync(file, "utf8").replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/\/\/.*$/gm, "");
1840
- const hasReexport = /\bexport\b[^;]*\bfrom\b\s*['"]/.test(src);
1841
- const hasComponent = /\bfunction\b|=>|\breturn\b|<[A-Za-z]/.test(src);
1842
- return hasReexport && !hasComponent;
1843
- } catch {
1844
- return false;
1867
+ // src/rules/lib/render-tracking.ts
1868
+ var rootTagNameOf = (name) => {
1869
+ if (!name) return void 0;
1870
+ if (name.type === "JSXIdentifier") return name.name;
1871
+ if (name.type === "JSXMemberExpression") return name.object?.name;
1872
+ return void 0;
1873
+ };
1874
+ var collectCompositionLocals = (node, isComposition, into) => {
1875
+ if (node.importKind === "type") return;
1876
+ if (!isComposition(node.source.value)) return;
1877
+ for (const spec of node.specifiers ?? []) {
1878
+ const localName = spec.local?.name;
1879
+ if (localName) into.add(localName);
1845
1880
  }
1846
- }
1881
+ };
1882
+ var rendersAnyOf = (locals, renderedTagNames) => [...locals].some((local) => renderedTagNames.has(local));
1883
+
1884
+ // src/rules/molecule-must-compose.ts
1847
1885
  function isCompositionSource(src) {
1848
1886
  if (/\/(atoms|molecules)(\/|$)/.test(src)) return true;
1849
1887
  if (/^\.\/[^/]+$/.test(src)) return true;
@@ -1856,38 +1894,21 @@ var moleculeMustCompose = {
1856
1894
  const filename = context.filename;
1857
1895
  const isMolecule = /\/molecules\/(?!index\.)[^/]+\.tsx?$/.test(filename);
1858
1896
  if (!isMolecule) return {};
1859
- if (isReexportOnly2(filename)) return {};
1897
+ if (isReexportOnly(filename)) return {};
1860
1898
  const bioImportLocals = /* @__PURE__ */ new Set();
1861
1899
  const renderedTagNames = /* @__PURE__ */ new Set();
1862
1900
  let programNode = null;
1863
1901
  return {
1864
1902
  ImportDeclaration(node) {
1865
- if (node.importKind === "type") return;
1866
- const source = node.source.value;
1867
- if (!isCompositionSource(source)) return;
1868
- for (const spec of node.specifiers ?? []) {
1869
- const localName = spec.local?.name;
1870
- if (localName) bioImportLocals.add(localName);
1871
- }
1903
+ collectCompositionLocals(node, isCompositionSource, bioImportLocals);
1872
1904
  },
1873
1905
  JSXOpeningElement(node) {
1874
- const name = node.name;
1875
- if (!name) return;
1876
- if (name.type === "JSXIdentifier") {
1877
- const id = name;
1878
- if (id.name) renderedTagNames.add(id.name);
1879
- } else if (name.type === "JSXMemberExpression") {
1880
- const member = name;
1881
- const root = member.object?.name;
1882
- if (root) renderedTagNames.add(root);
1883
- }
1906
+ const root = rootTagNameOf(node.name);
1907
+ if (root) renderedTagNames.add(root);
1884
1908
  },
1885
1909
  "Program:exit"(node) {
1886
1910
  programNode = node;
1887
- const composesByRender = [...bioImportLocals].some(
1888
- (localName) => renderedTagNames.has(localName)
1889
- );
1890
- if (!composesByRender) {
1911
+ if (!rendersAnyOf(bioImportLocals, renderedTagNames)) {
1891
1912
  const shortName = filename.split("/").pop() || filename;
1892
1913
  context.report({
1893
1914
  data: { filename: shortName },
@@ -2089,32 +2110,11 @@ var FALLBACK_BASENAMES = /* @__PURE__ */ new Set([
2089
2110
  "loading.tsx",
2090
2111
  "not-found.tsx"
2091
2112
  ]);
2092
- var SKIP_KEYS = /* @__PURE__ */ new Set(["parent", "loc", "range", "scope"]);
2093
2113
  function collectReturnStatements(body) {
2094
2114
  const out = [];
2095
- const visited = /* @__PURE__ */ new WeakSet();
2096
- function walk(node) {
2097
- if (!node || typeof node !== "object") return;
2098
- if (visited.has(node)) return;
2099
- visited.add(node);
2100
- const n = node;
2101
- if (node !== body && (n.type === "FunctionDeclaration" || n.type === "FunctionExpression" || n.type === "ArrowFunctionExpression")) {
2102
- return;
2103
- }
2104
- if (n.type === "ReturnStatement") {
2105
- out.push(node);
2106
- }
2107
- for (const key of Object.keys(n)) {
2108
- if (SKIP_KEYS.has(key)) continue;
2109
- const v = n[key];
2110
- if (Array.isArray(v)) {
2111
- for (const item of v) walk(item);
2112
- } else if (v && typeof v === "object") {
2113
- walk(v);
2114
- }
2115
- }
2116
- }
2117
- walk(body);
2115
+ walkOwnFunctionBody(body, (node) => {
2116
+ if (node.type === "ReturnStatement") out.push(node);
2117
+ });
2118
2118
  return out;
2119
2119
  }
2120
2120
  function tagOf(el) {
@@ -3978,37 +3978,13 @@ var ROUTE_BASENAMES = /* @__PURE__ */ new Set([
3978
3978
  "template.tsx"
3979
3979
  ]);
3980
3980
  var R3F_PACKAGES2 = /* @__PURE__ */ new Set(["@react-three/drei", "@react-three/fiber"]);
3981
- var WALK_SKIP_KEYS = /* @__PURE__ */ new Set(["parent", "loc", "range", "scope"]);
3982
3981
  function functionHasHookCall(body) {
3983
3982
  let found = false;
3984
- const visited = /* @__PURE__ */ new WeakSet();
3985
- function walk(node) {
3986
- if (found) return;
3987
- if (!node || typeof node !== "object") return;
3988
- if (visited.has(node)) return;
3989
- visited.add(node);
3990
- const n = node;
3991
- if (n.type === "FunctionDeclaration" || n.type === "FunctionExpression" || n.type === "ArrowFunctionExpression") {
3992
- if (node !== body) return;
3993
- }
3994
- if (n.type === "CallExpression") {
3995
- const callee = n.callee;
3996
- if (callee?.type === "Identifier" && callee.name && /^use[A-Z]/.test(callee.name)) {
3997
- found = true;
3998
- return;
3999
- }
4000
- }
4001
- for (const key of Object.keys(n)) {
4002
- if (WALK_SKIP_KEYS.has(key)) continue;
4003
- const v = n[key];
4004
- if (Array.isArray(v)) {
4005
- for (const item of v) walk(item);
4006
- } else if (v && typeof v === "object") {
4007
- walk(v);
4008
- }
4009
- }
4010
- }
4011
- walk(body);
3983
+ walkOwnFunctionBody(body, (node) => {
3984
+ if (found || node.type !== "CallExpression") return;
3985
+ const callee = node.callee;
3986
+ if (callee?.type === "Identifier" && callee.name && /^use[A-Z]/.test(callee.name)) found = true;
3987
+ });
4012
3988
  return found;
4013
3989
  }
4014
3990
  function hasMeaningfulChildren(el) {
@@ -6240,7 +6216,6 @@ export const clock = (): unknown => theClock
6240
6216
  var time_through_the_door_default = timeThroughTheDoor;
6241
6217
 
6242
6218
  // src/rules/tissue-must-compose.ts
6243
- import fs4 from "fs";
6244
6219
  var COMPOSITION_PATTERNS = [
6245
6220
  // Tier segment with a trailing slash OR at the end of the specifier, so both
6246
6221
  // relative (`../cells/x`, `../../feature/cells/x`) and self-subpath feature
@@ -6254,16 +6229,6 @@ var COMPOSITION_PATTERNS = [
6254
6229
  // real composition, not lint-theater.
6255
6230
  /\.(parts|sections|layouts)$/
6256
6231
  ];
6257
- function isReexportOnly3(file) {
6258
- try {
6259
- const src = fs4.readFileSync(file, "utf8").replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/\/\/.*$/gm, "");
6260
- const hasReexport = /\bexport\b[^;]*\bfrom\b\s*['"]/.test(src);
6261
- const hasComponent = /\bfunction\b|=>|\breturn\b|<[A-Za-z]/.test(src);
6262
- return hasReexport && !hasComponent;
6263
- } catch {
6264
- return false;
6265
- }
6266
- }
6267
6232
  var APP_ROUTE_MUST_COMPOSE = /* @__PURE__ */ new Set(["page.tsx", "layout.tsx", "template.tsx"]);
6268
6233
  var APP_ROUTE_FALLBACKS = /* @__PURE__ */ new Set([
6269
6234
  "default.tsx",
@@ -6292,7 +6257,7 @@ var tissueMustCompose = {
6292
6257
  const isAppFallback = /\/app\//.test(normalized) && APP_ROUTE_FALLBACKS.has(basename);
6293
6258
  if (isAppFallback) return {};
6294
6259
  if (!isTissue && !isAppRouteFile) return {};
6295
- if (isReexportOnly3(filename)) return {};
6260
+ if (isReexportOnly(filename)) return {};
6296
6261
  const bioImportLocals = /* @__PURE__ */ new Set();
6297
6262
  const renderedTagNames = /* @__PURE__ */ new Set();
6298
6263
  let hasMetadataExport = false;
@@ -6316,25 +6281,15 @@ var tissueMustCompose = {
6316
6281
  }
6317
6282
  },
6318
6283
  ImportDeclaration(node) {
6319
- if (node.importKind === "type") return;
6320
- const source = node.source.value;
6321
- if (!COMPOSITION_PATTERNS.some((pattern) => pattern.test(source))) return;
6322
- for (const spec of node.specifiers ?? []) {
6323
- const localName = spec.local?.name;
6324
- if (localName) bioImportLocals.add(localName);
6325
- }
6284
+ collectCompositionLocals(
6285
+ node,
6286
+ (source) => COMPOSITION_PATTERNS.some((pattern) => pattern.test(source)),
6287
+ bioImportLocals
6288
+ );
6326
6289
  },
6327
6290
  JSXOpeningElement(node) {
6328
- const name = node.name;
6329
- if (!name) return;
6330
- if (name.type === "JSXIdentifier") {
6331
- const id = name;
6332
- if (id.name) renderedTagNames.add(id.name);
6333
- } else if (name.type === "JSXMemberExpression") {
6334
- const member = name;
6335
- const root = member.object?.name;
6336
- if (root) renderedTagNames.add(root);
6337
- }
6291
+ const root = rootTagNameOf(node.name);
6292
+ if (root) renderedTagNames.add(root);
6338
6293
  },
6339
6294
  "Program:exit"(node) {
6340
6295
  programNode = node;
@@ -6345,10 +6300,7 @@ var tissueMustCompose = {
6345
6300
  if (basename === "page.tsx" && isTrulyEmpty) {
6346
6301
  return;
6347
6302
  }
6348
- const composesByRender = [...bioImportLocals].some(
6349
- (localName) => renderedTagNames.has(localName)
6350
- );
6351
- if (!composesByRender) {
6303
+ if (!rendersAnyOf(bioImportLocals, renderedTagNames)) {
6352
6304
  const shortName = normalized.split("/").pop() || normalized;
6353
6305
  const reason = bioImportLocals.size === 0 ? "noBioImports" : "bioImportedButNotRendered";
6354
6306
  context.report({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/oxlint-plugin-biological-architecture",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "types": "./dist/index.d.ts",
5
5
  "description": "Biological tier architecture as lint — the union of the dielime and during.day rule sets, shipped as presets.",
6
6
  "keywords": [
@@ -45,7 +45,7 @@
45
45
  "oxlint": ">=1.77"
46
46
  },
47
47
  "devDependencies": {
48
- "@geonosis/lint-parity": "2.5.0"
48
+ "@geonosis/lint-parity": "2.5.2"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=22"