@geonosis/oxlint-plugin-biological-architecture 2.6.1 → 2.8.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 +59 -0
- package/README.md +0 -2
- package/RULES.md +12 -39
- package/corpus/.oxlintrc.json +0 -18
- package/corpus/manifest.json +0 -2
- package/dist/{chunk-KV7JRNAP.js → chunk-24K5232K.js} +0 -2
- package/dist/index.d.ts +11 -1
- package/dist/index.js +221 -262
- package/dist/presets.js +1 -1
- package/package.json +2 -2
- package/corpus/domains/document/workflows/bad-per-kind-saga.ts +0 -3
- package/corpus/domains/document/workflows/bad-per-kind-workflow.ts +0 -5
- package/corpus/domains/document/workflows/good-minted-workflow.ts +0 -8
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
PRESUMED_ENGINES,
|
|
4
4
|
RENAMED_TO,
|
|
5
5
|
rulesOfPreset
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-24K5232K.js";
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { createRequire } from "module";
|
|
@@ -257,16 +257,6 @@ var requireOption = (rule, value, option, accepts) => {
|
|
|
257
257
|
}
|
|
258
258
|
return value;
|
|
259
259
|
};
|
|
260
|
-
var requireOptionHere = (rule, value, option, file, accepts) => {
|
|
261
|
-
if (isMissing(value)) {
|
|
262
|
-
throw new OptionMissing(
|
|
263
|
-
rule,
|
|
264
|
-
option,
|
|
265
|
-
`${refusalHeader(rule, option)} It is enabled over "${file}", which it would judge, and its \`${option}\` option is ${NOTHING}. Configure \`${option}\`, or turn the rule off for this tree.${accepts === void 0 ? "" : ` It accepts ${accepts}.`}`
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
return value;
|
|
269
|
-
};
|
|
270
260
|
var raisedOver = /* @__PURE__ */ new Map();
|
|
271
261
|
var fileOf2 = (context) => {
|
|
272
262
|
const named = context?.filename;
|
|
@@ -548,7 +538,12 @@ var cellMustNotComposeCell = directionRule({
|
|
|
548
538
|
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.",
|
|
549
539
|
fixShape: `A cell never composes another cell \u2014 not same-feature, not \`shared/\`, no exception. If you want to,
|
|
550
540
|
one of the two is misclassified: arrangement goes up to a \`tissues/\` file, a stateless display goes
|
|
551
|
-
down to \`compounds/\`, a store adapter goes sideways to \`organelles/\`.
|
|
541
|
+
down to \`compounds/\`, a store adapter goes sideways to \`organelles/\`. A parent that holds state AND
|
|
542
|
+
hosts a cell (a summary card that opens a modal-hosted form) is two cells side by side: the child
|
|
543
|
+
hosts its own modal and reads whether it is open from the store, the parent writes it, and a
|
|
544
|
+
\`tissues/\` file arranges both. Cells coordinate through a store, never through props from one to
|
|
545
|
+
the other. A stateless adapter between cells is a tissue \u2014 it may take number and boolean layout
|
|
546
|
+
configuration with a literal default (\`step = 10\`). Private siblings of the SAME
|
|
552
547
|
component (\`invoice-editor.parts.tsx\` from \`invoice-editor.tsx\`) are one cell and are fine. The
|
|
553
548
|
barrels \`cells/index.ts\`, \`cells/dynamic.ts\` and \`cells/dynamic.tsx\` are exempt \u2014 re-exporting
|
|
554
549
|
siblings is the shape \`cells-folder-index-is-barrel\` requires of them.`,
|
|
@@ -884,155 +879,6 @@ configs. Everywhere else, use the dialect-free aliases the seam exports (\`Datab
|
|
|
884
879
|
};
|
|
885
880
|
var dialect_through_the_seam_default = dialectThroughTheSeam;
|
|
886
881
|
|
|
887
|
-
// src/rules/document-sagas-are-generic.ts
|
|
888
|
-
var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
889
|
-
var RULE2 = "document-sagas-are-generic";
|
|
890
|
-
var DEFAULT_FACTORIES = ["saga", "createWorkflow"];
|
|
891
|
-
var perEntityNameOf = (node, factories) => {
|
|
892
|
-
for (const factory of factories) {
|
|
893
|
-
const name = literalNamedBy(node, factory);
|
|
894
|
-
if (name !== null) return name;
|
|
895
|
-
}
|
|
896
|
-
return null;
|
|
897
|
-
};
|
|
898
|
-
var declaresAFlow = (text, factories) => factories.some((factory) => new RegExp(`\\b${escapeForRegex(factory)}\\s*\\(`).test(text));
|
|
899
|
-
var documentSagasAreGeneric = {
|
|
900
|
-
create(context) {
|
|
901
|
-
const factories = requireOption(
|
|
902
|
-
RULE2,
|
|
903
|
-
context.options?.[0]?.factories ?? DEFAULT_FACTORIES,
|
|
904
|
-
"factories"
|
|
905
|
-
);
|
|
906
|
-
const filename = normalise(context.filename);
|
|
907
|
-
const named = context.options?.[0]?.within;
|
|
908
|
-
const insideTree = named === void 0 || named === "" ? void 0 : new RegExp(named).test(filename);
|
|
909
|
-
if (insideTree === false) return {};
|
|
910
|
-
if (insideTree === void 0 && !declaresAFlow(context.sourceCode?.text ?? "", factories)) {
|
|
911
|
-
return {};
|
|
912
|
-
}
|
|
913
|
-
const prefixes = requireOptionHere(
|
|
914
|
-
RULE2,
|
|
915
|
-
context.options?.[0]?.perKindPrefixes,
|
|
916
|
-
"perKindPrefixes",
|
|
917
|
-
filename
|
|
918
|
-
);
|
|
919
|
-
const within = requireOptionHere(RULE2, named, "within", filename);
|
|
920
|
-
if (!new RegExp(within).test(filename)) return {};
|
|
921
|
-
const perKindPrefix = new RegExp(`^(?:${prefixes.map(escapeForRegex).join("|")})\\.`);
|
|
922
|
-
return {
|
|
923
|
-
CallExpression(node) {
|
|
924
|
-
const name = perEntityNameOf(node, factories);
|
|
925
|
-
if (name === null || !perKindPrefix.test(name)) return;
|
|
926
|
-
context.report({ data: { name }, messageId: "perKindSaga", node });
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
},
|
|
930
|
-
fixShape: `A saga named for one document kind (\`invoice.issue\`, \`quote.accept\`) is the hand-written fork the
|
|
931
|
-
generic base entity replaced. Mint it from the generic factory \u2014 \`documentSagas(entity, effects)\` \u2014
|
|
932
|
-
and pass the kind as data. The same holds for a workflow: \`createWorkflow('invoice.create', \u2026)\` is
|
|
933
|
-
the fork, \`documentWorkflows(entity)\` is the mint. A name assembled from the entity \u2014 a template
|
|
934
|
-
literal \u2014 is what minting looks like and is left alone by construction. Configure the kind vocabulary
|
|
935
|
-
through \`perKindPrefixes\`, the tree that holds the flows through \`within\` (a regex over the
|
|
936
|
-
filename), and \`factories\` if the repo declares them under other names; enabled without
|
|
937
|
-
\`perKindPrefixes\` or \`within\` the rule refuses the run.`,
|
|
938
|
-
meta: {
|
|
939
|
-
floor: { name: "document", pending: true },
|
|
940
|
-
docs: {
|
|
941
|
-
description: "Document sagas are minted, not written: `documentSagas(entity, effects)` gives every entity that extends Document its create/save/setStatus/remove/send/convert/duplicate/share from one body. A saga or workflow named with a LITERAL for one entity is a fork; a name assembled from the entity is the mint and is unreadable here on purpose. `factories`, `perKindPrefixes` and `within` are options."
|
|
942
|
-
},
|
|
943
|
-
messages: {
|
|
944
|
-
perKindSaga: '"{{name}}" is a hand-written flow for one entity. Every entity that extends Document gets its sagas from `documentSagas(entity, effects)` and its workflows from the same mint; put what this entity decides in its `Document.extend` definition or in its `DocumentEffects`.'
|
|
945
|
-
},
|
|
946
|
-
schema: [
|
|
947
|
-
{
|
|
948
|
-
additionalProperties: false,
|
|
949
|
-
properties: {
|
|
950
|
-
factories: { items: { type: "string" }, type: "array" },
|
|
951
|
-
perKindPrefixes: { items: { type: "string" }, type: "array" },
|
|
952
|
-
within: { type: "string" }
|
|
953
|
-
},
|
|
954
|
-
type: "object"
|
|
955
|
-
}
|
|
956
|
-
],
|
|
957
|
-
type: "problem"
|
|
958
|
-
},
|
|
959
|
-
/** Every half comes from the options now: the vocabulary, the tree, and the factory it is minted by. */
|
|
960
|
-
probe(options) {
|
|
961
|
-
const { perKindPrefixes = [], within = "" } = bagOf(options);
|
|
962
|
-
const factory = firstOf(bagOf(options).factories, DEFAULT_FACTORIES);
|
|
963
|
-
return {
|
|
964
|
-
path: pathMatching(within, "geonosis-probe-saga.ts"),
|
|
965
|
-
source: `import { ${factory} } from 'sagaflow-js'
|
|
966
|
-
|
|
967
|
-
export const geonosisProbe = ${factory}('${perKindPrefixes[0] ?? ""}.geonosis-probe', {})
|
|
968
|
-
`
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
var document_sagas_are_generic_default = documentSagasAreGeneric;
|
|
973
|
-
|
|
974
|
-
// src/rules/documents-share-one-table.ts
|
|
975
|
-
var TABLE_CALLEES = ["sqliteTable", "pgTable"];
|
|
976
|
-
var documentsShareOneTable = {
|
|
977
|
-
create(context) {
|
|
978
|
-
const perKindTables = new Set(
|
|
979
|
-
requireOption(
|
|
980
|
-
"documents-share-one-table",
|
|
981
|
-
context.options?.[0]?.perKindTables,
|
|
982
|
-
"perKindTables"
|
|
983
|
-
)
|
|
984
|
-
);
|
|
985
|
-
if (!/\/packages\/db\/src\/schema\//.test(normalise(context.filename))) return {};
|
|
986
|
-
return {
|
|
987
|
-
CallExpression(node) {
|
|
988
|
-
for (const callee of TABLE_CALLEES) {
|
|
989
|
-
const table = literalNamedBy(node, callee);
|
|
990
|
-
if (table === null || !perKindTables.has(table)) continue;
|
|
991
|
-
context.report({ data: { table }, messageId: "perKindTable", node });
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
|
-
},
|
|
997
|
-
fixShape: `Every document kind is a row of the one \`documents\` table with a \`kind\` column, not a table of its
|
|
998
|
-
own. A \`pgTable('invoices', \u2026)\` beside a \`pgTable('quotes', \u2026)\` is the second storage the base entity
|
|
999
|
-
retired: write the row, not the table. Both constructors count \u2014 \`pgTable\` and \`sqliteTable\` \u2014 since
|
|
1000
|
-
the dialect is a seam fact, not a reason for a kind to own storage. The forbidden table names are the rule's
|
|
1001
|
-
\`perKindTables\` option; enabled without it the rule refuses the run rather than passing every
|
|
1002
|
-
schema silently.`,
|
|
1003
|
-
meta: {
|
|
1004
|
-
floor: { name: "document", pending: true },
|
|
1005
|
-
docs: {
|
|
1006
|
-
description: "Documents share ONE table: `documents` holds the head every kind derives and the body its kind declares. A table named for one kind of document is a second storage."
|
|
1007
|
-
},
|
|
1008
|
-
messages: {
|
|
1009
|
-
perKindTable: '"{{table}}" is a table for one kind of document. Every kind is a row of `documents` \u2014 add the kind to `documentEntities` and let its `headOf` fill the columns.'
|
|
1010
|
-
},
|
|
1011
|
-
schema: [
|
|
1012
|
-
{
|
|
1013
|
-
additionalProperties: false,
|
|
1014
|
-
properties: {
|
|
1015
|
-
perKindTables: { items: { type: "string" }, type: "array" }
|
|
1016
|
-
},
|
|
1017
|
-
type: "object"
|
|
1018
|
-
}
|
|
1019
|
-
],
|
|
1020
|
-
type: "problem"
|
|
1021
|
-
},
|
|
1022
|
-
/** The schema directory is compiled in; the forbidden table names are this repo's vocabulary. */
|
|
1023
|
-
probe(options) {
|
|
1024
|
-
const { perKindTables = [] } = bagOf(options);
|
|
1025
|
-
return {
|
|
1026
|
-
path: "packages/db/src/schema/geonosis-probe-table.ts",
|
|
1027
|
-
source: `import { pgTable, text } from 'drizzle-orm/pg-core'
|
|
1028
|
-
|
|
1029
|
-
export const geonosisProbe = pgTable('${perKindTables[0] ?? ""}', { id: text('id') })
|
|
1030
|
-
`
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
|
-
var documents_share_one_table_default = documentsShareOneTable;
|
|
1035
|
-
|
|
1036
882
|
// src/rules/lib/objects.ts
|
|
1037
883
|
var propertiesOf = (node) => {
|
|
1038
884
|
const object = node;
|
|
@@ -1084,7 +930,7 @@ var stepNameOf = (node, config) => {
|
|
|
1084
930
|
};
|
|
1085
931
|
|
|
1086
932
|
// src/rules/durable-body-reads-through-steps.ts
|
|
1087
|
-
var
|
|
933
|
+
var RULE2 = "durable-body-reads-through-steps";
|
|
1088
934
|
var ACCEPTS2 = "{ ports: string[] (required), durableKey: string, durableValue: boolean | string, workflowFactories: string[], stepFactories: string[], stepProxies: string[] }";
|
|
1089
935
|
var DEFAULT_WORKFLOW_FACTORIES = ["createWorkflow"];
|
|
1090
936
|
var DEFAULT_DURABLE_KEY = "execution";
|
|
@@ -1116,13 +962,13 @@ var insideAStep = (node, factories) => {
|
|
|
1116
962
|
var durableBodyReadsThroughSteps = {
|
|
1117
963
|
create(context) {
|
|
1118
964
|
const bag = context.options?.[0] ?? {};
|
|
1119
|
-
const ports = new Set(requireOption(
|
|
965
|
+
const ports = new Set(requireOption(RULE2, bag.ports, "ports", ACCEPTS2));
|
|
1120
966
|
const proxies = new Set(bag.stepProxies ?? []);
|
|
1121
|
-
const steps = stepFactoriesOf(
|
|
967
|
+
const steps = stepFactoriesOf(RULE2, bag.stepFactories);
|
|
1122
968
|
const workflows = new Set(
|
|
1123
|
-
requireOption(
|
|
969
|
+
requireOption(RULE2, bag.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES, "workflowFactories")
|
|
1124
970
|
);
|
|
1125
|
-
const durableKey = requireOption(
|
|
971
|
+
const durableKey = requireOption(RULE2, bag.durableKey ?? DEFAULT_DURABLE_KEY, "durableKey");
|
|
1126
972
|
const durableValue = bag.durableValue ?? DEFAULT_DURABLE_VALUE;
|
|
1127
973
|
return {
|
|
1128
974
|
CallExpression(node) {
|
|
@@ -1272,7 +1118,7 @@ file and the export together \u2014 half the pair still fires.`,
|
|
|
1272
1118
|
var effect_hook_naming_default = effectHookNaming;
|
|
1273
1119
|
|
|
1274
1120
|
// src/rules/emit-declares-attempts.ts
|
|
1275
|
-
var
|
|
1121
|
+
var RULE3 = "emit-declares-attempts";
|
|
1276
1122
|
var DEFAULT_EMITTERS = ["emitEventStep"];
|
|
1277
1123
|
var OPTIONS = "options";
|
|
1278
1124
|
var ATTEMPTS = "attempts";
|
|
@@ -1287,7 +1133,7 @@ var valueOf2 = (properties, name) => properties.find((one) => one.key?.type ===
|
|
|
1287
1133
|
var emitDeclaresAttempts = {
|
|
1288
1134
|
create(context) {
|
|
1289
1135
|
const emitters = new Set(
|
|
1290
|
-
requireOption(
|
|
1136
|
+
requireOption(RULE3, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters")
|
|
1291
1137
|
);
|
|
1292
1138
|
const budgetHelpers = new Set(context.options?.[0]?.budgetHelpers ?? []);
|
|
1293
1139
|
return {
|
|
@@ -1957,21 +1803,21 @@ var writesWith = (prefixes) => (node) => {
|
|
|
1957
1803
|
var writeNameOf = (node) => propertyNameOf(node.callee ?? {}) ?? "a service write";
|
|
1958
1804
|
|
|
1959
1805
|
// src/rules/mutating-step-declares-retry.ts
|
|
1960
|
-
var
|
|
1806
|
+
var RULE4 = "mutating-step-declares-retry";
|
|
1961
1807
|
var DEFAULT_RETRY_KEY = "maxRetries";
|
|
1962
1808
|
var DEFAULT_RETRY_LIMIT_KEY = "limit";
|
|
1963
1809
|
var NO_RETRY2 = 0;
|
|
1964
1810
|
var mutatingStepDeclaresRetry = {
|
|
1965
1811
|
create(context) {
|
|
1966
|
-
const factories = stepFactoriesOf(
|
|
1967
|
-
const isWrite = writesWith(mutationPrefixesOf(
|
|
1812
|
+
const factories = stepFactoriesOf(RULE4, context.options?.[0]?.stepFactories);
|
|
1813
|
+
const isWrite = writesWith(mutationPrefixesOf(RULE4, context.options?.[0]?.mutationPrefixes));
|
|
1968
1814
|
const retryKey = requireOption(
|
|
1969
|
-
|
|
1815
|
+
RULE4,
|
|
1970
1816
|
context.options?.[0]?.retryKey ?? DEFAULT_RETRY_KEY,
|
|
1971
1817
|
"retryKey"
|
|
1972
1818
|
);
|
|
1973
1819
|
const retryLimitKey = requireOption(
|
|
1974
|
-
|
|
1820
|
+
RULE4,
|
|
1975
1821
|
context.options?.[0]?.retryLimitKey ?? DEFAULT_RETRY_LIMIT_KEY,
|
|
1976
1822
|
"retryLimitKey"
|
|
1977
1823
|
);
|
|
@@ -2048,13 +1894,13 @@ the repo wraps \`createStep\`, and \`mutationPrefixes\` if its writes are named
|
|
|
2048
1894
|
var mutating_step_declares_retry_default = mutatingStepDeclaresRetry;
|
|
2049
1895
|
|
|
2050
1896
|
// src/rules/named-when-block.ts
|
|
2051
|
-
var
|
|
1897
|
+
var RULE5 = "named-when-block";
|
|
2052
1898
|
var DEFAULT_BLOCKS = ["when"];
|
|
2053
1899
|
var NAMED = 3;
|
|
2054
1900
|
var namedWhenBlock = {
|
|
2055
1901
|
create(context) {
|
|
2056
1902
|
const blocks = new Set(
|
|
2057
|
-
requireOption(
|
|
1903
|
+
requireOption(RULE5, context.options?.[0]?.blocks ?? DEFAULT_BLOCKS, "blocks")
|
|
2058
1904
|
);
|
|
2059
1905
|
return {
|
|
2060
1906
|
CallExpression(node) {
|
|
@@ -2272,16 +2118,16 @@ var next_route_segment_is_thin_delegate_default = nextRouteSegmentIsThinDelegate
|
|
|
2272
2118
|
// src/rules/no-brand-names.ts
|
|
2273
2119
|
import * as fs3 from "fs";
|
|
2274
2120
|
var EXEMPT_PATH = /\/examples\//;
|
|
2275
|
-
var
|
|
2121
|
+
var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2276
2122
|
var noBrandNames = {
|
|
2277
2123
|
create(context) {
|
|
2278
2124
|
const { allowedSubstrings = [], brands = [] } = context.options?.[0] ?? {};
|
|
2279
2125
|
requireOption("no-brand-names", brands, "brands");
|
|
2280
2126
|
const filename = normalise(context.filename);
|
|
2281
2127
|
if (EXEMPT_PATH.test(filename)) return {};
|
|
2282
|
-
const brandPattern = new RegExp(`\\b(?:${brands.map(
|
|
2128
|
+
const brandPattern = new RegExp(`\\b(?:${brands.map(escapeForRegex).join("|")})\\b`, "i");
|
|
2283
2129
|
const allowedPatterns = allowedSubstrings.map(
|
|
2284
|
-
(allowed) => new RegExp(
|
|
2130
|
+
(allowed) => new RegExp(escapeForRegex(allowed), "gi")
|
|
2285
2131
|
);
|
|
2286
2132
|
let programNode = null;
|
|
2287
2133
|
return {
|
|
@@ -2780,6 +2626,7 @@ var EXPLICIT_SIDE_EFFECT_HOOKS = /* @__PURE__ */ new Set([
|
|
|
2780
2626
|
]);
|
|
2781
2627
|
var CUSTOM_EFFECT_HOOK_PATTERN = /^use[A-Z][A-Za-z0-9]*Effect$/;
|
|
2782
2628
|
var STORE_HOOK_PATTERN = /^use[A-Z][A-Za-z0-9]*Store$/;
|
|
2629
|
+
var ANY_HOOK_PATTERN = /^use[A-Z]/;
|
|
2783
2630
|
function isSideEffectHook(name) {
|
|
2784
2631
|
if (EXPLICIT_SIDE_EFFECT_HOOKS.has(name)) return true;
|
|
2785
2632
|
if (CUSTOM_EFFECT_HOOK_PATTERN.test(name)) return true;
|
|
@@ -2793,6 +2640,7 @@ var ROUTE_FALLBACK_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
2793
2640
|
"loading.tsx",
|
|
2794
2641
|
"not-found.tsx"
|
|
2795
2642
|
]);
|
|
2643
|
+
var LAYOUT_BASENAMES = /* @__PURE__ */ new Set(["layout.tsx", "template.tsx"]);
|
|
2796
2644
|
var R3F_PACKAGES = /* @__PURE__ */ new Set(["@react-three/drei", "@react-three/fiber"]);
|
|
2797
2645
|
function isPascalCase(name) {
|
|
2798
2646
|
return /^[A-Z]/.test(name);
|
|
@@ -2825,11 +2673,21 @@ function isIdentityFragment(n) {
|
|
|
2825
2673
|
return false;
|
|
2826
2674
|
}
|
|
2827
2675
|
function classifyReturnShape(arg) {
|
|
2828
|
-
if (isNullLiteral(arg)) return "
|
|
2829
|
-
if (isChildrenIdentifier(arg)) return "
|
|
2830
|
-
if (isIdentityFragment(arg)) return "
|
|
2676
|
+
if (isNullLiteral(arg)) return "null";
|
|
2677
|
+
if (isChildrenIdentifier(arg)) return "passthrough";
|
|
2678
|
+
if (isIdentityFragment(arg)) return "passthrough";
|
|
2831
2679
|
return "real";
|
|
2832
2680
|
}
|
|
2681
|
+
function recordReturn(scope, arg) {
|
|
2682
|
+
if (!scope) return;
|
|
2683
|
+
const shape = classifyReturnShape(arg);
|
|
2684
|
+
if (shape === "real") {
|
|
2685
|
+
scope.hasNonIdentityReturn = true;
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
scope.hasIdentityReturn = true;
|
|
2689
|
+
if (shape === "passthrough") scope.hasPassthroughReturn = true;
|
|
2690
|
+
}
|
|
2833
2691
|
function suggestedHookName(componentName) {
|
|
2834
2692
|
const trimmed = componentName.replace(/(Provider|Sync|Listener|Hydration)$/, "");
|
|
2835
2693
|
const kebab = trimmed.replaceAll(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -2843,6 +2701,7 @@ var noHookInComponentDisguise = {
|
|
|
2843
2701
|
const basename = normalized.split("/").pop() ?? "";
|
|
2844
2702
|
if (ROUTE_FALLBACK_BASENAMES.has(basename)) return {};
|
|
2845
2703
|
let fileUsesR3F = false;
|
|
2704
|
+
let fileIsClientBoundary = false;
|
|
2846
2705
|
const scopeStack = [];
|
|
2847
2706
|
let pendingName = null;
|
|
2848
2707
|
function currentScope() {
|
|
@@ -2850,9 +2709,11 @@ var noHookInComponentDisguise = {
|
|
|
2850
2709
|
}
|
|
2851
2710
|
function enterFunction(name, node) {
|
|
2852
2711
|
scopeStack.push({
|
|
2712
|
+
calledAnyHook: false,
|
|
2853
2713
|
calledHooks: /* @__PURE__ */ new Set(),
|
|
2854
2714
|
hasIdentityReturn: false,
|
|
2855
2715
|
hasNonIdentityReturn: false,
|
|
2716
|
+
hasPassthroughReturn: false,
|
|
2856
2717
|
name,
|
|
2857
2718
|
node
|
|
2858
2719
|
});
|
|
@@ -2865,14 +2726,16 @@ var noHookInComponentDisguise = {
|
|
|
2865
2726
|
if (scope.hasNonIdentityReturn) return;
|
|
2866
2727
|
if (!scope.hasIdentityReturn) return;
|
|
2867
2728
|
if (basename === "page.tsx" && scope.calledHooks.size === 0) return;
|
|
2729
|
+
if (fileIsClientBoundary && scope.calledAnyHook && !scope.hasPassthroughReturn) return;
|
|
2868
2730
|
const hasHooks = scope.calledHooks.size > 0;
|
|
2731
|
+
const passthrough = LAYOUT_BASENAMES.has(basename) ? "passthroughLayout" : "passthroughComponent";
|
|
2869
2732
|
context.report({
|
|
2870
2733
|
data: {
|
|
2871
2734
|
hooks: hasHooks ? [...scope.calledHooks].toSorted((a, b) => a.localeCompare(b)).join(", ") : "",
|
|
2872
2735
|
name: scope.name,
|
|
2873
2736
|
suggested: suggestedHookName(scope.name)
|
|
2874
2737
|
},
|
|
2875
|
-
messageId: hasHooks ? "hookInDisguise" :
|
|
2738
|
+
messageId: hasHooks ? "hookInDisguise" : passthrough,
|
|
2876
2739
|
node: scope.node
|
|
2877
2740
|
});
|
|
2878
2741
|
}
|
|
@@ -2882,12 +2745,7 @@ var noHookInComponentDisguise = {
|
|
|
2882
2745
|
pendingName = null;
|
|
2883
2746
|
enterFunction(name, node);
|
|
2884
2747
|
if (node.body && node.body.type !== "BlockStatement") {
|
|
2885
|
-
|
|
2886
|
-
const scope = currentScope();
|
|
2887
|
-
if (scope) {
|
|
2888
|
-
if (shape === "identity") scope.hasIdentityReturn = true;
|
|
2889
|
-
else scope.hasNonIdentityReturn = true;
|
|
2890
|
-
}
|
|
2748
|
+
recordReturn(currentScope(), node.body);
|
|
2891
2749
|
}
|
|
2892
2750
|
},
|
|
2893
2751
|
"ArrowFunctionExpression:exit"() {
|
|
@@ -2899,6 +2757,7 @@ var noHookInComponentDisguise = {
|
|
|
2899
2757
|
if (!scope) return;
|
|
2900
2758
|
const name = node.callee.name;
|
|
2901
2759
|
if (!name) return;
|
|
2760
|
+
if (ANY_HOOK_PATTERN.test(name)) scope.calledAnyHook = true;
|
|
2902
2761
|
if (isSideEffectHook(name)) {
|
|
2903
2762
|
scope.calledHooks.add(name);
|
|
2904
2763
|
}
|
|
@@ -2924,12 +2783,16 @@ var noHookInComponentDisguise = {
|
|
|
2924
2783
|
fileUsesR3F = true;
|
|
2925
2784
|
}
|
|
2926
2785
|
},
|
|
2786
|
+
// Only the prologue is a directive: the same string after an import is an expression
|
|
2787
|
+
// statement, and React reads it as nothing.
|
|
2788
|
+
Program(node) {
|
|
2789
|
+
for (const statement2 of node.body ?? []) {
|
|
2790
|
+
if (typeof statement2.directive !== "string") break;
|
|
2791
|
+
if (statement2.directive === "use client") fileIsClientBoundary = true;
|
|
2792
|
+
}
|
|
2793
|
+
},
|
|
2927
2794
|
ReturnStatement(node) {
|
|
2928
|
-
|
|
2929
|
-
if (!scope) return;
|
|
2930
|
-
const shape = classifyReturnShape(node.argument);
|
|
2931
|
-
if (shape === "identity") scope.hasIdentityReturn = true;
|
|
2932
|
-
else scope.hasNonIdentityReturn = true;
|
|
2795
|
+
recordReturn(currentScope(), node.argument);
|
|
2933
2796
|
},
|
|
2934
2797
|
VariableDeclarator(node) {
|
|
2935
2798
|
if (node.id?.type === "Identifier" && node.init && (node.init.type === "ArrowFunctionExpression" || node.init.type === "FunctionExpression")) {
|
|
@@ -2940,14 +2803,19 @@ var noHookInComponentDisguise = {
|
|
|
2940
2803
|
},
|
|
2941
2804
|
fixShape: `A component that calls hooks, renders nothing, and exists only to run an effect is a hook wearing
|
|
2942
2805
|
\`.tsx\`. Move it to \`organelles/use-<thing>.ts\`, export it as \`use<Thing>\`, and let the cell call it.
|
|
2943
|
-
A component returns UI; a hook returns state
|
|
2806
|
+
A component returns UI; a hook returns state. The one exception is the client-boundary beacon, for a
|
|
2807
|
+
parent that cannot call hooks \u2014 a Server Component, or a tissue: \`'use client'\` as the file's first
|
|
2808
|
+
statement, a hook call, and \`return null\`. Render it beside the children, never around them \u2014 a
|
|
2809
|
+
component that returns its \`children\` is still a disguise. A component that calls no hook and
|
|
2810
|
+
returns nothing is not a beacon either; delete it.`,
|
|
2944
2811
|
meta: {
|
|
2945
2812
|
docs: {
|
|
2946
|
-
description: "Forbid components whose body calls side-effect hooks (useEffect/useLayoutEffect/useInsertionEffect/useRef) but whose every return is identity (null, bare `children`, `<></>`, `<>{children}</>`). Such a component renders no real UI \u2014 it's a hook masquerading as a component. Move to organelles/use-*-effect.ts and call the hook from a real component. Exempt: route fallback files (default/not-found/loading/error/global-error.tsx)
|
|
2813
|
+
description: "Forbid components whose body calls side-effect hooks (useEffect/useLayoutEffect/useInsertionEffect/useRef) but whose every return is identity (null, bare `children`, `<></>`, `<>{children}</>`). Such a component renders no real UI \u2014 it's a hook masquerading as a component. Move to organelles/use-*-effect.ts and call the hook from a real component. Exempt: route fallback files (default/not-found/loading/error/global-error.tsx), files importing from @react-three/fiber or @react-three/drei, and the client-boundary beacon \u2014 a 'use client' file whose component calls a hook and returns only null. That is lint-theater."
|
|
2947
2814
|
},
|
|
2948
2815
|
messages: {
|
|
2949
|
-
hookInDisguise: "Component '{{name}}' has side-effect hooks ({{hooks}}) but renders only identity/null. It's a hook in disguise \u2014 move to organelles/use-{{suggested}}-effect.ts and call the hook from a real component.",
|
|
2950
|
-
passthroughComponent: "Component '{{name}}' renders only identity/null
|
|
2816
|
+
hookInDisguise: "Component '{{name}}' has side-effect hooks ({{hooks}}) but renders only identity/null. It's a hook in disguise \u2014 move to organelles/use-{{suggested}}-effect.ts and call the hook from a real component. If its parent cannot call hooks (a Server Component, a tissue), make it the client boundary instead: 'use client' first, and return null \u2014 rendered beside the children, not around them.",
|
|
2817
|
+
passthroughComponent: "Component '{{name}}' renders only identity/null and calls no hook. It serves no architectural purpose: delete it and render its children where it is used, or give it real content.",
|
|
2818
|
+
passthroughLayout: "Component '{{name}}' renders only identity/null with no effects. It serves no architectural purpose. If this is a metadata-only layout, move `metadata`/`generateMetadata` to the sibling page.tsx and delete this file. If it's structural, give it real content."
|
|
2951
2819
|
},
|
|
2952
2820
|
schema: [],
|
|
2953
2821
|
type: "problem"
|
|
@@ -2956,20 +2824,20 @@ A component returns UI; a hook returns state.`,
|
|
|
2956
2824
|
var no_hook_in_component_disguise_default = noHookInComponentDisguise;
|
|
2957
2825
|
|
|
2958
2826
|
// src/rules/no-index-count-as-exact.ts
|
|
2959
|
-
var
|
|
2827
|
+
var RULE6 = "no-index-count-as-exact";
|
|
2960
2828
|
var DEFAULT_ESTIMATE_FIELDS = ["estimate_count", "count_estimate"];
|
|
2961
2829
|
var DEFAULT_LABEL_KEY = "count_is_estimate";
|
|
2962
2830
|
var noIndexCountAsExact = {
|
|
2963
2831
|
create(context) {
|
|
2964
2832
|
const fields = new Set(
|
|
2965
2833
|
requireOption(
|
|
2966
|
-
|
|
2834
|
+
RULE6,
|
|
2967
2835
|
context.options?.[0]?.estimateFields ?? DEFAULT_ESTIMATE_FIELDS,
|
|
2968
2836
|
"estimateFields"
|
|
2969
2837
|
)
|
|
2970
2838
|
);
|
|
2971
2839
|
const label = requireOption(
|
|
2972
|
-
|
|
2840
|
+
RULE6,
|
|
2973
2841
|
context.options?.[0]?.labelKey ?? DEFAULT_LABEL_KEY,
|
|
2974
2842
|
"labelKey"
|
|
2975
2843
|
);
|
|
@@ -3176,10 +3044,31 @@ var VALID_FOLDERS = /* @__PURE__ */ new Set([
|
|
|
3176
3044
|
"tissues"
|
|
3177
3045
|
]);
|
|
3178
3046
|
var FEATURE_PATH_PATTERN = /features\/([^/]+)\/([^/]+)\//;
|
|
3047
|
+
var folderUnderRoot = (normalized, roots) => {
|
|
3048
|
+
const path2 = `/${normalized}`;
|
|
3049
|
+
for (const declared of roots) {
|
|
3050
|
+
const root = declared.replaceAll(/^\/+|\/+$/g, "");
|
|
3051
|
+
const at = path2.indexOf(`/${root}/`);
|
|
3052
|
+
if (root === "" || at === -1) continue;
|
|
3053
|
+
const rest = path2.slice(at + root.length + 2);
|
|
3054
|
+
const slash = rest.indexOf("/");
|
|
3055
|
+
if (slash !== -1) return { folder: rest.slice(0, slash), root };
|
|
3056
|
+
}
|
|
3057
|
+
return void 0;
|
|
3058
|
+
};
|
|
3179
3059
|
var noInvalidFeatureFolders = {
|
|
3180
3060
|
create(context) {
|
|
3181
3061
|
const normalized = normalise(context.filename);
|
|
3182
3062
|
const match = normalized.match(FEATURE_PATH_PATTERN);
|
|
3063
|
+
const { roots = [] } = context.options?.[0] ?? {};
|
|
3064
|
+
const underRoot = match ? void 0 : folderUnderRoot(normalized, roots);
|
|
3065
|
+
if (underRoot && underRoot.folder !== "features" && !VALID_FOLDERS.has(underRoot.folder)) {
|
|
3066
|
+
return {
|
|
3067
|
+
Program(node) {
|
|
3068
|
+
context.report({ data: underRoot, messageId: "untiered", node });
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3183
3072
|
if (!match) return {};
|
|
3184
3073
|
const feature = match[1] ?? "";
|
|
3185
3074
|
const folder = match[2] ?? "";
|
|
@@ -3209,36 +3098,48 @@ var noInvalidFeatureFolders = {
|
|
|
3209
3098
|
fixShape: `A feature folder holds only the tiers and their two neighbours: \`atoms\`, \`molecules\`, \`compounds\`,
|
|
3210
3099
|
\`organelles\`, \`cells\`, \`tissues\`, \`stores\`, \`lib\`. Anything else (\`components\`, \`utils\`, \`helpers\`,
|
|
3211
3100
|
\`types\`) is an unclassified pile \u2014 put the file in the tier that describes what it does, and pure
|
|
3212
|
-
logic in \`lib
|
|
3101
|
+
logic in \`lib/\`. A repo whose components live outside \`features/\` names those directories in
|
|
3102
|
+
\`roots\` (default: none), and the same holds directly under each one. An unmigrated folder there
|
|
3103
|
+
(\`organisms/\`) is a finding per file \u2014 debt the ratchet counts, never a place to park a file whose
|
|
3104
|
+
tier would not accept it.`,
|
|
3213
3105
|
meta: {
|
|
3214
3106
|
docs: {
|
|
3215
|
-
description: 'Files inside features/*/ must be in a valid biological hierarchy folder. Hooks (use-*) must be in organelles/, not lib/. Folders like "dynamic", "utils", "helpers", "components", "hooks" are forbidden.'
|
|
3107
|
+
description: 'Files inside features/*/ \u2014 and directly under each directory the `roots` option declares \u2014 must be in a valid biological hierarchy folder. Hooks (use-*) must be in organelles/, not lib/. Folders like "dynamic", "utils", "helpers", "components", "hooks" are forbidden.'
|
|
3216
3108
|
},
|
|
3217
3109
|
messages: {
|
|
3218
3110
|
hookInLib: 'Hook file "{{file}}" is in features/{{feature}}/lib/ but hooks must be in features/{{feature}}/organelles/. lib/ is for pure logic only \u2014 no hooks, no state.',
|
|
3219
|
-
invalid: 'File is in "features/{{feature}}/{{folder}}/" which is not a valid biological hierarchy folder. Valid folders: compounds, organelles, cells, tissues, stores, lib.'
|
|
3111
|
+
invalid: 'File is in "features/{{feature}}/{{folder}}/" which is not a valid biological hierarchy folder. Valid folders: compounds, organelles, cells, tissues, stores, lib.',
|
|
3112
|
+
untiered: 'File is in "{{root}}/{{folder}}/", which is not a tier, so no tier rule inspects it. Move it to the tier that describes what it does: atoms, molecules, compounds, organelles, cells, tissues, stores or lib.'
|
|
3220
3113
|
},
|
|
3221
|
-
schema: [
|
|
3114
|
+
schema: [
|
|
3115
|
+
{
|
|
3116
|
+
additionalProperties: false,
|
|
3117
|
+
properties: {
|
|
3118
|
+
roots: { items: { type: "string" }, type: "array" }
|
|
3119
|
+
},
|
|
3120
|
+
type: "object"
|
|
3121
|
+
}
|
|
3122
|
+
],
|
|
3222
3123
|
type: "problem"
|
|
3223
3124
|
}
|
|
3224
3125
|
};
|
|
3225
3126
|
var no_invalid_feature_folders_default = noInvalidFeatureFolders;
|
|
3226
3127
|
|
|
3227
3128
|
// src/rules/no-loader-side-effect-in-index-file.ts
|
|
3228
|
-
var
|
|
3129
|
+
var RULE7 = "no-loader-side-effect-in-index-file";
|
|
3229
3130
|
var DEFAULT_DIRECTORIES = ["(?:^|/)subscribers/", "(?:^|/)jobs/"];
|
|
3230
3131
|
var DEFAULT_RESOURCE_FACTORIES = ["createWorkflow", "createStep", "createHook"];
|
|
3231
3132
|
var INDEX = /(?:^|\/)index\.[cm]?[jt]sx?$/;
|
|
3232
3133
|
var noLoaderSideEffectInIndexFile = {
|
|
3233
3134
|
create(context) {
|
|
3234
3135
|
const directories = requireOption(
|
|
3235
|
-
|
|
3136
|
+
RULE7,
|
|
3236
3137
|
context.options?.[0]?.directories ?? DEFAULT_DIRECTORIES,
|
|
3237
3138
|
"directories"
|
|
3238
3139
|
).map((source) => new RegExp(source));
|
|
3239
3140
|
const factories = new Set(
|
|
3240
3141
|
requireOption(
|
|
3241
|
-
|
|
3142
|
+
RULE7,
|
|
3242
3143
|
context.options?.[0]?.resourceFactories ?? DEFAULT_RESOURCE_FACTORIES,
|
|
3243
3144
|
"resourceFactories"
|
|
3244
3145
|
)
|
|
@@ -3401,7 +3302,7 @@ the same component. Thin preset wrappers over one imported component do not coun
|
|
|
3401
3302
|
var no_logic_in_component_files_default = noLogicInComponentFiles;
|
|
3402
3303
|
|
|
3403
3304
|
// src/rules/no-mock-db-in-integration.ts
|
|
3404
|
-
var
|
|
3305
|
+
var RULE8 = "no-mock-db-in-integration";
|
|
3405
3306
|
var BUN_MOCK = "mock.module";
|
|
3406
3307
|
var MOCK_CALLS = /* @__PURE__ */ new Set(["jest.mock", BUN_MOCK, "vi.mock"]);
|
|
3407
3308
|
var calleeOf = (node) => {
|
|
@@ -3419,8 +3320,8 @@ var inDirectory = (filename, directory) => {
|
|
|
3419
3320
|
var noMockDbInIntegration = {
|
|
3420
3321
|
create(context) {
|
|
3421
3322
|
const options = context.options?.[0] ?? {};
|
|
3422
|
-
const dirs = requireOption(
|
|
3423
|
-
const modules = requireOption(
|
|
3323
|
+
const dirs = requireOption(RULE8, options.integrationDirs, "integrationDirs");
|
|
3324
|
+
const modules = requireOption(RULE8, options.dbModules, "dbModules");
|
|
3424
3325
|
const mockNames = new Set(options.mockNames ?? []);
|
|
3425
3326
|
const filename = normalise(context.filename);
|
|
3426
3327
|
const spans = /* @__PURE__ */ new Set();
|
|
@@ -3521,14 +3422,14 @@ below the seam, and a caller that names it has crossed it.`,
|
|
|
3521
3422
|
var no_orm_outside_db_default = noOrmOutsideDb;
|
|
3522
3423
|
|
|
3523
3424
|
// src/rules/no-raw-html-atoms.ts
|
|
3524
|
-
var
|
|
3425
|
+
var RULE9 = "no-raw-html-atoms";
|
|
3525
3426
|
var WHERE_RAW_HTML_IS_THE_POINT = "atoms";
|
|
3526
3427
|
var noRawHtmlAtoms = {
|
|
3527
3428
|
create(context) {
|
|
3528
|
-
const restrictions = requireOption(
|
|
3429
|
+
const restrictions = requireOption(RULE9, context.options[0], "element\u2192atom map");
|
|
3529
3430
|
const scope = context.options[1]?.scope;
|
|
3530
3431
|
const filename = context.filename;
|
|
3531
|
-
const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(
|
|
3432
|
+
const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(RULE9, scope).some((tier) => inTier(filename, tier));
|
|
3532
3433
|
if (!inScope) return {};
|
|
3533
3434
|
const elementMap = /* @__PURE__ */ new Map();
|
|
3534
3435
|
for (const r of restrictions) {
|
|
@@ -4178,7 +4079,7 @@ var no_type_definitions_in_components_default = noTypeDefinitionsInComponents;
|
|
|
4178
4079
|
|
|
4179
4080
|
// src/rules/no-unregistered-plugin-dir.ts
|
|
4180
4081
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4181
|
-
var
|
|
4082
|
+
var RULE10 = "no-unregistered-plugin-dir";
|
|
4182
4083
|
var ACCEPTS3 = "{ roots: (string | { path: string, files: boolean })[], registry: string | string[], manifests: string[] }";
|
|
4183
4084
|
var trimSlashes = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
4184
4085
|
var withoutComments = (source) => source.replaceAll(/\/\*[\s\S]*?\*\//g, " ").replaceAll(/\/\/[^\n]*/g, " ");
|
|
@@ -4206,12 +4107,12 @@ var oneFile = (segments) => {
|
|
|
4206
4107
|
var noUnregisteredPluginDir = {
|
|
4207
4108
|
create(context) {
|
|
4208
4109
|
const options = context.options?.[0] ?? {};
|
|
4209
|
-
const roots = requireOption(
|
|
4110
|
+
const roots = requireOption(RULE10, options.roots, "roots", ACCEPTS3).map(
|
|
4210
4111
|
(one) => typeof one === "string" ? { files: false, path: trimSlashes(one) } : { files: one.files === true, path: trimSlashes(one.path) }
|
|
4211
4112
|
);
|
|
4212
|
-
const declared = requireOption(
|
|
4113
|
+
const declared = requireOption(RULE10, options.registry, "registry", ACCEPTS3);
|
|
4213
4114
|
const registries = (typeof declared === "string" ? [declared] : declared).map(trimSlashes);
|
|
4214
|
-
const manifests = new Set(requireOption(
|
|
4115
|
+
const manifests = new Set(requireOption(RULE10, options.manifests, "manifests", ACCEPTS3));
|
|
4215
4116
|
const filename = normalise(context.filename);
|
|
4216
4117
|
return {
|
|
4217
4118
|
Program(node) {
|
|
@@ -4488,16 +4389,16 @@ source. The CELL is the only level where several sources meet; it derives and pa
|
|
|
4488
4389
|
var organelle_single_source_default = organelleSingleSource;
|
|
4489
4390
|
|
|
4490
4391
|
// src/rules/plugin-route-namespaced.ts
|
|
4491
|
-
var
|
|
4392
|
+
var RULE11 = "plugin-route-namespaced";
|
|
4492
4393
|
var DEFAULT_SCOPES = ["store", "admin"];
|
|
4493
4394
|
var ROUTE_FILE = /(?:^|\/)api\/(.+\/)?route\.[cm]?[jt]sx?$/;
|
|
4494
4395
|
var NAMESPACED_FROM = 2;
|
|
4495
4396
|
var readPackages = (configured) => {
|
|
4496
|
-
const rules2 = requireOption(
|
|
4397
|
+
const rules2 = requireOption(RULE11, configured, "packages");
|
|
4497
4398
|
for (const rule of rules2) {
|
|
4498
4399
|
if (typeof rule.under !== "string" || rule.under === "") {
|
|
4499
4400
|
throw new Error(
|
|
4500
|
-
`biological-architecture/${
|
|
4401
|
+
`biological-architecture/${RULE11}: every \`packages\` entry needs \`under\` \u2014 the directory whose children are packages. Without it the rule cannot tell which files belong to a plugin.`
|
|
4501
4402
|
);
|
|
4502
4403
|
}
|
|
4503
4404
|
}
|
|
@@ -4524,7 +4425,7 @@ var pluginRouteNamespaced = {
|
|
|
4524
4425
|
create(context) {
|
|
4525
4426
|
const rules2 = readPackages(context.options?.[0]?.packages);
|
|
4526
4427
|
const scopes = new Set(
|
|
4527
|
-
requireOption(
|
|
4428
|
+
requireOption(RULE11, context.options?.[0]?.scopes ?? DEFAULT_SCOPES, "scopes")
|
|
4528
4429
|
);
|
|
4529
4430
|
const path2 = normalise(context.filename);
|
|
4530
4431
|
const route = routePathOf(path2);
|
|
@@ -4710,8 +4611,55 @@ decides what an event MEANS and throws when it cannot.`,
|
|
|
4710
4611
|
};
|
|
4711
4612
|
var queue_loop_is_the_library_default = queueLoopIsTheLibrary;
|
|
4712
4613
|
|
|
4614
|
+
// src/rules/retired.ts
|
|
4615
|
+
var SINCE = "2.7.0";
|
|
4616
|
+
var REASON = "it enforced one consumer's document kinds, which is vocabulary the kit does not hold";
|
|
4617
|
+
var announced = /* @__PURE__ */ new Set();
|
|
4618
|
+
var retired = (name, schema) => ({
|
|
4619
|
+
create: () => {
|
|
4620
|
+
if (!announced.has(name)) {
|
|
4621
|
+
announced.add(name);
|
|
4622
|
+
process.stderr.write(
|
|
4623
|
+
`biological-architecture: rule "${name}" is retired since ${SINCE} \u2014 ${REASON}. It reports nothing and goes at the next major. Remove it from your config.
|
|
4624
|
+
`
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
return {};
|
|
4628
|
+
},
|
|
4629
|
+
fixShape: `\`${name}\` is retired since ${SINCE} and reports nothing. Remove it from the config.`,
|
|
4630
|
+
meta: {
|
|
4631
|
+
docs: {
|
|
4632
|
+
description: `Retired since ${SINCE}: ${REASON}. Reports nothing; goes at the next major.`
|
|
4633
|
+
},
|
|
4634
|
+
messages: {},
|
|
4635
|
+
retired: { reason: REASON, since: SINCE },
|
|
4636
|
+
schema,
|
|
4637
|
+
type: "problem"
|
|
4638
|
+
}
|
|
4639
|
+
});
|
|
4640
|
+
var RETIRED_RULES = {
|
|
4641
|
+
"document-sagas-are-generic": retired("document-sagas-are-generic", [
|
|
4642
|
+
{
|
|
4643
|
+
additionalProperties: false,
|
|
4644
|
+
properties: {
|
|
4645
|
+
factories: { items: { type: "string" }, type: "array" },
|
|
4646
|
+
perKindPrefixes: { items: { type: "string" }, type: "array" },
|
|
4647
|
+
within: { type: "string" }
|
|
4648
|
+
},
|
|
4649
|
+
type: "object"
|
|
4650
|
+
}
|
|
4651
|
+
]),
|
|
4652
|
+
"documents-share-one-table": retired("documents-share-one-table", [
|
|
4653
|
+
{
|
|
4654
|
+
additionalProperties: false,
|
|
4655
|
+
properties: { perKindTables: { items: { type: "string" }, type: "array" } },
|
|
4656
|
+
type: "object"
|
|
4657
|
+
}
|
|
4658
|
+
])
|
|
4659
|
+
};
|
|
4660
|
+
|
|
4713
4661
|
// src/rules/route-no-inline-mutation.ts
|
|
4714
|
-
var
|
|
4662
|
+
var RULE12 = "route-no-inline-mutation";
|
|
4715
4663
|
var DEFAULT_PATHS = [String.raw`(?:^|/)api/.*/route\.[cm]?[jt]sx?$`];
|
|
4716
4664
|
var DEFAULT_INLINE_MUTATION_OK = "arch:inline-mutation-ok";
|
|
4717
4665
|
var commentsOf2 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
|
|
@@ -4755,15 +4703,15 @@ var annotated = (found, comments, marker) => {
|
|
|
4755
4703
|
};
|
|
4756
4704
|
var routeNoInlineMutation = {
|
|
4757
4705
|
create(context) {
|
|
4758
|
-
const paths = requireOption(
|
|
4706
|
+
const paths = requireOption(RULE12, context.options?.[0]?.paths ?? DEFAULT_PATHS, "paths").map(
|
|
4759
4707
|
(source) => new RegExp(source)
|
|
4760
4708
|
);
|
|
4761
4709
|
const marker = requireOption(
|
|
4762
|
-
|
|
4710
|
+
RULE12,
|
|
4763
4711
|
context.options?.[0]?.inlineMutationOk ?? DEFAULT_INLINE_MUTATION_OK,
|
|
4764
4712
|
"inlineMutationOk"
|
|
4765
4713
|
);
|
|
4766
|
-
const isWrite = writesWith(mutationPrefixesOf(
|
|
4714
|
+
const isWrite = writesWith(mutationPrefixesOf(RULE12, context.options?.[0]?.mutationPrefixes));
|
|
4767
4715
|
const filename = normalise(context.filename);
|
|
4768
4716
|
if (!paths.some((pattern) => pattern.test(filename))) return {};
|
|
4769
4717
|
return {
|
|
@@ -4833,7 +4781,7 @@ var route_no_inline_mutation_default = routeNoInlineMutation;
|
|
|
4833
4781
|
|
|
4834
4782
|
// src/rules/router-schema-parity.ts
|
|
4835
4783
|
import { existsSync as existsSync2 } from "fs";
|
|
4836
|
-
var
|
|
4784
|
+
var RULE13 = "router-schema-parity";
|
|
4837
4785
|
var EXTENSIONS2 = [".ts", ".tsx"];
|
|
4838
4786
|
var trimSlashes2 = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
4839
4787
|
var baseOf = (file) => file.replace(/\.[cm]?[jt]sx?$/, "");
|
|
@@ -4851,8 +4799,8 @@ var directlyUnder = (filename, directory) => {
|
|
|
4851
4799
|
var routerSchemaParity = {
|
|
4852
4800
|
create(context) {
|
|
4853
4801
|
const options = context.options?.[0] ?? {};
|
|
4854
|
-
const routers = trimSlashes2(requireOption(
|
|
4855
|
-
const schemas = trimSlashes2(requireOption(
|
|
4802
|
+
const routers = trimSlashes2(requireOption(RULE13, options.routers, "routers"));
|
|
4803
|
+
const schemas = trimSlashes2(requireOption(RULE13, options.schemas, "schemas"));
|
|
4856
4804
|
const suffix = options.suffix ?? "";
|
|
4857
4805
|
const ignored = new Set(options.ignore ?? []);
|
|
4858
4806
|
const mounts = options.mounts ?? {};
|
|
@@ -4924,7 +4872,7 @@ repo-root-relative, because in one consumer they live in different packages.`,
|
|
|
4924
4872
|
var router_schema_parity_default = routerSchemaParity;
|
|
4925
4873
|
|
|
4926
4874
|
// src/rules/service-name-not-reserved.ts
|
|
4927
|
-
var
|
|
4875
|
+
var RULE14 = "service-name-not-reserved";
|
|
4928
4876
|
var DEFAULT_MODULE_FACTORIES = ["Module"];
|
|
4929
4877
|
var MODULES_2_19 = [
|
|
4930
4878
|
"analytics",
|
|
@@ -4964,13 +4912,13 @@ var serviceNameNotReserved = {
|
|
|
4964
4912
|
create(context) {
|
|
4965
4913
|
const factories = new Set(
|
|
4966
4914
|
requireOption(
|
|
4967
|
-
|
|
4915
|
+
RULE14,
|
|
4968
4916
|
context.options?.[0]?.moduleFactories ?? DEFAULT_MODULE_FACTORIES,
|
|
4969
4917
|
"moduleFactories"
|
|
4970
4918
|
)
|
|
4971
4919
|
);
|
|
4972
4920
|
const reserved = new Set(
|
|
4973
|
-
requireOption(
|
|
4921
|
+
requireOption(RULE14, context.options?.[0]?.reserved ?? MODULES_2_19, "reserved")
|
|
4974
4922
|
);
|
|
4975
4923
|
const constants = /* @__PURE__ */ new Map();
|
|
4976
4924
|
const pending = [];
|
|
@@ -5281,12 +5229,12 @@ can be misspelled and a second default nobody agreed to.`,
|
|
|
5281
5229
|
var ssot_no_process_env_default = ssotNoProcessEnv;
|
|
5282
5230
|
|
|
5283
5231
|
// src/rules/step-declares-compensation-or-none.ts
|
|
5284
|
-
var
|
|
5232
|
+
var RULE15 = "step-declares-compensation-or-none";
|
|
5285
5233
|
var NO_COMPENSATION = "noCompensation";
|
|
5286
5234
|
var WITH_COMPENSATION = 3;
|
|
5287
5235
|
var stepDeclaresCompensationOrNone = {
|
|
5288
5236
|
create(context) {
|
|
5289
|
-
const factories = stepFactoriesOf(
|
|
5237
|
+
const factories = stepFactoriesOf(RULE15, context.options?.[0]?.stepFactories);
|
|
5290
5238
|
return {
|
|
5291
5239
|
CallExpression(node) {
|
|
5292
5240
|
if (!isStepCall(node, factories)) return;
|
|
@@ -5330,7 +5278,7 @@ variable \u2014 passes, since the flag may be in the base. Configure \`stepFacto
|
|
|
5330
5278
|
var step_declares_compensation_or_none_default = stepDeclaresCompensationOrNone;
|
|
5331
5279
|
|
|
5332
5280
|
// src/rules/step-opens-its-own-cell.ts
|
|
5333
|
-
var
|
|
5281
|
+
var RULE16 = "step-opens-its-own-cell";
|
|
5334
5282
|
var STEP = "step";
|
|
5335
5283
|
var ACCEPTS4 = "{ within: string (required), hosts: string[], scopeKey: string, engine: string, wrapper: string }";
|
|
5336
5284
|
var DEFAULT_HOSTS = ["createRuntime", "entrypointFor"];
|
|
@@ -5365,9 +5313,9 @@ var declaresScope = (call, scopeKey) => {
|
|
|
5365
5313
|
var stepOpensItsOwnCell = {
|
|
5366
5314
|
create(context) {
|
|
5367
5315
|
const bag = context.options?.[0] ?? {};
|
|
5368
|
-
const within = requireOption(
|
|
5369
|
-
const hosts = new Set(requireOption(
|
|
5370
|
-
const scopeKey = requireOption(
|
|
5316
|
+
const within = requireOption(RULE16, bag.within, "within", ACCEPTS4);
|
|
5317
|
+
const hosts = new Set(requireOption(RULE16, bag.hosts ?? DEFAULT_HOSTS, "hosts", ACCEPTS4));
|
|
5318
|
+
const scopeKey = requireOption(RULE16, bag.scopeKey ?? DEFAULT_SCOPE_KEY, "scopeKey", ACCEPTS4);
|
|
5371
5319
|
const { engine, wrapper } = bag;
|
|
5372
5320
|
const filename = normalise(context.filename);
|
|
5373
5321
|
if (!new RegExp(within).test(filename)) return {};
|
|
@@ -5470,7 +5418,7 @@ without \`within\` the rule refuses the run.`,
|
|
|
5470
5418
|
const path2 = pathMatching(within, "geonosis-probe-engine-step.ts");
|
|
5471
5419
|
if (wrapper !== void 0 && new RegExp(wrapper).test(path2)) {
|
|
5472
5420
|
throw new Error(
|
|
5473
|
-
`biological-architecture/${
|
|
5421
|
+
`biological-architecture/${RULE16}: the probe path "${path2}" is also the \`wrapper\`, which is the one file allowed to reach \`${engine}\` \u2014 no single file inside \`within\` can both land where the rule looks and be somewhere it speaks.`
|
|
5474
5422
|
);
|
|
5475
5423
|
}
|
|
5476
5424
|
return {
|
|
@@ -5675,10 +5623,10 @@ data, it belongs on the admin surface.`,
|
|
|
5675
5623
|
var store_route_scopes_tenant_data_default = storeRouteScopesTenantData;
|
|
5676
5624
|
|
|
5677
5625
|
// src/rules/subpath-over-barrel.ts
|
|
5678
|
-
var
|
|
5626
|
+
var RULE17 = "subpath-over-barrel";
|
|
5679
5627
|
var ACCEPTS5 = "{ packages: { name: string, subpaths: { [subpath: string]: string[] } }[] (required) }";
|
|
5680
5628
|
var homesOf = (declared) => {
|
|
5681
|
-
const subpaths = requireOption(
|
|
5629
|
+
const subpaths = requireOption(RULE17, Object.keys(declared.subpaths ?? {}), "subpaths", ACCEPTS5);
|
|
5682
5630
|
return new Map(
|
|
5683
5631
|
subpaths.flatMap(
|
|
5684
5632
|
(subpath) => (declared.subpaths?.[subpath] ?? []).map((name) => [name, subpath])
|
|
@@ -5701,9 +5649,9 @@ var importedNamesOf = (node) => {
|
|
|
5701
5649
|
};
|
|
5702
5650
|
var subpathOverBarrel = {
|
|
5703
5651
|
create(context) {
|
|
5704
|
-
const declared = requireOption(
|
|
5652
|
+
const declared = requireOption(RULE17, context.options?.[0]?.packages, "packages", ACCEPTS5);
|
|
5705
5653
|
const homes = new Map(
|
|
5706
|
-
declared.map((one) => [requireOption(
|
|
5654
|
+
declared.map((one) => [requireOption(RULE17, one.name, "name", ACCEPTS5), homesOf(one)])
|
|
5707
5655
|
);
|
|
5708
5656
|
return {
|
|
5709
5657
|
ImportDeclaration(node) {
|
|
@@ -5773,7 +5721,7 @@ is a type-only import, which erases before any of that happens. Declare the map
|
|
|
5773
5721
|
const [exported] = first?.subpaths?.[subpath ?? ""] ?? [];
|
|
5774
5722
|
if (exported === void 0) {
|
|
5775
5723
|
throw new Error(
|
|
5776
|
-
`biological-architecture/${
|
|
5724
|
+
`biological-architecture/${RULE17}: the configured packages declare no subpath with a name in it, so there is nothing a probe could import from the root.`
|
|
5777
5725
|
);
|
|
5778
5726
|
}
|
|
5779
5727
|
return {
|
|
@@ -5788,7 +5736,7 @@ export const geonosisProbe = ${exported}
|
|
|
5788
5736
|
var subpath_over_barrel_default = subpathOverBarrel;
|
|
5789
5737
|
|
|
5790
5738
|
// src/rules/subscriber-declares-id.ts
|
|
5791
|
-
var
|
|
5739
|
+
var RULE18 = "subscriber-declares-id";
|
|
5792
5740
|
var DEFAULT_PATHS2 = ["(?:^|/)subscribers/"];
|
|
5793
5741
|
var CONFIG = "config";
|
|
5794
5742
|
var CONTEXT = "context";
|
|
@@ -5802,7 +5750,7 @@ var declaredName2 = (node) => {
|
|
|
5802
5750
|
};
|
|
5803
5751
|
var subscriberDeclaresId = {
|
|
5804
5752
|
create(context) {
|
|
5805
|
-
const paths = requireOption(
|
|
5753
|
+
const paths = requireOption(RULE18, context.options?.[0]?.paths ?? DEFAULT_PATHS2, "paths").map(
|
|
5806
5754
|
(source) => new RegExp(source)
|
|
5807
5755
|
);
|
|
5808
5756
|
const filename = normalise(context.filename);
|
|
@@ -6065,7 +6013,7 @@ are tests is \`testGlobs\` and which calls poll is \`pollers\`, both defaulted.`
|
|
|
6065
6013
|
var test_no_clock_polling_for_promise_flips_default = testNoClockPollingForPromiseFlips;
|
|
6066
6014
|
|
|
6067
6015
|
// src/rules/time-through-the-door.ts
|
|
6068
|
-
var
|
|
6016
|
+
var RULE19 = "time-through-the-door";
|
|
6069
6017
|
var ACCEPTS6 = "{ door: string (a regex over the filename), exempt: string[] (regex sources, default the test paths: __tests__/, .test., .spec.), libraries: string[] (default ['dayjs']) }";
|
|
6070
6018
|
var DEFAULT_LIBRARIES = ["dayjs"];
|
|
6071
6019
|
var DEFAULT_EXEMPT = ["__tests__/", String.raw`\.test\.`, String.raw`\.spec\.`];
|
|
@@ -6093,11 +6041,11 @@ var memberName = (callee) => ({
|
|
|
6093
6041
|
var readable2 = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
|
|
6094
6042
|
var timeThroughTheDoor = {
|
|
6095
6043
|
create(context) {
|
|
6096
|
-
const door = requireOption(
|
|
6044
|
+
const door = requireOption(RULE19, context.options?.[0]?.door, "door", ACCEPTS6);
|
|
6097
6045
|
const exempt = context.options?.[0]?.exempt ?? DEFAULT_EXEMPT;
|
|
6098
6046
|
const exemptModules = context.options?.[0]?.exemptModules ?? [];
|
|
6099
6047
|
const libraries = requireOption(
|
|
6100
|
-
|
|
6048
|
+
RULE19,
|
|
6101
6049
|
context.options?.[0]?.libraries ?? DEFAULT_LIBRARIES,
|
|
6102
6050
|
"libraries",
|
|
6103
6051
|
ACCEPTS6
|
|
@@ -6201,7 +6149,7 @@ it internally cannot be tested at a chosen instant.`,
|
|
|
6201
6149
|
);
|
|
6202
6150
|
if (outside === void 0) {
|
|
6203
6151
|
throw new Error(
|
|
6204
|
-
`biological-architecture/${
|
|
6152
|
+
`biological-architecture/${RULE19}: every path a probe could be written at is inside the \`door\` (${door}) or one of its \`exempt\` patterns, so there is nowhere left for this rule to speak.`
|
|
6205
6153
|
);
|
|
6206
6154
|
}
|
|
6207
6155
|
return {
|
|
@@ -6409,6 +6357,12 @@ function isAllowedType(t) {
|
|
|
6409
6357
|
return true;
|
|
6410
6358
|
}
|
|
6411
6359
|
}
|
|
6360
|
+
var literalDefaultsOf = (param) => new Map(
|
|
6361
|
+
(param.properties ?? []).flatMap(
|
|
6362
|
+
(property) => property.type === "Property" && property.key?.type === "Identifier" && property.value?.type === "AssignmentPattern" && property.value.right?.type === "Literal" ? [[property.key.name ?? "", property.value.right.value]] : []
|
|
6363
|
+
)
|
|
6364
|
+
);
|
|
6365
|
+
var isLayoutConfiguration = (t, defaults, prop) => t.type === "TSNumberKeyword" && typeof defaults.get(prop) === "number" || t.type === "TSBooleanKeyword" && typeof defaults.get(prop) === "boolean";
|
|
6412
6366
|
var tissueNoDataProps = {
|
|
6413
6367
|
create(context) {
|
|
6414
6368
|
const normalized = normalise(context.filename);
|
|
@@ -6424,6 +6378,7 @@ var tissueNoDataProps = {
|
|
|
6424
6378
|
const typeLiteral = firstParam.typeAnnotation?.typeAnnotation;
|
|
6425
6379
|
if (!typeLiteral || typeLiteral.type !== "TSTypeLiteral") return;
|
|
6426
6380
|
const members = typeLiteral.members ?? [];
|
|
6381
|
+
const defaults = literalDefaultsOf(firstParam);
|
|
6427
6382
|
for (const member of members) {
|
|
6428
6383
|
if (member.key?.type !== "Identifier") continue;
|
|
6429
6384
|
const propName = member.key.name ?? "";
|
|
@@ -6432,6 +6387,7 @@ var tissueNoDataProps = {
|
|
|
6432
6387
|
const t = member.typeAnnotation?.typeAnnotation;
|
|
6433
6388
|
if (!t) continue;
|
|
6434
6389
|
if (isAllowedType(t)) continue;
|
|
6390
|
+
if (isLayoutConfiguration(t, defaults, propName)) continue;
|
|
6435
6391
|
context.report({
|
|
6436
6392
|
data: { prop: propName, type: describeTypeAnnotation(t) },
|
|
6437
6393
|
messageId: "dataProp",
|
|
@@ -6471,13 +6427,14 @@ var tissueNoDataProps = {
|
|
|
6471
6427
|
fixShape: `A tissue takes \`children\`, ReactNode slot props, layout variants, \`params\` and \`searchParams\` \u2014 and
|
|
6472
6428
|
nothing else. A prop typed \`string\`, \`number\`, an array or a domain type (\`id\`, \`initialData\`,
|
|
6473
6429
|
\`documents\`) means the file owns data, which makes it a CELL: move it to \`cells/\`. A tissue gets its
|
|
6474
|
-
content as slots, never as data
|
|
6430
|
+
content as slots, never as data. Layout configuration is not data: a \`number\` or \`boolean\` prop the
|
|
6431
|
+
tissue gives a literal default (\`{ step = 10, wrap = false }\`) is allowed.`,
|
|
6475
6432
|
meta: {
|
|
6476
6433
|
docs: {
|
|
6477
6434
|
description: "Tissues must not own data. Props on a tissue must be limited to `children`, ReactNode slot props, `params`, `searchParams`, icon types, or layout-variant literal unions. Any primitive (`string`/`number`/`boolean`), array type, or arbitrary domain type reference means the file is actually a cell \u2014 move it to cells/. That is lint-theater.2 (Tissue self-sufficiency rule)."
|
|
6478
6435
|
},
|
|
6479
6436
|
messages: {
|
|
6480
|
-
dataProp: "Tissue prop '{{prop}}' has type '{{type}}' which is not allowed on a tissue. Tissues arrange children without owning data. Move the file from features/*/tissues/ to features/*/cells/ \u2014 cells own data. Allowed tissue props: children, params, searchParams, ReactNode slots, literal-union variants, icon types."
|
|
6437
|
+
dataProp: "Tissue prop '{{prop}}' has type '{{type}}' which is not allowed on a tissue. Tissues arrange children without owning data. Move the file from features/*/tissues/ to features/*/cells/ \u2014 cells own data. Allowed tissue props: children, params, searchParams, ReactNode slots, literal-union variants, icon types, and number/boolean layout configuration with a literal default."
|
|
6481
6438
|
},
|
|
6482
6439
|
schema: [],
|
|
6483
6440
|
type: "problem"
|
|
@@ -6588,13 +6545,13 @@ var tissue_no_stores_default = tissueNoStores;
|
|
|
6588
6545
|
// src/rules/todo-names-its-plan.ts
|
|
6589
6546
|
var ID_DEFAULT = String.raw`\d{3,}`;
|
|
6590
6547
|
var commentsOf3 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
|
|
6591
|
-
var
|
|
6548
|
+
var escapeForRegex2 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6592
6549
|
var todoNamesItsPlan = {
|
|
6593
6550
|
create(context) {
|
|
6594
6551
|
const { id = ID_DEFAULT, markers = [] } = context.options?.[0] ?? {};
|
|
6595
6552
|
requireOption("todo-names-its-plan", markers, "markers", "{ markers: string[], id?: string }");
|
|
6596
6553
|
const marker = new RegExp(
|
|
6597
|
-
`\\b(${markers.map(
|
|
6554
|
+
`\\b(${markers.map(escapeForRegex2).join("|")})\\b(?:\\(([^)]*)\\))?`,
|
|
6598
6555
|
"g"
|
|
6599
6556
|
);
|
|
6600
6557
|
const names2 = new RegExp(`^${id}$`);
|
|
@@ -7011,7 +6968,7 @@ var isNamedOnly = (value) => /^[a-z]+$/i.test(value.trim());
|
|
|
7011
6968
|
var tokens_only_colors_default = tokensOnlyColors;
|
|
7012
6969
|
|
|
7013
6970
|
// src/rules/unique-query-step-name.ts
|
|
7014
|
-
var
|
|
6971
|
+
var RULE20 = "unique-query-step-name";
|
|
7015
6972
|
var ACCEPTS7 = "{ queryStepCalls: string[] (required), workflowFactories: string[] }";
|
|
7016
6973
|
var DEFAULT_WORKFLOW_FACTORIES2 = ["createWorkflow"];
|
|
7017
6974
|
var CONFIG2 = "config";
|
|
@@ -7055,13 +7012,13 @@ var uniqueQueryStepName = {
|
|
|
7055
7012
|
create(context) {
|
|
7056
7013
|
const factories = new Set(
|
|
7057
7014
|
requireOption(
|
|
7058
|
-
|
|
7015
|
+
RULE20,
|
|
7059
7016
|
context.options?.[0]?.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES2,
|
|
7060
7017
|
"workflowFactories"
|
|
7061
7018
|
)
|
|
7062
7019
|
);
|
|
7063
7020
|
const calls = new Set(
|
|
7064
|
-
requireOption(
|
|
7021
|
+
requireOption(RULE20, context.options?.[0]?.queryStepCalls, "queryStepCalls", ACCEPTS7)
|
|
7065
7022
|
);
|
|
7066
7023
|
return {
|
|
7067
7024
|
CallExpression(node) {
|
|
@@ -7142,7 +7099,7 @@ because the collision it reports is one framework's step-handler map and not eve
|
|
|
7142
7099
|
var unique_query_step_name_default = uniqueQueryStepName;
|
|
7143
7100
|
|
|
7144
7101
|
// src/rules/worker-handles-are-scoped.ts
|
|
7145
|
-
var
|
|
7102
|
+
var RULE21 = "worker-handles-are-scoped";
|
|
7146
7103
|
var ACCEPTS8 = "{ within: string (a regex over the filename), wrapper: string[] }";
|
|
7147
7104
|
var handlerKeys = /* @__PURE__ */ new Set(["fetch", "queue", "scheduled", "email"]);
|
|
7148
7105
|
var entrypointMakers = /* @__PURE__ */ new Set([
|
|
@@ -7152,8 +7109,8 @@ var entrypointMakers = /* @__PURE__ */ new Set([
|
|
|
7152
7109
|
]);
|
|
7153
7110
|
var cellHandlesAreScoped = {
|
|
7154
7111
|
create(context) {
|
|
7155
|
-
const within = requireOption(
|
|
7156
|
-
const wrappers = requireOption(
|
|
7112
|
+
const within = requireOption(RULE21, context.options?.[0]?.within, "within", ACCEPTS8);
|
|
7113
|
+
const wrappers = requireOption(RULE21, context.options?.[0]?.wrapper, "wrapper", ACCEPTS8);
|
|
7157
7114
|
if (!new RegExp(within).test(normalise(context.filename))) return {};
|
|
7158
7115
|
const scopedBy = new Set(wrappers);
|
|
7159
7116
|
let handler = null;
|
|
@@ -7356,8 +7313,6 @@ var rules = refusingOnce(
|
|
|
7356
7313
|
"compound-no-stores": compound_no_stores_default,
|
|
7357
7314
|
"constants-in-constants-file": constants_in_constants_file_default,
|
|
7358
7315
|
"dialect-through-the-seam": dialect_through_the_seam_default,
|
|
7359
|
-
"document-sagas-are-generic": document_sagas_are_generic_default,
|
|
7360
|
-
"documents-share-one-table": documents_share_one_table_default,
|
|
7361
7316
|
"durable-body-reads-through-steps": durable_body_reads_through_steps_default,
|
|
7362
7317
|
"effect-hook-naming": effect_hook_naming_default,
|
|
7363
7318
|
"emit-declares-attempts": emit_declares_attempts_default,
|
|
@@ -7430,18 +7385,22 @@ var rules = refusingOnce(
|
|
|
7430
7385
|
})
|
|
7431
7386
|
);
|
|
7432
7387
|
var { version } = createRequire(import.meta.url)("../package.json");
|
|
7388
|
+
var loaded = { ...rules, ...RETIRED_RULES };
|
|
7433
7389
|
var plugin = {
|
|
7434
7390
|
configs: PRESETS,
|
|
7435
7391
|
meta: {
|
|
7436
7392
|
name: "biological-architecture",
|
|
7437
7393
|
version
|
|
7438
7394
|
},
|
|
7439
|
-
|
|
7395
|
+
// A retired id resolves here and nowhere else: a config that still names one parses, and the id
|
|
7396
|
+
// reports nothing. `rules` — what the kit's own tooling reads as its rule set — does not carry it.
|
|
7397
|
+
rules: loaded
|
|
7440
7398
|
};
|
|
7441
7399
|
var index_default = plugin;
|
|
7442
7400
|
export {
|
|
7443
7401
|
PRESETS,
|
|
7444
7402
|
RENAMED_TO,
|
|
7403
|
+
RETIRED_RULES,
|
|
7445
7404
|
index_default as default,
|
|
7446
7405
|
rules,
|
|
7447
7406
|
rulesOfPreset
|