@foldkit/oxlint-plugin 0.9.0 → 0.10.0-canary.a7edce98aebd

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 CHANGED
@@ -20,6 +20,7 @@
20
20
  "foldkit/no-duplicate-onmount-per-element": "error",
21
21
  "foldkit/no-empty-children-array": "error",
22
22
  "foldkit/no-empty-commands-array": "error",
23
+ "foldkit/no-empty-to-parent-out-message": "error",
23
24
  "foldkit/no-empty-object-tagged-call": "error",
24
25
  "foldkit/no-hand-rolled-command-struct": "error",
25
26
  "foldkit/no-hardcoded-route-strings": "error",
@@ -74,6 +75,7 @@
74
75
  "foldkit/no-duplicate-onmount-per-element": "off",
75
76
  "foldkit/no-empty-children-array": "off",
76
77
  "foldkit/no-empty-commands-array": "off",
78
+ "foldkit/no-empty-to-parent-out-message": "off",
77
79
  "foldkit/no-empty-object-tagged-call": "off",
78
80
  "foldkit/no-hand-rolled-command-struct": "off",
79
81
  "foldkit/no-hardcoded-route-strings": "off",
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ declare const _default: {
30
30
  readonly 'no-duplicate-onmount-per-element': import("effect-oxlint").CreateRule;
31
31
  readonly 'no-empty-children-array': import("effect-oxlint").CreateRule;
32
32
  readonly 'no-empty-commands-array': import("effect-oxlint").CreateRule;
33
+ readonly 'no-empty-to-parent-out-message': import("effect-oxlint").CreateRule;
33
34
  readonly 'no-empty-object-tagged-call': import("effect-oxlint").CreateRule;
34
35
  readonly 'no-hand-rolled-command-struct': import("effect-oxlint").CreateRule;
35
36
  readonly 'no-hardcoded-route-strings': import("effect-oxlint").CreateRule;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AA+DtC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDD,wBAMC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAiEtC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDD,wBAMC"}
package/dist/index.js CHANGED
@@ -11792,7 +11792,7 @@ var staticPropertyName2 = (property) => {
11792
11792
  return staticStringValue(property.key);
11793
11793
  };
11794
11794
  var isEmptyCommandsProperty = (property) => Option_exports.contains(staticPropertyName2(property), COMMANDS_PROPERTY) && isArrayExpression(property.value) && Array_exports.isReadonlyArrayEmpty(property.value.elements);
11795
- var removalRange = (object, property, index) => {
11795
+ var removalRange = (sourceText, object, property, index) => {
11796
11796
  const maybePrevious = Array_exports.get(object.properties, index - 1);
11797
11797
  if (Option_exports.isSome(maybePrevious)) {
11798
11798
  return [maybePrevious.value.end, property.end];
@@ -11801,7 +11801,15 @@ var removalRange = (object, property, index) => {
11801
11801
  if (Option_exports.isSome(maybeNext)) {
11802
11802
  return [property.start, maybeNext.value.start];
11803
11803
  }
11804
- return property.range;
11804
+ const trailingComma = /^\s*,/.exec(sourceText.slice(property.end, object.end));
11805
+ if (trailingComma === null) {
11806
+ return property.range;
11807
+ }
11808
+ const maybeMatchedText = Array_exports.head(trailingComma);
11809
+ if (Option_exports.isNone(maybeMatchedText)) {
11810
+ return property.range;
11811
+ }
11812
+ return [property.start, property.end + maybeMatchedText.value.length];
11805
11813
  };
11806
11814
  var emptyCommandsMessage = "Return { model } instead of the literal { model, commands: [] }. Return a computed Commands collection directly, even when it may be empty.";
11807
11815
  var noEmptyCommandsArray = Rule_exports.define({
@@ -11822,6 +11830,10 @@ var noEmptyCommandsArray = Rule_exports.define({
11822
11830
  node.properties,
11823
11831
  (property) => property.type === "SpreadElement"
11824
11832
  );
11833
+ const hasDynamicComputedProperty = Array_exports.some(
11834
+ node.properties,
11835
+ (property) => isObjectProperty(property) && property.computed && Option_exports.isNone(staticPropertyName2(property))
11836
+ );
11825
11837
  const commandProperties = Array_exports.filter(
11826
11838
  node.properties,
11827
11839
  (property) => isObjectProperty(property) && Option_exports.contains(staticPropertyName2(property), COMMANDS_PROPERTY)
@@ -11839,13 +11851,15 @@ var noEmptyCommandsArray = Rule_exports.define({
11839
11851
  node: property,
11840
11852
  message: emptyCommandsMessage
11841
11853
  });
11842
- if (hasSpread || hasComments || Array_exports.isReadonlyArrayNonEmpty(Array_exports.drop(commandProperties, 1))) {
11854
+ if (hasSpread || hasDynamicComputedProperty || hasComments || Array_exports.isReadonlyArrayNonEmpty(Array_exports.drop(commandProperties, 1))) {
11843
11855
  return ctx.report(diagnostic);
11844
11856
  }
11845
11857
  return ctx.report(
11846
11858
  Diagnostic_exports.withFix(
11847
11859
  diagnostic,
11848
- (fixer) => fixer.removeRange(removalRange(node, property, index))
11860
+ (fixer) => fixer.removeRange(
11861
+ removalRange(ctx.sourceCode.text, node, property, index)
11862
+ )
11849
11863
  )
11850
11864
  );
11851
11865
  },
@@ -11936,6 +11950,135 @@ var noEmptyObjectTaggedCall = Rule_exports.define({
11936
11950
  }
11937
11951
  });
11938
11952
 
11953
+ // src/rules/no-empty-to-parent-out-message.ts
11954
+ var TO_PARENT_OUT_MESSAGE_PROPERTY = "toParentOutMessage";
11955
+ var isObjectProperty2 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "Property";
11956
+ var staticPropertyName3 = (property) => {
11957
+ if (!property.computed && isIdentifier(property.key)) {
11958
+ return Option_exports.some(property.key.name);
11959
+ }
11960
+ return staticStringValue(property.key);
11961
+ };
11962
+ var isArrowFunctionExpression3 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
11963
+ var isFunctionExpression = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "FunctionExpression";
11964
+ var isMapperFunction = (node) => isArrowFunctionExpression3(node) && !node.async || isFunctionExpression(node) && !node.async && !node.generator;
11965
+ var isIdentifierReference = (node) => isIdentifier(node);
11966
+ var indexReferences = (scopes) => {
11967
+ const references = /* @__PURE__ */ new WeakMap();
11968
+ for (const scope3 of scopes) {
11969
+ for (const reference of scope3.references) {
11970
+ references.set(reference.identifier, reference);
11971
+ }
11972
+ }
11973
+ return references;
11974
+ };
11975
+ var isUnshadowedUndefined = (references, node) => {
11976
+ if (!isIdentifierReference(node) || node.name !== "undefined") {
11977
+ return false;
11978
+ }
11979
+ if (references === void 0) {
11980
+ return true;
11981
+ }
11982
+ const reference = references.get(node);
11983
+ return reference !== void 0 && (reference.resolved === null || Array_exports.isArrayEmpty(reference.resolved.defs));
11984
+ };
11985
+ var directlyReturnsUndefined = (references, mapper) => {
11986
+ if (mapper.body === null) {
11987
+ return false;
11988
+ }
11989
+ if (mapper.body.type !== "BlockStatement") {
11990
+ return isUnshadowedUndefined(references, mapper.body);
11991
+ }
11992
+ const [firstStatement, secondStatement] = mapper.body.body;
11993
+ return firstStatement?.type === "ReturnStatement" && secondStatement === void 0 && isUnshadowedUndefined(references, firstStatement.argument);
11994
+ };
11995
+ var isEmptyMapperProperty = (references, property) => property.kind === "init" && Option_exports.contains(
11996
+ staticPropertyName3(property),
11997
+ TO_PARENT_OUT_MESSAGE_PROPERTY
11998
+ ) && isMapperFunction(property.value) && directlyReturnsUndefined(references, property.value);
11999
+ var removalRange2 = (sourceText, object, property, index) => {
12000
+ const maybePrevious = Array_exports.get(object.properties, index - 1);
12001
+ if (Option_exports.isSome(maybePrevious)) {
12002
+ return [maybePrevious.value.end, property.end];
12003
+ }
12004
+ const maybeNext = Array_exports.get(object.properties, index + 1);
12005
+ if (Option_exports.isSome(maybeNext)) {
12006
+ return [property.start, maybeNext.value.start];
12007
+ }
12008
+ const trailingComma = /^\s*,/.exec(sourceText.slice(property.end, object.end));
12009
+ if (trailingComma === null) {
12010
+ return property.range;
12011
+ }
12012
+ const maybeMatchedText = Array_exports.head(trailingComma);
12013
+ if (Option_exports.isNone(maybeMatchedText)) {
12014
+ return property.range;
12015
+ }
12016
+ return [property.start, property.end + maybeMatchedText.value.length];
12017
+ };
12018
+ var emptyMapperMessage = "Omit toParentOutMessage. This mapper directly returns undefined, so it forwards nothing to the parent.";
12019
+ var noEmptyToParentOutMessage = Rule_exports.define({
12020
+ name: "no-empty-to-parent-out-message",
12021
+ meta: Rule_exports.meta({
12022
+ type: "suggestion",
12023
+ description: "Omit an inline toParentOutMessage mapper that directly returns undefined.",
12024
+ fixable: "code"
12025
+ }),
12026
+ create: function* () {
12027
+ const ctx = yield* RuleContext;
12028
+ const scopes = ctx.sourceCode.scopeManager?.scopes;
12029
+ const references = scopes === void 0 ? void 0 : indexReferences(scopes);
12030
+ return {
12031
+ ObjectExpression: (node) => {
12032
+ if (!isObjectExpression(node)) {
12033
+ return Effect_exports.void;
12034
+ }
12035
+ const hasSpread = Array_exports.some(
12036
+ node.properties,
12037
+ (property) => property.type === "SpreadElement"
12038
+ );
12039
+ const hasDynamicComputedProperty = Array_exports.some(
12040
+ node.properties,
12041
+ (property) => isObjectProperty2(property) && property.computed && Option_exports.isNone(staticPropertyName3(property))
12042
+ );
12043
+ const mapperProperties = Array_exports.filter(
12044
+ node.properties,
12045
+ (property) => isObjectProperty2(property) && Option_exports.contains(
12046
+ staticPropertyName3(property),
12047
+ TO_PARENT_OUT_MESSAGE_PROPERTY
12048
+ )
12049
+ );
12050
+ const hasComments = Array_exports.isReadonlyArrayNonEmpty(
12051
+ ctx.sourceCode.getCommentsInside(node)
12052
+ );
12053
+ return Effect_exports.forEach(
12054
+ node.properties.entries(),
12055
+ ([index, property]) => {
12056
+ if (!isObjectProperty2(property) || !isEmptyMapperProperty(references, property)) {
12057
+ return Effect_exports.void;
12058
+ }
12059
+ const diagnostic = Diagnostic_exports.make({
12060
+ node: property,
12061
+ message: emptyMapperMessage
12062
+ });
12063
+ if (hasSpread || hasDynamicComputedProperty || hasComments || Array_exports.isReadonlyArrayNonEmpty(Array_exports.drop(mapperProperties, 1))) {
12064
+ return ctx.report(diagnostic);
12065
+ }
12066
+ return ctx.report(
12067
+ Diagnostic_exports.withFix(
12068
+ diagnostic,
12069
+ (fixer) => fixer.removeRange(
12070
+ removalRange2(ctx.sourceCode.text, node, property, index)
12071
+ )
12072
+ )
12073
+ );
12074
+ },
12075
+ { discard: true }
12076
+ );
12077
+ }
12078
+ };
12079
+ }
12080
+ });
12081
+
11939
12082
  // src/rules/no-hand-rolled-command-struct.ts
11940
12083
  var COMMAND_STRUCT_SHAPES = [
11941
12084
  ["name", "effect"],
@@ -12151,7 +12294,7 @@ var staticMemberName = (node) => {
12151
12294
  }
12152
12295
  return void 0;
12153
12296
  };
12154
- var staticPropertyName3 = (property) => {
12297
+ var staticPropertyName4 = (property) => {
12155
12298
  if (property.key.type === "Identifier") {
12156
12299
  return property.key.name;
12157
12300
  }
@@ -12178,7 +12321,7 @@ var restrictedGlobalDiagnostic = (node, name) => Diagnostic_exports.make({
12178
12321
  node,
12179
12322
  message: `Global \`${name}\` is not portable across Foldkit server targets. Use a server API available everywhere you deploy or pass the value into the entry.`
12180
12323
  });
12181
- var indexReferences = (scopes) => {
12324
+ var indexReferences2 = (scopes) => {
12182
12325
  const references = /* @__PURE__ */ new WeakMap();
12183
12326
  for (const scope3 of scopes) {
12184
12327
  for (const reference of scope3.references) {
@@ -12199,7 +12342,7 @@ var noNonportableServerGlobals = Rule_exports.define({
12199
12342
  }),
12200
12343
  create: function* () {
12201
12344
  const ctx = yield* RuleContext;
12202
- const references = indexReferences(ctx.sourceCode.scopeManager.scopes);
12345
+ const references = indexReferences2(ctx.sourceCode.scopeManager.scopes);
12203
12346
  return {
12204
12347
  Identifier: (node) => {
12205
12348
  if (node.type !== "Identifier" || !restrictedGlobalNames.has(node.name) || isInsideTypeQuery(node)) {
@@ -12229,7 +12372,7 @@ var noNonportableServerGlobals = Rule_exports.define({
12229
12372
  if (property.type !== "Property") {
12230
12373
  return [];
12231
12374
  }
12232
- const name = staticPropertyName3(property);
12375
+ const name = staticPropertyName4(property);
12233
12376
  return name !== void 0 && restrictedGlobalNames.has(name) ? [{ name, property }] : [];
12234
12377
  });
12235
12378
  return isUnshadowedGlobalReference(references, source) ? Effect_exports.forEach(
@@ -12816,7 +12959,7 @@ var gotMessagePattern = /^Got\w*Message$/;
12816
12959
  var dynamicCalleeLabel = "<dynamic call>";
12817
12960
  var isWrapperExpression3 = (node) => typeof node === "object" && node !== null && "type" in node && typeof node.type === "string" && mapperWrapperTypes.includes(node.type) && "expression" in node;
12818
12961
  var unwrapExpression3 = (node) => isWrapperExpression3(node) ? unwrapExpression3(node.expression) : node;
12819
- var isArrowFunctionExpression3 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
12962
+ var isArrowFunctionExpression4 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ArrowFunctionExpression";
12820
12963
  var isBlockStatement2 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "BlockStatement";
12821
12964
  var isReturnStatement3 = (node) => typeof node === "object" && node !== null && "type" in node && node.type === "ReturnStatement";
12822
12965
  var resolveMemberPath2 = (node) => {
@@ -12843,7 +12986,7 @@ var commandMapperArrow = (node) => pipe(
12843
12986
  Option_exports.filter((calleePath) => commandMapperPaths.includes(calleePath)),
12844
12987
  Option_exports.flatMap(() => {
12845
12988
  const [, mapperArgument] = node.arguments;
12846
- return isArrowFunctionExpression3(mapperArgument) ? Option_exports.some(mapperArgument) : Option_exports.none();
12989
+ return isArrowFunctionExpression4(mapperArgument) ? Option_exports.some(mapperArgument) : Option_exports.none();
12847
12990
  })
12848
12991
  );
12849
12992
  var subscriptionLiftMapperArrow = (node) => {
@@ -12868,7 +13011,7 @@ var subscriptionLiftMapperArrow = (node) => {
12868
13011
  );
12869
13012
  }),
12870
13013
  Option_exports.flatMap(
12871
- (mapperValue) => isArrowFunctionExpression3(mapperValue) ? Option_exports.some(mapperValue) : Option_exports.none()
13014
+ (mapperValue) => isArrowFunctionExpression4(mapperValue) ? Option_exports.some(mapperValue) : Option_exports.none()
12872
13015
  )
12873
13016
  );
12874
13017
  };
@@ -12970,6 +13113,7 @@ var basePlugin = Plugin_exports.define({
12970
13113
  "no-duplicate-onmount-per-element": noDuplicateOnmountPerElement,
12971
13114
  "no-empty-children-array": noEmptyChildrenArray,
12972
13115
  "no-empty-commands-array": noEmptyCommandsArray,
13116
+ "no-empty-to-parent-out-message": noEmptyToParentOutMessage,
12973
13117
  "no-empty-object-tagged-call": noEmptyObjectTaggedCall,
12974
13118
  "no-hand-rolled-command-struct": noHandRolledCommandStruct,
12975
13119
  "no-hardcoded-route-strings": noHardcodedRouteStrings,
@@ -1 +1 @@
1
- {"version":3,"file":"no-empty-commands-array.d.ts","sourceRoot":"","sources":["../../src/rules/no-empty-commands-array.ts"],"names":[],"mappings":"AAqDA;;;2EAG2E;AAC3E,eAAO,MAAM,oBAAoB,oCA+D/B,CAAA"}
1
+ {"version":3,"file":"no-empty-commands-array.d.ts","sourceRoot":"","sources":["../../src/rules/no-empty-commands-array.ts"],"names":[],"mappings":"AA+DA;;;2EAG2E;AAC3E,eAAO,MAAM,oBAAoB,oCAyE/B,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Flags an inline `toParentOutMessage` mapper that directly returns
3
+ * `undefined`. That mapper forwards nothing to the parent, so the property
4
+ * should be omitted. This syntax-only rule does not inspect async functions,
5
+ * generators, getters, setters, or mappers referenced by name.
6
+ */
7
+ export declare const noEmptyToParentOutMessage: import("effect-oxlint").CreateRule;
8
+ //# sourceMappingURL=no-empty-to-parent-out-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-empty-to-parent-out-message.d.ts","sourceRoot":"","sources":["../../src/rules/no-empty-to-parent-out-message.ts"],"names":[],"mappings":"AAsJA;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,oCAgFpC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foldkit/oxlint-plugin",
3
- "version": "0.9.0",
3
+ "version": "0.10.0-canary.a7edce98aebd",
4
4
  "description": "Oxlint rules for Foldkit applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/recommended.json CHANGED
@@ -20,6 +20,7 @@
20
20
  "foldkit/no-duplicate-onmount-per-element": "error",
21
21
  "foldkit/no-empty-children-array": "error",
22
22
  "foldkit/no-empty-commands-array": "error",
23
+ "foldkit/no-empty-to-parent-out-message": "error",
23
24
  "foldkit/no-empty-object-tagged-call": "error",
24
25
  "foldkit/no-hand-rolled-command-struct": "error",
25
26
  "foldkit/no-hardcoded-route-strings": "error",
@@ -74,6 +75,7 @@
74
75
  "foldkit/no-duplicate-onmount-per-element": "off",
75
76
  "foldkit/no-empty-children-array": "off",
76
77
  "foldkit/no-empty-commands-array": "off",
78
+ "foldkit/no-empty-to-parent-out-message": "off",
77
79
  "foldkit/no-empty-object-tagged-call": "off",
78
80
  "foldkit/no-hand-rolled-command-struct": "off",
79
81
  "foldkit/no-hardcoded-route-strings": "off",