@foldkit/oxlint-plugin 0.13.0-canary.41f95ced5e07 → 0.13.0-canary.4cb3546e35b7
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/all.json +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +645 -27
- package/dist/rules/no-child-message-construction-in-root.d.ts +2 -2
- package/dist/rules/no-child-message-construction-in-root.d.ts.map +1 -1
- package/dist/rules/no-direct-submodel-state-update.d.ts +3 -0
- package/dist/rules/no-direct-submodel-state-update.d.ts.map +1 -0
- package/dist/rules/require-fold-for-child-update-result.d.ts +6 -0
- package/dist/rules/require-fold-for-child-update-result.d.ts.map +1 -0
- package/package.json +3 -2
- package/recommended.json +4 -0
package/all.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"foldkit/mount-factory-must-use-element": "error",
|
|
18
18
|
"foldkit/no-array-index-view-keys": "error",
|
|
19
19
|
"foldkit/no-child-message-construction-in-root": "error",
|
|
20
|
+
"foldkit/no-direct-submodel-state-update": "error",
|
|
20
21
|
"foldkit/no-disabling-dev-guardrails": "error",
|
|
21
22
|
"foldkit/no-duplicate-onmount-per-element": "error",
|
|
22
23
|
"foldkit/no-empty-children-array": "error",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"foldkit/prefer-command-mapmessage": "error",
|
|
39
40
|
"foldkit/prefer-effect-module-names": "error",
|
|
40
41
|
"foldkit/prefer-option-over-nullable-in-model": "error",
|
|
42
|
+
"foldkit/require-fold-for-child-update-result": "error",
|
|
41
43
|
"foldkit/require-rel-for-external-link": "error",
|
|
42
44
|
"foldkit/selection-submodel-factory-at-module-scope": "error",
|
|
43
45
|
"foldkit/wrap-child-output-in-got-message": "error"
|
|
@@ -100,6 +102,7 @@
|
|
|
100
102
|
"foldkit/mount-factory-must-use-element": "off",
|
|
101
103
|
"foldkit/no-array-index-view-keys": "off",
|
|
102
104
|
"foldkit/no-child-message-construction-in-root": "off",
|
|
105
|
+
"foldkit/no-direct-submodel-state-update": "off",
|
|
103
106
|
"foldkit/no-disabling-dev-guardrails": "off",
|
|
104
107
|
"foldkit/no-duplicate-onmount-per-element": "off",
|
|
105
108
|
"foldkit/no-empty-children-array": "off",
|
|
@@ -119,6 +122,7 @@
|
|
|
119
122
|
"foldkit/prefer-callable-message-constructor": "off",
|
|
120
123
|
"foldkit/prefer-command-mapmessage": "off",
|
|
121
124
|
"foldkit/prefer-option-over-nullable-in-model": "off",
|
|
125
|
+
"foldkit/require-fold-for-child-update-result": "off",
|
|
122
126
|
"foldkit/require-rel-for-external-link": "off",
|
|
123
127
|
"foldkit/selection-submodel-factory-at-module-scope": "off",
|
|
124
128
|
"foldkit/wrap-child-output-in-got-message": "off"
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ declare const _default: {
|
|
|
23
23
|
readonly 'mount-factory-must-use-element': import("effect-oxlint").CreateRule;
|
|
24
24
|
readonly 'no-array-index-view-keys': import("effect-oxlint").CreateRule;
|
|
25
25
|
readonly 'no-child-message-construction-in-root': import("effect-oxlint").CreateRule;
|
|
26
|
+
readonly 'no-direct-submodel-state-update': import("effect-oxlint").CreateRule;
|
|
26
27
|
readonly 'no-disabling-dev-guardrails': import("effect-oxlint").CreateRule;
|
|
27
28
|
readonly 'no-duplicate-onmount-per-element': import("effect-oxlint").CreateRule;
|
|
28
29
|
readonly 'no-empty-children-array': import("effect-oxlint").CreateRule;
|
|
@@ -44,6 +45,7 @@ declare const _default: {
|
|
|
44
45
|
readonly 'prefer-command-mapmessage': import("effect-oxlint").CreateRule;
|
|
45
46
|
readonly 'prefer-effect-module-names': import("effect-oxlint").CreateRule;
|
|
46
47
|
readonly 'prefer-option-over-nullable-in-model': import("effect-oxlint").CreateRule;
|
|
48
|
+
readonly 'require-fold-for-child-update-result': import("effect-oxlint").CreateRule;
|
|
47
49
|
readonly 'require-rel-for-external-link': import("effect-oxlint").CreateRule;
|
|
48
50
|
readonly 'selection-submodel-factory-at-module-scope': import("effect-oxlint").CreateRule;
|
|
49
51
|
readonly 'wrap-child-output-in-got-message': import("effect-oxlint").CreateRule;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAsFtC,KAAK,QAAQ,GAAG,QAAQ,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACpB,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;CAC3C,CAAC,CAAA;AAEF,KAAK,gBAAgB,GAAG,MAAM,CAAC,YAAY,GAAG;IAC5C,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;CAC3B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA6EG,WAAW;QACX,GAAG"}
|
package/dist/index.js
CHANGED
|
@@ -535,7 +535,7 @@ function isBoolean(input) {
|
|
|
535
535
|
* @category guards
|
|
536
536
|
* @since 2.0.0
|
|
537
537
|
*/
|
|
538
|
-
function isFunction$
|
|
538
|
+
function isFunction$3(input) {
|
|
539
539
|
return typeof input === "function";
|
|
540
540
|
}
|
|
541
541
|
/**
|
|
@@ -648,7 +648,7 @@ function isObject(input) {
|
|
|
648
648
|
* @since 4.0.0
|
|
649
649
|
*/
|
|
650
650
|
function isObjectKeyword(input) {
|
|
651
|
-
return typeof input === "object" && input !== null || isFunction$
|
|
651
|
+
return typeof input === "object" && input !== null || isFunction$3(input);
|
|
652
652
|
}
|
|
653
653
|
/**
|
|
654
654
|
* Checks whether a value has a given property key.
|
|
@@ -1738,7 +1738,7 @@ const NodeInspectSymbol = /*#__PURE__*/ Symbol.for("nodejs.util.inspect.custom")
|
|
|
1738
1738
|
const toJson = (input) => {
|
|
1739
1739
|
try {
|
|
1740
1740
|
input = redact(input);
|
|
1741
|
-
if (hasProperty(input, "toJSON") && isFunction$
|
|
1741
|
+
if (hasProperty(input, "toJSON") && isFunction$3(input["toJSON"]) && input["toJSON"].length === 0) return input.toJSON();
|
|
1742
1742
|
else if (Array.isArray(input)) return input.map(toJson);
|
|
1743
1743
|
return input;
|
|
1744
1744
|
} catch {
|
|
@@ -2825,6 +2825,62 @@ const map$5 = /*#__PURE__*/ dual(2, (self, f) => isNone(self) ? none() : some$1(
|
|
|
2825
2825
|
*/
|
|
2826
2826
|
const flatMap$3 = /*#__PURE__*/ dual(2, (self, f) => isNone(self) ? none() : f(self.value));
|
|
2827
2827
|
/**
|
|
2828
|
+
* Combines a structure of `Option`s (tuple, struct, or iterable) into a single
|
|
2829
|
+
* `Option` containing the unwrapped structure.
|
|
2830
|
+
*
|
|
2831
|
+
* **When to use**
|
|
2832
|
+
*
|
|
2833
|
+
* Use when you need to combine multiple `Option` values into one while
|
|
2834
|
+
* preserving the input shape, with any `None` making the result `None`.
|
|
2835
|
+
*
|
|
2836
|
+
* **Details**
|
|
2837
|
+
*
|
|
2838
|
+
* - Tuple input → `Option` of a tuple with the same length
|
|
2839
|
+
* - Struct input → `Option` of a struct with the same keys
|
|
2840
|
+
* - Iterable input → `Option` of an `Array`
|
|
2841
|
+
* - Any `None` in the input → entire result is `None`
|
|
2842
|
+
*
|
|
2843
|
+
* **Example** (Combining a tuple and a struct)
|
|
2844
|
+
*
|
|
2845
|
+
* ```ts import.meta.vitest
|
|
2846
|
+
* import { Option } from "effect"
|
|
2847
|
+
*
|
|
2848
|
+
* const maybeName: Option.Option<string> = Option.some("John")
|
|
2849
|
+
* const maybeAge: Option.Option<number> = Option.some(25)
|
|
2850
|
+
*
|
|
2851
|
+
* // ┌─── Option<[string, number]>
|
|
2852
|
+
* // ▼
|
|
2853
|
+
* const tuple = Option.all([maybeName, maybeAge]) // => Option.some(["John", 25])
|
|
2854
|
+
*
|
|
2855
|
+
* // ┌─── Option<{ name: string; age: number; }>
|
|
2856
|
+
* // ▼
|
|
2857
|
+
* const struct = Option.all({ name: maybeName, age: maybeAge }) // => Option.some({ name: "John", age: 25 })
|
|
2858
|
+
* ```
|
|
2859
|
+
*
|
|
2860
|
+
* @see {@link product} for combining exactly two
|
|
2861
|
+
* @see {@link productMany} for a homogeneous collection
|
|
2862
|
+
*
|
|
2863
|
+
* @category combining
|
|
2864
|
+
* @since 2.0.0
|
|
2865
|
+
*/
|
|
2866
|
+
const all = (input) => {
|
|
2867
|
+
if (Symbol.iterator in input) {
|
|
2868
|
+
const out = [];
|
|
2869
|
+
for (const o of input) {
|
|
2870
|
+
if (isNone(o)) return none();
|
|
2871
|
+
out.push(o.value);
|
|
2872
|
+
}
|
|
2873
|
+
return some$1(out);
|
|
2874
|
+
}
|
|
2875
|
+
const out = {};
|
|
2876
|
+
for (const key of Object.keys(input)) {
|
|
2877
|
+
const o = input[key];
|
|
2878
|
+
if (isNone(o)) return none();
|
|
2879
|
+
assignProperty(out, key, o.value);
|
|
2880
|
+
}
|
|
2881
|
+
return some$1(out);
|
|
2882
|
+
};
|
|
2883
|
+
/**
|
|
2828
2884
|
* Filters an `Option` using a predicate. Returns `None` if the predicate is
|
|
2829
2885
|
* not satisfied or the input is `None`.
|
|
2830
2886
|
*
|
|
@@ -10280,7 +10336,7 @@ function collectIssues(checks, value, issues, ast, options) {
|
|
|
10280
10336
|
function getConstructorDescriptor(ast) {
|
|
10281
10337
|
if (!isDeclaration(ast)) return void 0;
|
|
10282
10338
|
const getDescriptor = ast.annotations?.[CONSTRUCTOR_ANNOTATION_KEY];
|
|
10283
|
-
return isFunction$
|
|
10339
|
+
return isFunction$3(getDescriptor) ? getDescriptor(ast.typeParameters) : void 0;
|
|
10284
10340
|
}
|
|
10285
10341
|
/**
|
|
10286
10342
|
* Creates an effectful decoder for `unknown` input.
|
|
@@ -11736,9 +11792,9 @@ const isCallExpression$1 = (node) => typeof node === "object" && node !== null &
|
|
|
11736
11792
|
const isMemberExpression = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "MemberExpression";
|
|
11737
11793
|
const isObjectExpression = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ObjectExpression";
|
|
11738
11794
|
const isObjectProperty = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "Property";
|
|
11739
|
-
const isArrayExpression = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrayExpression";
|
|
11795
|
+
const isArrayExpression$1 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrayExpression";
|
|
11740
11796
|
const isSpreadElement = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "SpreadElement";
|
|
11741
|
-
const isArrowFunction = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
|
|
11797
|
+
const isArrowFunction$1 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
|
|
11742
11798
|
const helperCalleeName = (callee) => {
|
|
11743
11799
|
if (isIdentifier(callee)) return some$1(callee.name);
|
|
11744
11800
|
if (isMemberExpression(callee) && !callee.computed && isIdentifier(callee.property)) return some$1(callee.property.name);
|
|
@@ -11885,7 +11941,7 @@ const isEffectCall = (node, memberName, references) => {
|
|
|
11885
11941
|
const isSyncReturningIdentifier = (node, references) => {
|
|
11886
11942
|
if (!isEffectCall(node, "sync", references)) return false;
|
|
11887
11943
|
const [thunk] = node.arguments;
|
|
11888
|
-
return isArrowFunction(thunk) && isIdentifier(thunk.body);
|
|
11944
|
+
return isArrowFunction$1(thunk) && isIdentifier(thunk.body);
|
|
11889
11945
|
};
|
|
11890
11946
|
const isEagerSucceed = (node, references) => isEffectCall(node, "succeed", references);
|
|
11891
11947
|
const ACQUIRE_CAPTURED_HANDLE_MESSAGE = "Effect.acquireRelease must construct its resource inside the acquire Effect, not return an eagerly created or captured handle. An interruption between construction and acquire leaks the handle. Build it in place with Effect.sync or another lazy Effect so acquire owns the whole lifetime.";
|
|
@@ -12216,7 +12272,7 @@ const singleValueMapNamespaces = [
|
|
|
12216
12272
|
];
|
|
12217
12273
|
const isArrowFunctionExpression$3 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
|
|
12218
12274
|
const isBlockStatement$2 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "BlockStatement";
|
|
12219
|
-
const isReturnStatement$
|
|
12275
|
+
const isReturnStatement$4 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ReturnStatement";
|
|
12220
12276
|
const isObjectPattern = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ObjectPattern";
|
|
12221
12277
|
const isSingleValueMapCallee = (callee, references) => {
|
|
12222
12278
|
if (references === void 0) return isMemberExpression(callee) && !callee.computed && isIdentifier(callee.property, "map") && isIdentifier(callee.object) && singleValueMapNamespaces.includes(callee.object.name);
|
|
@@ -12259,12 +12315,12 @@ const isIdentityBearing = (callback, references, declaredVariables) => {
|
|
|
12259
12315
|
};
|
|
12260
12316
|
const callbackReturnExpression = (callback) => {
|
|
12261
12317
|
if (!isBlockStatement$2(callback.body)) return some$1(callback.body);
|
|
12262
|
-
return pipe(callback.body.body, findLast(isReturnStatement$
|
|
12318
|
+
return pipe(callback.body.body, findLast(isReturnStatement$4), flatMap$3((returnStatement) => fromNullishOr(returnStatement.argument)));
|
|
12263
12319
|
};
|
|
12264
12320
|
const isKeyedWrapped = (node, references) => isCallExpression$1(node.callee) && isFoldkitHtmlBuilderMember(node.callee.callee, "keyed", references);
|
|
12265
12321
|
const hasKeyAttribute = (rowElementCall, references) => {
|
|
12266
12322
|
const [attributesArgument] = rowElementCall.arguments;
|
|
12267
|
-
if (!isArrayExpression(attributesArgument)) return false;
|
|
12323
|
+
if (!isArrayExpression$1(attributesArgument)) return false;
|
|
12268
12324
|
return attributesArgument.elements.some((element) => isCallExpression$1(element) && isFoldkitHtmlBuilderMember(element.callee, "Key", references));
|
|
12269
12325
|
};
|
|
12270
12326
|
const unkeyedRowElement = (returnExpression, references) => {
|
|
@@ -12696,7 +12752,7 @@ const messageModuleSegmentNames = /* @__PURE__ */ new Set([
|
|
|
12696
12752
|
"schema"
|
|
12697
12753
|
]);
|
|
12698
12754
|
const isMessageModuleSegment = (segment) => segment !== void 0 && messageModuleSegmentNames.has(moduleSegmentName(segment).toLowerCase());
|
|
12699
|
-
const isFunction$
|
|
12755
|
+
const isFunction$2 = (node) => typeof node === "object" && node !== null && "type" in node && (node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression");
|
|
12700
12756
|
const isTypeReference = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "TSTypeReference";
|
|
12701
12757
|
const isTypeAnnotatedIdentifier = (node) => isIdentifier(node);
|
|
12702
12758
|
const importedMessagePath = (references, node) => isTypeReference(node) ? map$5(resolveImportedPath(references, node.typeName), (path) => ({
|
|
@@ -12751,7 +12807,7 @@ const defineViewMessagePath = (references, functionNode) => {
|
|
|
12751
12807
|
const isOwnedByEnclosingView = (node, references, messagePath) => {
|
|
12752
12808
|
const ownsMessage = (ancestor) => {
|
|
12753
12809
|
if (ancestor === null || ancestor === void 0) return false;
|
|
12754
|
-
if (isFunction$
|
|
12810
|
+
if (isFunction$2(ancestor)) {
|
|
12755
12811
|
if (ancestor.params.some((parameter) => exists(htmlBuilderMessagePath(references, parameter), (builderMessagePath) => sameImportedMessagePath(builderMessagePath, messagePath)))) return true;
|
|
12756
12812
|
if (exists(defineViewMessagePath(references, ancestor), (viewMessagePath) => sameImportedMessagePath(viewMessagePath, messagePath))) return true;
|
|
12757
12813
|
}
|
|
@@ -12784,13 +12840,31 @@ const childNamespace = (source, importedMembers, localMembers) => {
|
|
|
12784
12840
|
const [localRoot] = localMembers;
|
|
12785
12841
|
return localRoot ?? "Child";
|
|
12786
12842
|
};
|
|
12843
|
+
const resolveImportedPathThroughConst = (references, node, visited = /* @__PURE__ */ new Set()) => {
|
|
12844
|
+
const importedPath = resolveImportedPath(references, node);
|
|
12845
|
+
if (isSome(importedPath)) return importedPath;
|
|
12846
|
+
return gen$2(function* () {
|
|
12847
|
+
const path = yield* staticMemberPath(node);
|
|
12848
|
+
const variable = yield* resolvedVariable(references, path.root);
|
|
12849
|
+
if (visited.has(variable) || length(variable.defs) !== 1) return yield* none();
|
|
12850
|
+
const [definition] = variable.defs;
|
|
12851
|
+
if (definition === void 0 || definition.type !== "Variable" || !isVariableDeclarator$1(definition.node) || !isVariableDeclaration$1(definition.node.parent) || definition.node.parent.kind !== "const" || !isIdentifier(definition.node.id)) return yield* none();
|
|
12852
|
+
const initializer = yield* fromNullishOr(definition.node.init);
|
|
12853
|
+
const resolved = yield* resolveImportedPathThroughConst(references, initializer, /* @__PURE__ */ new Set([...visited, variable]));
|
|
12854
|
+
return {
|
|
12855
|
+
source: resolved.source,
|
|
12856
|
+
members: [...resolved.members, ...path.members],
|
|
12857
|
+
localMembers: [path.root.name, ...path.members]
|
|
12858
|
+
};
|
|
12859
|
+
});
|
|
12860
|
+
};
|
|
12787
12861
|
const childMessageConstruction = (callee, references) => {
|
|
12788
12862
|
if (hasComputedMember(callee)) return none();
|
|
12789
12863
|
if (references === void 0) return flatMap$3(staticMemberPath(callee), (path) => {
|
|
12790
12864
|
const [namespace, middle, constructorName, extraSegment] = [path.root.name, ...path.members];
|
|
12791
12865
|
return namespace !== void 0 && middle === "Message" && constructorName !== void 0 && constructorName !== "Message" && extraSegment === void 0 && pascalIdentifierPattern.test(namespace) && pascalIdentifierPattern.test(constructorName) ? some$1({ calleeLabel: `${namespace}.Message.${constructorName}` }) : none();
|
|
12792
12866
|
});
|
|
12793
|
-
return flatMap$3(
|
|
12867
|
+
return flatMap$3(resolveImportedPathThroughConst(references, callee), (path) => {
|
|
12794
12868
|
const [messageNamespace, constructorName] = path.members.slice(-2);
|
|
12795
12869
|
if (messageNamespace !== "Message" || constructorName === void 0 || constructorName === "Message" || !pascalIdentifierPattern.test(constructorName) || isRootMessageModule(path.source)) return none();
|
|
12796
12870
|
return some$1({
|
|
@@ -12803,8 +12877,8 @@ const childMessageConstruction = (callee, references) => {
|
|
|
12803
12877
|
});
|
|
12804
12878
|
};
|
|
12805
12879
|
/**
|
|
12806
|
-
* Flags
|
|
12807
|
-
*
|
|
12880
|
+
* Flags construction of a child Submodel Message variant from outside the
|
|
12881
|
+
* child, such as Chat.Message.ClickedOpen(...) or an immutable local alias.
|
|
12808
12882
|
*/
|
|
12809
12883
|
const noChildMessageConstructionInRoot = Rule_exports.define({
|
|
12810
12884
|
name: "no-child-message-construction-in-root",
|
|
@@ -12832,6 +12906,329 @@ const noChildMessageConstructionInRoot = Rule_exports.define({
|
|
|
12832
12906
|
}
|
|
12833
12907
|
});
|
|
12834
12908
|
//#endregion
|
|
12909
|
+
//#region src/rules/no-direct-submodel-state-update.ts
|
|
12910
|
+
const directSubmodelStateUpdateMessage = (fieldName) => `Do not update the Submodel field \`${fieldName}\` directly from its parent. Expose a child-owned update capability, then apply it with Update.foldChild or Update.foldChildStep.`;
|
|
12911
|
+
const isNode$2 = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
12912
|
+
const isArrowFunction = (value) => isNode$2(value) && value.type === "ArrowFunctionExpression";
|
|
12913
|
+
const isReturnStatement$3 = (value) => isNode$2(value) && value.type === "ReturnStatement";
|
|
12914
|
+
const walk$1 = (root, visit) => {
|
|
12915
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
12916
|
+
const visitNode = (node, ancestors) => {
|
|
12917
|
+
if (visited.has(node)) return;
|
|
12918
|
+
visited.add(node);
|
|
12919
|
+
visit(node, ancestors);
|
|
12920
|
+
for (const [key, value] of Object.entries(node)) {
|
|
12921
|
+
if (key === "parent") continue;
|
|
12922
|
+
if (isNode$2(value)) visitNode(value, [...ancestors, node]);
|
|
12923
|
+
else if (globalThis.Array.isArray(value)) {
|
|
12924
|
+
for (const entry of value) if (isNode$2(entry)) visitNode(entry, [...ancestors, node]);
|
|
12925
|
+
}
|
|
12926
|
+
}
|
|
12927
|
+
};
|
|
12928
|
+
visitNode(root, []);
|
|
12929
|
+
};
|
|
12930
|
+
const expressionReturnedBy = (callback) => {
|
|
12931
|
+
if (callback.body.type !== "BlockStatement") return callback.body;
|
|
12932
|
+
for (const statement of callback.body.body) if (isReturnStatement$3(statement) && statement.argument !== null) return statement.argument;
|
|
12933
|
+
};
|
|
12934
|
+
const isFunction$1 = (node) => node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration";
|
|
12935
|
+
const expressionsReturnedBy = (callback) => {
|
|
12936
|
+
if (callback.body.type !== "BlockStatement") return [callback.body];
|
|
12937
|
+
const returned = [];
|
|
12938
|
+
const visit = (node) => {
|
|
12939
|
+
if (isFunction$1(node)) return;
|
|
12940
|
+
if (isReturnStatement$3(node) && node.argument !== null) {
|
|
12941
|
+
returned.push(node.argument);
|
|
12942
|
+
return;
|
|
12943
|
+
}
|
|
12944
|
+
for (const [key, value] of Object.entries(node)) {
|
|
12945
|
+
if (key === "parent") continue;
|
|
12946
|
+
if (isNode$2(value)) visit(value);
|
|
12947
|
+
else if (globalThis.Array.isArray(value)) {
|
|
12948
|
+
for (const entry of value) if (isNode$2(entry)) visit(entry);
|
|
12949
|
+
}
|
|
12950
|
+
}
|
|
12951
|
+
};
|
|
12952
|
+
for (const statement of callback.body.body) visit(statement);
|
|
12953
|
+
return returned;
|
|
12954
|
+
};
|
|
12955
|
+
const directMemberField = (expression, parameterName) => {
|
|
12956
|
+
if (!isMemberExpression(expression) || expression.computed) return;
|
|
12957
|
+
if (!isIdentifier(expression.object, parameterName)) return;
|
|
12958
|
+
return isIdentifier(expression.property) ? expression.property.name : void 0;
|
|
12959
|
+
};
|
|
12960
|
+
const directReadField = (callback) => {
|
|
12961
|
+
const [modelParameter] = callback.params;
|
|
12962
|
+
if (!isIdentifier(modelParameter)) return;
|
|
12963
|
+
const returned = expressionReturnedBy(callback);
|
|
12964
|
+
if (!isCallExpression$1(returned)) return;
|
|
12965
|
+
const [readValue] = returned.arguments;
|
|
12966
|
+
return directMemberField(readValue, modelParameter.name);
|
|
12967
|
+
};
|
|
12968
|
+
const propertyValue = (object, name) => {
|
|
12969
|
+
for (const property of object.properties) if (property.type === "Property" && !property.computed && isIdentifier(property.key, name)) return property.value;
|
|
12970
|
+
};
|
|
12971
|
+
const directWriteField = (callback, references) => {
|
|
12972
|
+
const [modelParameter, nextChildParameter] = callback.params;
|
|
12973
|
+
if (!isIdentifier(modelParameter) || !isIdentifier(nextChildParameter)) return;
|
|
12974
|
+
const returned = expressionReturnedBy(callback);
|
|
12975
|
+
if (!isCallExpression$1(returned) || !isEvoCall$1(returned, references)) return;
|
|
12976
|
+
const [evolvedModel, updates] = returned.arguments;
|
|
12977
|
+
if (!isIdentifier(evolvedModel, modelParameter.name) || !isObjectExpression(updates)) return;
|
|
12978
|
+
for (const property of updates.properties) {
|
|
12979
|
+
if (property.type !== "Property" || property.computed || !isIdentifier(property.key) || !isArrowFunction(property.value)) continue;
|
|
12980
|
+
const replacement = expressionReturnedBy(property.value);
|
|
12981
|
+
if (isIdentifier(replacement, nextChildParameter.name)) return property.key.name;
|
|
12982
|
+
}
|
|
12983
|
+
};
|
|
12984
|
+
const isEvoCall$1 = (node, references) => {
|
|
12985
|
+
if (references === void 0) return exists(staticMemberPath(node.callee), (path) => {
|
|
12986
|
+
const [namespace, helperName, extraMember] = [path.root.name, ...path.members];
|
|
12987
|
+
return namespace === "Struct" && helperName === "evo" && extraMember === void 0;
|
|
12988
|
+
}) || isIdentifier(node.callee, "evo");
|
|
12989
|
+
return exists(resolveFoldkitApiPath(references, node.callee), (path) => {
|
|
12990
|
+
const [namespace, helperName, extraMember] = path;
|
|
12991
|
+
return namespace === "Struct" && helperName === "evo" && extraMember === void 0;
|
|
12992
|
+
});
|
|
12993
|
+
};
|
|
12994
|
+
const foldKind = (node, references) => {
|
|
12995
|
+
const foldKindForName = (helperName) => {
|
|
12996
|
+
if (helperName === "foldChild") return "Fold";
|
|
12997
|
+
if (helperName === "foldChildStep") return "Step";
|
|
12998
|
+
};
|
|
12999
|
+
if (references === void 0) return getOrUndefined(flatMap$3(staticMemberPath(node.callee), (path) => {
|
|
13000
|
+
const [namespace, helperName, extraMember] = [path.root.name, ...path.members];
|
|
13001
|
+
if (namespace !== "Update" || extraMember !== void 0) return none();
|
|
13002
|
+
if (helperName === void 0) return none();
|
|
13003
|
+
return fromNullishOr(foldKindForName(helperName));
|
|
13004
|
+
}));
|
|
13005
|
+
return getOrUndefined(flatMap$3(resolveFoldkitApiPath(references, node.callee), (path) => {
|
|
13006
|
+
const [namespace, helperName, extraMember] = path;
|
|
13007
|
+
if (namespace !== "Update" || extraMember !== void 0) return none();
|
|
13008
|
+
if (helperName === void 0) return none();
|
|
13009
|
+
return fromNullishOr(foldKindForName(helperName));
|
|
13010
|
+
}));
|
|
13011
|
+
};
|
|
13012
|
+
const isCombineCall = (node, references) => {
|
|
13013
|
+
if (references === void 0) return exists(staticMemberPath(node.callee), (path) => {
|
|
13014
|
+
const [namespace, helperName, extraMember] = [path.root.name, ...path.members];
|
|
13015
|
+
return namespace === "Update" && helperName === "combine" && extraMember === void 0;
|
|
13016
|
+
});
|
|
13017
|
+
return exists(resolveFoldkitApiPath(references, node.callee), (path) => {
|
|
13018
|
+
const [namespace, helperName, extraMember] = path;
|
|
13019
|
+
return namespace === "Update" && helperName === "combine" && extraMember === void 0;
|
|
13020
|
+
});
|
|
13021
|
+
};
|
|
13022
|
+
const moduleCallbacks$1 = (program) => {
|
|
13023
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
13024
|
+
for (const statement of program.body) {
|
|
13025
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
13026
|
+
if (declaration?.type !== "VariableDeclaration") continue;
|
|
13027
|
+
for (const declarator of declaration.declarations) if (isIdentifier(declarator.id) && isArrowFunction(declarator.init)) callbacks.set(declarator.id.name, declarator.init);
|
|
13028
|
+
}
|
|
13029
|
+
return callbacks;
|
|
13030
|
+
};
|
|
13031
|
+
const callbackValue$1 = (value, callbacks) => isArrowFunction(value) ? value : isIdentifier(value) ? callbacks.get(value.name) : void 0;
|
|
13032
|
+
const addChildField = (fieldsByParentModel, parentModel, childField) => {
|
|
13033
|
+
const fields = fieldsByParentModel.get(parentModel);
|
|
13034
|
+
if (fields === void 0) {
|
|
13035
|
+
fieldsByParentModel.set(parentModel, /* @__PURE__ */ new Set([childField]));
|
|
13036
|
+
return;
|
|
13037
|
+
}
|
|
13038
|
+
fields.add(childField);
|
|
13039
|
+
};
|
|
13040
|
+
const foldDefinitions = (program, scopes, references) => {
|
|
13041
|
+
const callbacks = moduleCallbacks$1(program);
|
|
13042
|
+
const byVariable = /* @__PURE__ */ new WeakMap();
|
|
13043
|
+
const fallbackByName = /* @__PURE__ */ new Map();
|
|
13044
|
+
const writeCallbacks = /* @__PURE__ */ new Set();
|
|
13045
|
+
for (const statement of program.body) {
|
|
13046
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
13047
|
+
if (declaration?.type !== "VariableDeclaration") continue;
|
|
13048
|
+
for (const declarator of declaration.declarations) {
|
|
13049
|
+
if (!isIdentifier(declarator.id) || !isCallExpression$1(declarator.init)) continue;
|
|
13050
|
+
const kind = foldKind(declarator.init, references);
|
|
13051
|
+
if (kind === void 0) continue;
|
|
13052
|
+
const [configuration] = declarator.init.arguments;
|
|
13053
|
+
if (!isObjectExpression(configuration)) continue;
|
|
13054
|
+
const read = callbackValue$1(propertyValue(configuration, "read"), callbacks);
|
|
13055
|
+
const write = callbackValue$1(propertyValue(configuration, "write"), callbacks);
|
|
13056
|
+
if (read === void 0 || write === void 0) continue;
|
|
13057
|
+
writeCallbacks.add(write);
|
|
13058
|
+
const readField = directReadField(read);
|
|
13059
|
+
const writeField = directWriteField(write, references);
|
|
13060
|
+
if (readField === void 0 || readField !== writeField) continue;
|
|
13061
|
+
const definition = {
|
|
13062
|
+
childField: readField,
|
|
13063
|
+
kind
|
|
13064
|
+
};
|
|
13065
|
+
fallbackByName.set(declarator.id.name, definition);
|
|
13066
|
+
for (const scope of scopes ?? []) for (const variable of scope.variables) if (variable.defs.some((candidate) => candidate.node === declarator)) byVariable.set(variable, definition);
|
|
13067
|
+
}
|
|
13068
|
+
}
|
|
13069
|
+
return {
|
|
13070
|
+
byVariable,
|
|
13071
|
+
fallbackByName,
|
|
13072
|
+
writeCallbacks
|
|
13073
|
+
};
|
|
13074
|
+
};
|
|
13075
|
+
const foldDefinitionFor = (callee, definitions, references) => {
|
|
13076
|
+
if (!isIdentifierReference(callee)) return;
|
|
13077
|
+
if (references === void 0) return definitions.fallbackByName.get(callee.name);
|
|
13078
|
+
return getOrUndefined(flatMap$3(resolvedVariable(references, callee), (variable) => fromNullishOr(definitions.byVariable.get(variable))));
|
|
13079
|
+
};
|
|
13080
|
+
const isArrayExpression = (value) => isNode$2(value) && value.type === "ArrayExpression";
|
|
13081
|
+
const childFieldsForCombine = (node, definitions, references) => {
|
|
13082
|
+
if (!isCombineCall(node, references)) return /* @__PURE__ */ new Set();
|
|
13083
|
+
const [firstArgument, secondArgument] = node.arguments;
|
|
13084
|
+
const steps = isArrayExpression(firstArgument) ? firstArgument : isArrayExpression(secondArgument) ? secondArgument : void 0;
|
|
13085
|
+
if (steps === void 0) return /* @__PURE__ */ new Set();
|
|
13086
|
+
const childFields = /* @__PURE__ */ new Set();
|
|
13087
|
+
for (const step of steps.elements) {
|
|
13088
|
+
if (isIdentifierReference(step)) {
|
|
13089
|
+
const definition = foldDefinitionFor(step, definitions, references);
|
|
13090
|
+
if (definition?.kind === "Step") childFields.add(definition.childField);
|
|
13091
|
+
continue;
|
|
13092
|
+
}
|
|
13093
|
+
if (!isCallExpression$1(step)) continue;
|
|
13094
|
+
const definition = foldDefinitionFor(step.callee, definitions, references);
|
|
13095
|
+
if (definition?.kind === "Fold") childFields.add(definition.childField);
|
|
13096
|
+
}
|
|
13097
|
+
return childFields;
|
|
13098
|
+
};
|
|
13099
|
+
const combineStepDefinitions = (program, scopes, definitions, references) => {
|
|
13100
|
+
const byVariable = /* @__PURE__ */ new WeakMap();
|
|
13101
|
+
const fallbackByName = /* @__PURE__ */ new Map();
|
|
13102
|
+
walk$1(program, (node) => {
|
|
13103
|
+
if (node.type !== "VariableDeclarator" || !isIdentifier(node.id) || !isCallExpression$1(node.init) || !isCombineCall(node.init, references)) return;
|
|
13104
|
+
const [firstArgument, secondArgument] = node.init.arguments;
|
|
13105
|
+
if (!isArrayExpression(firstArgument) || secondArgument !== void 0) return;
|
|
13106
|
+
const childFields = childFieldsForCombine(node.init, definitions, references);
|
|
13107
|
+
if (childFields.size === 0) return;
|
|
13108
|
+
const definition = { childFields };
|
|
13109
|
+
fallbackByName.set(node.id.name, definition);
|
|
13110
|
+
for (const scope of scopes ?? []) for (const variable of scope.variables) if (variable.defs.some((candidate) => candidate.node === node)) byVariable.set(variable, definition);
|
|
13111
|
+
});
|
|
13112
|
+
return {
|
|
13113
|
+
byVariable,
|
|
13114
|
+
fallbackByName
|
|
13115
|
+
};
|
|
13116
|
+
};
|
|
13117
|
+
const combineStepDefinitionFor = (callee, definitions, references) => {
|
|
13118
|
+
if (!isIdentifierReference(callee)) return;
|
|
13119
|
+
if (references === void 0) return definitions.fallbackByName.get(callee.name);
|
|
13120
|
+
return getOrUndefined(flatMap$3(resolvedVariable(references, callee), (variable) => fromNullishOr(definitions.byVariable.get(variable))));
|
|
13121
|
+
};
|
|
13122
|
+
const isDataLastFoldCall = (node, ancestors) => ancestors.some((ancestor) => isCallExpression$1(ancestor) && ancestor.callee === node);
|
|
13123
|
+
const invokedModel = (node, ancestors) => {
|
|
13124
|
+
const invocation = ancestors.find((candidate) => isCallExpression$1(candidate) && candidate.callee === node);
|
|
13125
|
+
if (invocation === void 0 || !isCallExpression$1(invocation)) return;
|
|
13126
|
+
const [parentModel] = invocation.arguments;
|
|
13127
|
+
return isIdentifierReference(parentModel) ? parentModel : void 0;
|
|
13128
|
+
};
|
|
13129
|
+
const combineModel = (node, ancestors) => {
|
|
13130
|
+
const [firstArgument, secondArgument] = node.arguments;
|
|
13131
|
+
if (isIdentifierReference(firstArgument) && isArrayExpression(secondArgument)) return firstArgument;
|
|
13132
|
+
return isArrayExpression(firstArgument) ? invokedModel(node, ancestors) : void 0;
|
|
13133
|
+
};
|
|
13134
|
+
const addDefinitionEvidence = (definition, parentModel, childFieldsByParentModel, fallbackChildFieldsByParentModel, references) => {
|
|
13135
|
+
if (references === void 0) {
|
|
13136
|
+
addChildField(fallbackChildFieldsByParentModel, parentModel.name, definition.childField);
|
|
13137
|
+
return;
|
|
13138
|
+
}
|
|
13139
|
+
map$5(resolvedVariable(references, parentModel), (variable) => addChildField(childFieldsByParentModel, variable, definition.childField));
|
|
13140
|
+
};
|
|
13141
|
+
const foldEvidence = (program, scopes, references) => {
|
|
13142
|
+
const definitions = foldDefinitions(program, scopes, references);
|
|
13143
|
+
const stepDefinitions = combineStepDefinitions(program, scopes, definitions, references);
|
|
13144
|
+
const childFieldsByParentModel = /* @__PURE__ */ new Map();
|
|
13145
|
+
const fallbackChildFieldsByParentModel = /* @__PURE__ */ new Map();
|
|
13146
|
+
walk$1(program, (node, ancestors) => {
|
|
13147
|
+
if (!isCallExpression$1(node)) return;
|
|
13148
|
+
const parentModelForCombine = isCombineCall(node, references) ? combineModel(node, ancestors) : void 0;
|
|
13149
|
+
if (parentModelForCombine !== void 0) for (const childField of childFieldsForCombine(node, definitions, references)) addDefinitionEvidence({
|
|
13150
|
+
childField,
|
|
13151
|
+
kind: "Step"
|
|
13152
|
+
}, parentModelForCombine, childFieldsByParentModel, fallbackChildFieldsByParentModel, references);
|
|
13153
|
+
if (isDataLastFoldCall(node, ancestors)) return;
|
|
13154
|
+
const stepDefinition = combineStepDefinitionFor(node.callee, stepDefinitions, references);
|
|
13155
|
+
if (stepDefinition !== void 0) {
|
|
13156
|
+
const [parentModel] = node.arguments;
|
|
13157
|
+
if (!isIdentifierReference(parentModel)) return;
|
|
13158
|
+
for (const childField of stepDefinition.childFields) addDefinitionEvidence({
|
|
13159
|
+
childField,
|
|
13160
|
+
kind: "Step"
|
|
13161
|
+
}, parentModel, childFieldsByParentModel, fallbackChildFieldsByParentModel, references);
|
|
13162
|
+
return;
|
|
13163
|
+
}
|
|
13164
|
+
const directDefinition = foldDefinitionFor(node.callee, definitions, references);
|
|
13165
|
+
const dataLastDefinition = isCallExpression$1(node.callee) ? foldDefinitionFor(node.callee.callee, definitions, references) : void 0;
|
|
13166
|
+
if (directDefinition === void 0 && (dataLastDefinition === void 0 || dataLastDefinition.kind !== "Fold")) return;
|
|
13167
|
+
const [firstArgument, secondArgument] = node.arguments;
|
|
13168
|
+
if (directDefinition?.kind === "Fold" && secondArgument === void 0) return;
|
|
13169
|
+
const definition = directDefinition ?? dataLastDefinition;
|
|
13170
|
+
const parentModel = firstArgument;
|
|
13171
|
+
if (!isIdentifierReference(parentModel) || definition === void 0) return;
|
|
13172
|
+
addDefinitionEvidence(definition, parentModel, childFieldsByParentModel, fallbackChildFieldsByParentModel, references);
|
|
13173
|
+
});
|
|
13174
|
+
return {
|
|
13175
|
+
childFieldsByParentModel,
|
|
13176
|
+
fallbackChildFieldsByParentModel,
|
|
13177
|
+
writeCallbacks: definitions.writeCallbacks
|
|
13178
|
+
};
|
|
13179
|
+
};
|
|
13180
|
+
const directChildEvoField = (node, evidence, references) => {
|
|
13181
|
+
if (!isEvoCall$1(node, references)) return;
|
|
13182
|
+
const [model, updates] = node.arguments;
|
|
13183
|
+
if (!isIdentifierReference(model) || !isObjectExpression(updates)) return;
|
|
13184
|
+
const childFields = references === void 0 ? evidence.fallbackChildFieldsByParentModel.get(model.name) : getOrUndefined(flatMap$3(resolvedVariable(references, model), (variable) => fromNullishOr(evidence.childFieldsByParentModel.get(variable))));
|
|
13185
|
+
if (childFields === void 0) return;
|
|
13186
|
+
for (const property of updates.properties) {
|
|
13187
|
+
if (property.type !== "Property" || property.computed || !isIdentifier(property.key) || !childFields.has(property.key.name) || !isArrowFunction(property.value)) continue;
|
|
13188
|
+
const [childModel] = property.value.params;
|
|
13189
|
+
if (!isIdentifier(childModel)) continue;
|
|
13190
|
+
for (const nestedUpdate of expressionsReturnedBy(property.value)) {
|
|
13191
|
+
if (!isCallExpression$1(nestedUpdate) || !isEvoCall$1(nestedUpdate, references)) continue;
|
|
13192
|
+
const [nestedModel] = nestedUpdate.arguments;
|
|
13193
|
+
if (isIdentifier(nestedModel, childModel.name)) return property.key.name;
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
};
|
|
13197
|
+
/** Flags parent-owned evo calls that directly update a child Submodel Model. */
|
|
13198
|
+
const noDirectSubmodelStateUpdate = Rule_exports.define({
|
|
13199
|
+
name: "no-direct-submodel-state-update",
|
|
13200
|
+
meta: Rule_exports.meta({
|
|
13201
|
+
type: "suggestion",
|
|
13202
|
+
description: "Keep Submodel state changes inside child-owned update capabilities."
|
|
13203
|
+
}),
|
|
13204
|
+
create: function* () {
|
|
13205
|
+
const ctx = yield* RuleContext;
|
|
13206
|
+
const scopes = ctx.sourceCode.scopeManager?.scopes;
|
|
13207
|
+
const references = scopes === void 0 ? void 0 : indexReferences(scopes);
|
|
13208
|
+
return { "Program:exit": (node) => {
|
|
13209
|
+
if (node.type !== "Program") return void_;
|
|
13210
|
+
const evidence = foldEvidence(node, scopes, references);
|
|
13211
|
+
if (evidence.childFieldsByParentModel.size === 0 && evidence.fallbackChildFieldsByParentModel.size === 0) return void_;
|
|
13212
|
+
return forEach((() => {
|
|
13213
|
+
const findings = [];
|
|
13214
|
+
walk$1(node, (candidate, ancestors) => {
|
|
13215
|
+
if (!isCallExpression$1(candidate)) return;
|
|
13216
|
+
if (ancestors.some((ancestor) => evidence.writeCallbacks.has(ancestor))) return;
|
|
13217
|
+
const fieldName = directChildEvoField(candidate, evidence, references);
|
|
13218
|
+
if (fieldName !== void 0) findings.push({
|
|
13219
|
+
node: candidate,
|
|
13220
|
+
fieldName
|
|
13221
|
+
});
|
|
13222
|
+
});
|
|
13223
|
+
return findings;
|
|
13224
|
+
})(), ({ node: findingNode, fieldName }) => ctx.report(Diagnostic_exports.make({
|
|
13225
|
+
node: findingNode,
|
|
13226
|
+
message: directSubmodelStateUpdateMessage(fieldName)
|
|
13227
|
+
})), { discard: true });
|
|
13228
|
+
} };
|
|
13229
|
+
}
|
|
13230
|
+
});
|
|
13231
|
+
//#endregion
|
|
12835
13232
|
//#region src/rules/no-disabling-dev-guardrails.ts
|
|
12836
13233
|
const FACTORY_NAMES = ["makeApplication", "makeElement"];
|
|
12837
13234
|
const FREEZE_MODEL_MESSAGE = "`freezeModel: false` turns off Foldkit's dev mutation guard. When on, the runtime deep-freezes the Model after init and after every update, so an accidental mutation throws at the exact write site during development and costs nothing in production. Fix the mutation instead of disabling the guard. A deliberate disable belongs behind an oxlint-disable comment at this line.";
|
|
@@ -12913,7 +13310,7 @@ const noDuplicateOnmountPerElement = Rule_exports.define({
|
|
|
12913
13310
|
const scopes = ctx.sourceCode.scopeManager?.scopes;
|
|
12914
13311
|
const references = scopes === void 0 ? void 0 : indexReferences(scopes);
|
|
12915
13312
|
return { ArrayExpression: (node) => {
|
|
12916
|
-
if (!isArrayExpression(node)) return void_;
|
|
13313
|
+
if (!isArrayExpression$1(node)) return void_;
|
|
12917
13314
|
if (node.elements.filter((element) => isOnMountCall(element, references)).length <= 1) return void_;
|
|
12918
13315
|
return ctx.report(Diagnostic_exports.make({
|
|
12919
13316
|
node,
|
|
@@ -13191,7 +13588,7 @@ const noEmptyChildrenArray = Rule_exports.define({
|
|
|
13191
13588
|
const maybeTarget = orElse(elementCallTarget(node, references), () => keyedCallTarget(node, references));
|
|
13192
13589
|
if (isNone(maybeTarget)) return void_;
|
|
13193
13590
|
const maybeChildren = childrenArgument(node);
|
|
13194
|
-
if (isNone(maybeChildren) || !isArrayExpression(maybeChildren.value) || isReadonlyArrayNonEmpty(maybeChildren.value.elements)) return void_;
|
|
13591
|
+
if (isNone(maybeChildren) || !isArrayExpression$1(maybeChildren.value) || isReadonlyArrayNonEmpty(maybeChildren.value.elements)) return void_;
|
|
13195
13592
|
if (isReadonlyArrayNonEmpty(ctx.sourceCode.getCommentsInside(maybeChildren.value))) return void_;
|
|
13196
13593
|
return ctx.report(Diagnostic_exports.make({
|
|
13197
13594
|
node: maybeChildren.value,
|
|
@@ -13203,7 +13600,7 @@ const noEmptyChildrenArray = Rule_exports.define({
|
|
|
13203
13600
|
//#endregion
|
|
13204
13601
|
//#region src/rules/no-empty-commands-array.ts
|
|
13205
13602
|
const COMMANDS_PROPERTY = "commands";
|
|
13206
|
-
const isEmptyCommandsProperty = (property) => contains$1(staticPropertyName(property), COMMANDS_PROPERTY) && isArrayExpression(property.value) && isReadonlyArrayEmpty(property.value.elements);
|
|
13603
|
+
const isEmptyCommandsProperty = (property) => contains$1(staticPropertyName(property), COMMANDS_PROPERTY) && isArrayExpression$1(property.value) && isReadonlyArrayEmpty(property.value.elements);
|
|
13207
13604
|
const removalRange$1 = (sourceText, object, property, index) => {
|
|
13208
13605
|
const maybePrevious = get$1(object.properties, index - 1);
|
|
13209
13606
|
if (isSome(maybePrevious)) return [maybePrevious.value.end, property.end];
|
|
@@ -14712,11 +15109,11 @@ const isEvoCall = (node, references) => {
|
|
|
14712
15109
|
if (callee.type === "Identifier") return callee.name === "evo";
|
|
14713
15110
|
return callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier" && callee.property.name === "evo";
|
|
14714
15111
|
};
|
|
14715
|
-
const isReturnStatement$
|
|
15112
|
+
const isReturnStatement$2 = (statement) => statement.type === "ReturnStatement";
|
|
14716
15113
|
const updaterBodyObject = (updater) => {
|
|
14717
15114
|
const body = updater.body;
|
|
14718
15115
|
if (body.type === "ObjectExpression") return some$1(body);
|
|
14719
|
-
if (body.type === "BlockStatement") return pipe(body.body, findFirst(isReturnStatement$
|
|
15116
|
+
if (body.type === "BlockStatement") return pipe(body.body, findFirst(isReturnStatement$2), flatMap$3((returnStatement) => fromNullishOr(returnStatement.argument)), filter$2(isObjectExpression));
|
|
14720
15117
|
return none();
|
|
14721
15118
|
};
|
|
14722
15119
|
const hasSpreadProperty = (objectExpression) => objectExpression.properties.some((property) => property.type === "SpreadElement");
|
|
@@ -14879,7 +15276,7 @@ const isMessageConstructorReference = (node, references) => {
|
|
|
14879
15276
|
};
|
|
14880
15277
|
const isMessageConstructorCall = (node, references) => isCallExpression$1(node) && isMessageConstructorReference(node.callee, references);
|
|
14881
15278
|
const isMessageWrappingArrow = (node, references) => {
|
|
14882
|
-
if (!isArrowFunction(node)) return false;
|
|
15279
|
+
if (!isArrowFunction$1(node)) return false;
|
|
14883
15280
|
if (!isBlockStatement$1(node.body)) return isMessageConstructorCall(node.body, references);
|
|
14884
15281
|
return node.body.body.some((statement) => statement.type === "ReturnStatement" && isMessageConstructorCall(statement.argument, references));
|
|
14885
15282
|
};
|
|
@@ -14897,8 +15294,8 @@ const isPipeMethodCall = (node) => exists(staticMemberPath(node.callee), (path)
|
|
|
14897
15294
|
const [member, extraMember] = path.members;
|
|
14898
15295
|
return member === "pipe" && extraMember === void 0;
|
|
14899
15296
|
});
|
|
14900
|
-
const returnedExpression = (node) => {
|
|
14901
|
-
if (!isArrowFunction(node)) return none();
|
|
15297
|
+
const returnedExpression$1 = (node) => {
|
|
15298
|
+
if (!isArrowFunction$1(node)) return none();
|
|
14902
15299
|
if (!isBlockStatement$1(node.body)) return some$1(node.body);
|
|
14903
15300
|
const returnStatements = node.body.body.filter((statement) => statement.type === "ReturnStatement");
|
|
14904
15301
|
return match(returnStatements, {
|
|
@@ -14908,7 +15305,7 @@ const returnedExpression = (node) => {
|
|
|
14908
15305
|
};
|
|
14909
15306
|
const resultPathMapsMessage = (node, references) => {
|
|
14910
15307
|
if (isEffectMapWrappingMessage(node, references)) return true;
|
|
14911
|
-
const maybeReturnedExpression = returnedExpression(node);
|
|
15308
|
+
const maybeReturnedExpression = returnedExpression$1(node);
|
|
14912
15309
|
if (isSome(maybeReturnedExpression)) return resultPathMapsMessage(maybeReturnedExpression.value, references);
|
|
14913
15310
|
if (!isCallExpression$1(node)) return false;
|
|
14914
15311
|
if (isPipeCall(node, references)) return node.arguments.some((argument) => resultPathMapsMessage(argument, references));
|
|
@@ -15034,7 +15431,7 @@ const NULLABLE_MEMBERS = /* @__PURE__ */ new Set([
|
|
|
15034
15431
|
"optional",
|
|
15035
15432
|
"optionalKey"
|
|
15036
15433
|
]);
|
|
15037
|
-
const isNode = (node) => typeof node === "object" && node !== null && "type" in node && typeof node.type === "string";
|
|
15434
|
+
const isNode$1 = (node) => typeof node === "object" && node !== null && "type" in node && typeof node.type === "string";
|
|
15038
15435
|
const schemaMemberName = (node, references) => {
|
|
15039
15436
|
if (references === void 0) return flatMap$3(staticMemberPath(node), (path) => {
|
|
15040
15437
|
const [member, extraMember] = path.members;
|
|
@@ -15049,7 +15446,7 @@ const schemaMemberName = (node, references) => {
|
|
|
15049
15446
|
};
|
|
15050
15447
|
const isSchemaStructCall = (node, references) => isCallExpression$1(node) && contains$1(schemaMemberName(node.callee, references), "Struct");
|
|
15051
15448
|
const nullableMember = (node, references) => {
|
|
15052
|
-
if (!isNode(node) || !exists(schemaMemberName(node, references), (name) => NULLABLE_MEMBERS.has(name))) return none();
|
|
15449
|
+
if (!isNode$1(node) || !exists(schemaMemberName(node, references), (name) => NULLABLE_MEMBERS.has(name))) return none();
|
|
15053
15450
|
return some$1(node);
|
|
15054
15451
|
};
|
|
15055
15452
|
const nullableFieldMember = (value, references) => {
|
|
@@ -15099,6 +15496,225 @@ const preferOptionOverNullableInModel = Rule_exports.define({
|
|
|
15099
15496
|
}
|
|
15100
15497
|
});
|
|
15101
15498
|
//#endregion
|
|
15499
|
+
//#region src/rules/require-fold-for-child-update-result.ts
|
|
15500
|
+
const sameNames = (actual, expected) => length(actual) === length(expected) && every(zip(actual, expected), ([actualName, expectedName]) => actualName === expectedName);
|
|
15501
|
+
const sameIdentifierName = (reference, binding) => reference.name === binding.name;
|
|
15502
|
+
const isFoldkitEvoCall = (node, references) => {
|
|
15503
|
+
if (references !== void 0) return exists(resolveFoldkitApiPath(references, node.callee), (path) => sameNames(path, ["Struct", "evo"]));
|
|
15504
|
+
return exists(staticMemberPath(node.callee), (path) => path.root.name === "evo" && isReadonlyArrayEmpty(path.members) || path.root.name === "Struct" && sameNames(path.members, ["evo"]));
|
|
15505
|
+
};
|
|
15506
|
+
const isFoldCall = (node, references) => {
|
|
15507
|
+
const isFoldPath = (path) => sameNames(path, ["Update", "foldChild"]) || sameNames(path, ["Update", "foldChildStep"]);
|
|
15508
|
+
if (references !== void 0) return exists(resolveFoldkitApiPath(references, node.callee), isFoldPath);
|
|
15509
|
+
return exists(staticMemberPath(node.callee), (path) => isFoldPath([path.root.name, ...path.members]));
|
|
15510
|
+
};
|
|
15511
|
+
const isInsideFoldWrite = (node, references) => {
|
|
15512
|
+
let ancestor = node.parent;
|
|
15513
|
+
while (ancestor !== null && ancestor !== void 0) {
|
|
15514
|
+
if (isObjectProperty(ancestor) && contains$1(staticPropertyName(ancestor), "write") && isObjectExpression(ancestor.parent) && isCallExpression$1(ancestor.parent.parent) && isFoldCall(ancestor.parent.parent, references)) return true;
|
|
15515
|
+
ancestor = ancestor.parent;
|
|
15516
|
+
}
|
|
15517
|
+
return false;
|
|
15518
|
+
};
|
|
15519
|
+
const isNode = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
15520
|
+
const walk = (root, visit) => {
|
|
15521
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
15522
|
+
const visitNode = (node) => {
|
|
15523
|
+
if (visited.has(node)) return;
|
|
15524
|
+
visited.add(node);
|
|
15525
|
+
visit(node);
|
|
15526
|
+
for (const [key, value] of Object.entries(node)) {
|
|
15527
|
+
if (key === "parent") continue;
|
|
15528
|
+
if (isNode(value)) visitNode(value);
|
|
15529
|
+
else if (globalThis.Array.isArray(value)) {
|
|
15530
|
+
for (const item of value) if (isNode(item)) visitNode(item);
|
|
15531
|
+
}
|
|
15532
|
+
}
|
|
15533
|
+
};
|
|
15534
|
+
visitNode(root);
|
|
15535
|
+
};
|
|
15536
|
+
const isReturnStatement$1 = (node) => isNode(node) && node.type === "ReturnStatement";
|
|
15537
|
+
const returnedExpression = (callback) => {
|
|
15538
|
+
if (callback.body.type !== "BlockStatement") return callback.body;
|
|
15539
|
+
const returnStatements = filter$1(callback.body.body, isReturnStatement$1);
|
|
15540
|
+
if (length(returnStatements) !== 1) return;
|
|
15541
|
+
return pipe(head(returnStatements), flatMap$3((statement) => fromNullishOr(statement.argument)), getOrUndefined);
|
|
15542
|
+
};
|
|
15543
|
+
const modelFieldNames = (functionNode) => {
|
|
15544
|
+
const [parentModel] = functionNode.params;
|
|
15545
|
+
const returned = returnedExpression(functionNode);
|
|
15546
|
+
if (!isIdentifier(parentModel) || returned === void 0) return /* @__PURE__ */ new Set();
|
|
15547
|
+
const fieldNames = /* @__PURE__ */ new Set();
|
|
15548
|
+
walk(returned, (node) => {
|
|
15549
|
+
pipe(staticMemberPath(node), filter$2((path) => length(path.members) === 1 && sameIdentifierName(path.root, parentModel)), flatMap$3((path) => head(path.members)), map$5((fieldName) => fieldNames.add(fieldName)));
|
|
15550
|
+
});
|
|
15551
|
+
return fieldNames;
|
|
15552
|
+
};
|
|
15553
|
+
const evoFieldNames = (functionNode, references) => {
|
|
15554
|
+
const [parentModel, nextChild] = functionNode.params;
|
|
15555
|
+
if (!isIdentifier(parentModel) || !isIdentifier(nextChild)) return /* @__PURE__ */ new Set();
|
|
15556
|
+
const fieldNames = /* @__PURE__ */ new Set();
|
|
15557
|
+
walk(functionNode.body, (node) => {
|
|
15558
|
+
if (!isCallExpression$1(node) || !isFoldkitEvoCall(node, references)) return;
|
|
15559
|
+
const [evoModel, updates] = node.arguments;
|
|
15560
|
+
if (!isIdentifier(evoModel) || !sameIdentifierName(evoModel, parentModel) || !isObjectExpression(updates)) return;
|
|
15561
|
+
for (const property of updates.properties) {
|
|
15562
|
+
if (!isObjectProperty(property) || property.computed || !isArrowFunction$1(property.value)) continue;
|
|
15563
|
+
const replacement = returnedExpression(property.value);
|
|
15564
|
+
if (!isIdentifier(replacement) || !sameIdentifierName(replacement, nextChild)) continue;
|
|
15565
|
+
pipe(staticPropertyName(property), map$5((fieldName) => fieldNames.add(fieldName)));
|
|
15566
|
+
}
|
|
15567
|
+
});
|
|
15568
|
+
return fieldNames;
|
|
15569
|
+
};
|
|
15570
|
+
const moduleCallbacks = (program) => {
|
|
15571
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
15572
|
+
for (const statement of program.body) {
|
|
15573
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
15574
|
+
if (declaration?.type !== "VariableDeclaration") continue;
|
|
15575
|
+
for (const declarator of declaration.declarations) if (isIdentifier(declarator.id) && isArrowFunction$1(declarator.init)) callbacks.set(declarator.id.name, declarator.init);
|
|
15576
|
+
}
|
|
15577
|
+
return callbacks;
|
|
15578
|
+
};
|
|
15579
|
+
const callbackValue = (value, callbacks) => {
|
|
15580
|
+
if (isArrowFunction$1(value)) return value;
|
|
15581
|
+
if (isIdentifier(value)) return callbacks.get(value.name);
|
|
15582
|
+
};
|
|
15583
|
+
const foldChildFieldsByNamespace = (program, references) => {
|
|
15584
|
+
const fieldsByNamespace = /* @__PURE__ */ new Map();
|
|
15585
|
+
const callbacks = moduleCallbacks(program);
|
|
15586
|
+
walk(program, (node) => {
|
|
15587
|
+
if (!isCallExpression$1(node) || !isFoldCall(node, references)) return;
|
|
15588
|
+
const [configuration] = node.arguments;
|
|
15589
|
+
if (!isObjectExpression(configuration)) return;
|
|
15590
|
+
let namespace;
|
|
15591
|
+
let readFields;
|
|
15592
|
+
let writeFields;
|
|
15593
|
+
for (const property of configuration.properties) {
|
|
15594
|
+
if (!isObjectProperty(property)) continue;
|
|
15595
|
+
if (contains$1(staticPropertyName(property), "update")) pipe(staticMemberPath(property.value), filter$2((path) => length(path.members) === 1), map$5((path) => {
|
|
15596
|
+
namespace = path.root.name;
|
|
15597
|
+
}));
|
|
15598
|
+
else if (contains$1(staticPropertyName(property), "read")) {
|
|
15599
|
+
const callback = callbackValue(property.value, callbacks);
|
|
15600
|
+
if (callback !== void 0) readFields = modelFieldNames(callback);
|
|
15601
|
+
} else if (contains$1(staticPropertyName(property), "write")) {
|
|
15602
|
+
const callback = callbackValue(property.value, callbacks);
|
|
15603
|
+
if (callback !== void 0) writeFields = evoFieldNames(callback, references);
|
|
15604
|
+
}
|
|
15605
|
+
}
|
|
15606
|
+
if (namespace === void 0 || readFields === void 0 || writeFields === void 0) return;
|
|
15607
|
+
for (const fieldName of readFields) {
|
|
15608
|
+
if (!writeFields.has(fieldName)) continue;
|
|
15609
|
+
const fieldNames = fieldsByNamespace.get(namespace) ?? /* @__PURE__ */ new Set();
|
|
15610
|
+
fieldNames.add(fieldName);
|
|
15611
|
+
fieldsByNamespace.set(namespace, fieldNames);
|
|
15612
|
+
}
|
|
15613
|
+
});
|
|
15614
|
+
return fieldsByNamespace;
|
|
15615
|
+
};
|
|
15616
|
+
const childModelField = (node) => flatMap$3(staticMemberPath(node), (path) => {
|
|
15617
|
+
const [fieldName, extraFieldName] = path.members;
|
|
15618
|
+
return fieldName !== void 0 && extraFieldName === void 0 ? some$1({
|
|
15619
|
+
fieldName,
|
|
15620
|
+
parentModel: path.root
|
|
15621
|
+
}) : none();
|
|
15622
|
+
});
|
|
15623
|
+
const childReturnOrigin = (node, references, foldFieldsByNamespace) => gen$2(function* () {
|
|
15624
|
+
if (!isIdentifier(node.id) || !isCallExpression$1(node.init)) return yield* none();
|
|
15625
|
+
const childModel = yield* flatMap$3(head(node.init.arguments), childModelField);
|
|
15626
|
+
const callee = yield* staticMemberPath(node.init.callee);
|
|
15627
|
+
const [helperName, extraHelperName] = callee.members;
|
|
15628
|
+
if (helperName === void 0 || extraHelperName !== void 0) return yield* none();
|
|
15629
|
+
if (references !== void 0) {
|
|
15630
|
+
const importedPath = yield* resolveImportedPath(references, node.init.callee);
|
|
15631
|
+
const [namespace, importedHelperName, extraImportedHelperName] = importedPath.localMembers;
|
|
15632
|
+
if (!importedPath.source.startsWith(".") || namespace === void 0 || importedHelperName === void 0 || extraImportedHelperName !== void 0 || namespace !== callee.root.name || importedHelperName !== helperName) return yield* none();
|
|
15633
|
+
} else if (callee.root.name.charAt(0) !== callee.root.name.charAt(0).toUpperCase()) return yield* none();
|
|
15634
|
+
const foldFields = foldFieldsByNamespace.get(callee.root.name);
|
|
15635
|
+
if (foldFields === void 0 || !foldFields.has(childModel.fieldName)) return yield* none();
|
|
15636
|
+
return {
|
|
15637
|
+
childFieldName: childModel.fieldName,
|
|
15638
|
+
helperLabel: `${callee.root.name}.${helperName}`,
|
|
15639
|
+
parentModel: childModel.parentModel
|
|
15640
|
+
};
|
|
15641
|
+
});
|
|
15642
|
+
const resultBinding = (updater) => pipe(returnedExpression(updater), fromNullishOr, flatMap$3(staticMemberPath), flatMap$3((path) => sameNames(path.members, ["model"]) ? some$1(path.root) : none()));
|
|
15643
|
+
const sameBinding = (a, b, references) => {
|
|
15644
|
+
if (references === void 0) return a.name === b.name;
|
|
15645
|
+
const maybeA = resolvedVariable(references, a);
|
|
15646
|
+
const maybeB = resolvedVariable(references, b);
|
|
15647
|
+
return isSome(maybeA) && isSome(maybeB) ? maybeA.value === maybeB.value : a.name === b.name;
|
|
15648
|
+
};
|
|
15649
|
+
const manualChildResultMessage = (childFieldName, helperLabel, resultName) => `The parent writes \`${resultName}.model\` into \`${childFieldName}\` after calling \`${helperLabel}\`. A child Return can include Commands or an OutMessage that this copy ignores. Use Update.foldChild or Update.foldChildStep.`;
|
|
15650
|
+
/** Flags a parent that copies a namespaced child helper or update Return's Model
|
|
15651
|
+
* into the matching evo field. It requires a matching Update.foldChild or
|
|
15652
|
+
* Update.foldChildStep reference in the file, and leaves ordinary helpers,
|
|
15653
|
+
* direct imports, aliases, init assembly, and Model-only reflect helpers alone. */
|
|
15654
|
+
const requireFoldForChildUpdateResult = Rule_exports.define({
|
|
15655
|
+
name: "require-fold-for-child-update-result",
|
|
15656
|
+
meta: Rule_exports.meta({
|
|
15657
|
+
type: "suggestion",
|
|
15658
|
+
description: "Fold child update results instead of manually copying their Models into a parent."
|
|
15659
|
+
}),
|
|
15660
|
+
create: function* () {
|
|
15661
|
+
const ctx = yield* RuleContext;
|
|
15662
|
+
const scopes = ctx.sourceCode.scopeManager?.scopes;
|
|
15663
|
+
const references = scopes === void 0 ? void 0 : indexReferences(scopes);
|
|
15664
|
+
const originsByVariable = /* @__PURE__ */ new WeakMap();
|
|
15665
|
+
const fallbackOriginsByName = /* @__PURE__ */ new Map();
|
|
15666
|
+
let foldFieldsByNamespace = /* @__PURE__ */ new Map();
|
|
15667
|
+
return {
|
|
15668
|
+
Program: (node) => {
|
|
15669
|
+
if (!isProgram$1(node)) return void_;
|
|
15670
|
+
foldFieldsByNamespace = foldChildFieldsByNamespace(node, references);
|
|
15671
|
+
if (references === void 0) return void_;
|
|
15672
|
+
for (const scope of scopes ?? []) for (const variable of scope.variables) {
|
|
15673
|
+
const definition = variable.defs.find((candidate) => candidate.type === "Variable" && isVariableDeclarator$1(candidate.node));
|
|
15674
|
+
if (definition === void 0 || !isVariableDeclarator$1(definition.node)) continue;
|
|
15675
|
+
pipe(childReturnOrigin(definition.node, references, foldFieldsByNamespace), map$5((origin) => originsByVariable.set(variable, origin)));
|
|
15676
|
+
}
|
|
15677
|
+
return void_;
|
|
15678
|
+
},
|
|
15679
|
+
VariableDeclarator: (node) => {
|
|
15680
|
+
if (references !== void 0 || !isVariableDeclarator$1(node)) return void_;
|
|
15681
|
+
return pipe(childReturnOrigin(node, references, foldFieldsByNamespace), match$3({
|
|
15682
|
+
onNone: () => void_,
|
|
15683
|
+
onSome: (origin) => {
|
|
15684
|
+
if (isIdentifier(node.id)) fallbackOriginsByName.set(node.id.name, origin);
|
|
15685
|
+
return void_;
|
|
15686
|
+
}
|
|
15687
|
+
}));
|
|
15688
|
+
},
|
|
15689
|
+
CallExpression: (node) => {
|
|
15690
|
+
if (!isCallExpression$1(node) || !isFoldkitEvoCall(node, references) || isInsideFoldWrite(node, references)) return void_;
|
|
15691
|
+
const [parentModel, updates] = node.arguments;
|
|
15692
|
+
if (!isIdentifier(parentModel) || !isObjectExpression(updates)) return void_;
|
|
15693
|
+
return forEach(updates.properties, (property) => {
|
|
15694
|
+
if (!isObjectProperty(property) || property.computed || !isArrowFunction$1(property.value)) return void_;
|
|
15695
|
+
return pipe(all({
|
|
15696
|
+
childFieldName: staticPropertyName(property),
|
|
15697
|
+
result: resultBinding(property.value)
|
|
15698
|
+
}), flatMap$3(({ childFieldName, result }) => {
|
|
15699
|
+
const origin = references === void 0 ? fallbackOriginsByName.get(result.name) : getOrUndefined(map$5(resolvedVariable(references, result), (variable) => originsByVariable.get(variable)));
|
|
15700
|
+
return origin === void 0 ? none() : some$1({
|
|
15701
|
+
childFieldName,
|
|
15702
|
+
origin,
|
|
15703
|
+
result
|
|
15704
|
+
});
|
|
15705
|
+
}), filter$2(({ childFieldName, origin, result }) => childFieldName === origin.childFieldName && sameBinding(parentModel, origin.parentModel, references) && !sameBinding(result, origin.parentModel, references)), match$3({
|
|
15706
|
+
onNone: () => void_,
|
|
15707
|
+
onSome: ({ childFieldName, origin, result }) => ctx.report(Diagnostic_exports.make({
|
|
15708
|
+
node: property.value,
|
|
15709
|
+
message: manualChildResultMessage(childFieldName, origin.helperLabel, result.name)
|
|
15710
|
+
}))
|
|
15711
|
+
}));
|
|
15712
|
+
}, { discard: true });
|
|
15713
|
+
}
|
|
15714
|
+
};
|
|
15715
|
+
}
|
|
15716
|
+
});
|
|
15717
|
+
//#endregion
|
|
15102
15718
|
//#region src/rules/require-rel-for-external-link.ts
|
|
15103
15719
|
const protectiveRelSubstrings = ["noopener", "noreferrer"];
|
|
15104
15720
|
const isTargetBlankElement = (element, references) => {
|
|
@@ -15130,7 +15746,7 @@ const requireRelForExternalLink = Rule_exports.define({
|
|
|
15130
15746
|
const scopes = ctx.sourceCode.scopeManager?.scopes;
|
|
15131
15747
|
const references = scopes === void 0 ? void 0 : indexReferences(scopes);
|
|
15132
15748
|
return { ArrayExpression: (node) => {
|
|
15133
|
-
if (!isArrayExpression(node)) return void_;
|
|
15749
|
+
if (!isArrayExpression$1(node)) return void_;
|
|
15134
15750
|
if (some(node.elements, isSpreadElement)) return void_;
|
|
15135
15751
|
const maybeTargetBlank = findFirst(node.elements, (element) => isTargetBlankElement(element, references));
|
|
15136
15752
|
if (isNone(maybeTargetBlank)) return void_;
|
|
@@ -15399,6 +16015,7 @@ const basePlugin = Plugin_exports.define({
|
|
|
15399
16015
|
"mount-factory-must-use-element": mountFactoryMustUseElement,
|
|
15400
16016
|
"no-array-index-view-keys": noArrayIndexViewKeys,
|
|
15401
16017
|
"no-child-message-construction-in-root": noChildMessageConstructionInRoot,
|
|
16018
|
+
"no-direct-submodel-state-update": noDirectSubmodelStateUpdate,
|
|
15402
16019
|
"no-disabling-dev-guardrails": noDisablingDevGuardrails,
|
|
15403
16020
|
"no-duplicate-onmount-per-element": noDuplicateOnmountPerElement,
|
|
15404
16021
|
"no-empty-children-array": noEmptyChildrenArray,
|
|
@@ -15420,6 +16037,7 @@ const basePlugin = Plugin_exports.define({
|
|
|
15420
16037
|
"prefer-command-mapmessage": preferCommandMapmessage,
|
|
15421
16038
|
"prefer-effect-module-names": preferEffectModuleNames,
|
|
15422
16039
|
"prefer-option-over-nullable-in-model": preferOptionOverNullableInModel,
|
|
16040
|
+
"require-fold-for-child-update-result": requireFoldForChildUpdateResult,
|
|
15423
16041
|
"require-rel-for-external-link": requireRelForExternalLink,
|
|
15424
16042
|
"selection-submodel-factory-at-module-scope": selectionSubmodelFactoryAtModuleScope,
|
|
15425
16043
|
"wrap-child-output-in-got-message": wrapChildOutputInGotMessage
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Flags
|
|
3
|
-
*
|
|
2
|
+
* Flags construction of a child Submodel Message variant from outside the
|
|
3
|
+
* child, such as Chat.Message.ClickedOpen(...) or an immutable local alias.
|
|
4
4
|
*/
|
|
5
5
|
export declare const noChildMessageConstructionInRoot: import("effect-oxlint").CreateRule;
|
|
6
6
|
//# sourceMappingURL=no-child-message-construction-in-root.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-child-message-construction-in-root.d.ts","sourceRoot":"","sources":["../../src/rules/no-child-message-construction-in-root.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-child-message-construction-in-root.d.ts","sourceRoot":"","sources":["../../src/rules/no-child-message-construction-in-root.ts"],"names":[],"mappings":"AA+YA;;;GAGG;AACH,eAAO,MAAM,gCAAgC,oCA0C3C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-direct-submodel-state-update.d.ts","sourceRoot":"","sources":["../../src/rules/no-direct-submodel-state-update.ts"],"names":[],"mappings":"AAuyBA,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,oCA+DtC,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Flags a parent that copies a namespaced child helper or update Return's Model
|
|
2
|
+
* into the matching evo field. It requires a matching Update.foldChild or
|
|
3
|
+
* Update.foldChildStep reference in the file, and leaves ordinary helpers,
|
|
4
|
+
* direct imports, aliases, init assembly, and Model-only reflect helpers alone. */
|
|
5
|
+
export declare const requireFoldForChildUpdateResult: import("effect-oxlint").CreateRule;
|
|
6
|
+
//# sourceMappingURL=require-fold-for-child-update-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-fold-for-child-update-result.d.ts","sourceRoot":"","sources":["../../src/rules/require-fold-for-child-update-result.ts"],"names":[],"mappings":"AA+bA;;;oFAGoF;AACpF,eAAO,MAAM,+BAA+B,oCAiJ1C,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foldkit/oxlint-plugin",
|
|
3
|
-
"version": "0.13.0-canary.
|
|
3
|
+
"version": "0.13.0-canary.4cb3546e35b7",
|
|
4
4
|
"description": "Oxlint rules for Foldkit applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"scripts": {
|
|
54
54
|
"clean": "rimraf dist recommended.json all.json *.tsbuildinfo",
|
|
55
55
|
"build": "pnpm run clean && rolldown -c rolldown.config.ts && tsc -b tsconfig.build.json && node scripts/generate-presets.mjs",
|
|
56
|
-
"test": "vitest run",
|
|
56
|
+
"test": "pnpm run typecheck:real-oxlint-fixtures && vitest run",
|
|
57
|
+
"typecheck:real-oxlint-fixtures": "node scripts/typecheck-real-oxlint-fixtures.mjs",
|
|
57
58
|
"typecheck": "tsc --noEmit"
|
|
58
59
|
}
|
|
59
60
|
}
|
package/recommended.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"foldkit/mount-factory-must-use-element": "error",
|
|
18
18
|
"foldkit/no-array-index-view-keys": "error",
|
|
19
19
|
"foldkit/no-child-message-construction-in-root": "error",
|
|
20
|
+
"foldkit/no-direct-submodel-state-update": "error",
|
|
20
21
|
"foldkit/no-disabling-dev-guardrails": "error",
|
|
21
22
|
"foldkit/no-duplicate-onmount-per-element": "error",
|
|
22
23
|
"foldkit/no-empty-children-array": "error",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"foldkit/prefer-command-mapmessage": "error",
|
|
39
40
|
"foldkit/prefer-effect-module-names": "error",
|
|
40
41
|
"foldkit/prefer-option-over-nullable-in-model": "error",
|
|
42
|
+
"foldkit/require-fold-for-child-update-result": "error",
|
|
41
43
|
"foldkit/require-rel-for-external-link": "error",
|
|
42
44
|
"foldkit/selection-submodel-factory-at-module-scope": "error",
|
|
43
45
|
"foldkit/wrap-child-output-in-got-message": "error"
|
|
@@ -100,6 +102,7 @@
|
|
|
100
102
|
"foldkit/mount-factory-must-use-element": "off",
|
|
101
103
|
"foldkit/no-array-index-view-keys": "off",
|
|
102
104
|
"foldkit/no-child-message-construction-in-root": "off",
|
|
105
|
+
"foldkit/no-direct-submodel-state-update": "off",
|
|
103
106
|
"foldkit/no-disabling-dev-guardrails": "off",
|
|
104
107
|
"foldkit/no-duplicate-onmount-per-element": "off",
|
|
105
108
|
"foldkit/no-empty-children-array": "off",
|
|
@@ -119,6 +122,7 @@
|
|
|
119
122
|
"foldkit/prefer-callable-message-constructor": "off",
|
|
120
123
|
"foldkit/prefer-command-mapmessage": "off",
|
|
121
124
|
"foldkit/prefer-option-over-nullable-in-model": "off",
|
|
125
|
+
"foldkit/require-fold-for-child-update-result": "off",
|
|
122
126
|
"foldkit/require-rel-for-external-link": "off",
|
|
123
127
|
"foldkit/selection-submodel-factory-at-module-scope": "off",
|
|
124
128
|
"foldkit/wrap-child-output-in-got-message": "off"
|