@harness-engineering/eslint-plugin 0.3.1 → 0.4.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/dist/index.d.ts CHANGED
@@ -28,6 +28,9 @@ declare const rules: {
28
28
  'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
29
29
  name: string;
30
30
  };
31
+ 'no-undefined-optional-assignment': _typescript_eslint_utils_ts_eslint.RuleModule<"undefinedOptionalAssignment", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
32
+ name: string;
33
+ };
31
34
  'no-unbounded-array-chains': _typescript_eslint_utils_ts_eslint.RuleModule<"unboundedArrayChain", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
32
35
  name: string;
33
36
  };
@@ -75,6 +78,9 @@ declare const plugin: {
75
78
  'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
76
79
  name: string;
77
80
  };
81
+ 'no-undefined-optional-assignment': _typescript_eslint_utils_ts_eslint.RuleModule<"undefinedOptionalAssignment", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
82
+ name: string;
83
+ };
78
84
  'no-unbounded-array-chains': _typescript_eslint_utils_ts_eslint.RuleModule<"unboundedArrayChain", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
79
85
  name: string;
80
86
  };
@@ -166,6 +172,9 @@ declare const configs: {
166
172
  'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
167
173
  name: string;
168
174
  };
175
+ 'no-undefined-optional-assignment': _typescript_eslint_utils_ts_eslint.RuleModule<"undefinedOptionalAssignment", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
176
+ name: string;
177
+ };
169
178
  'no-unbounded-array-chains': _typescript_eslint_utils_ts_eslint.RuleModule<"unboundedArrayChain", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
170
179
  name: string;
171
180
  };
@@ -232,6 +241,9 @@ declare const configs: {
232
241
  'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
233
242
  name: string;
234
243
  };
244
+ 'no-undefined-optional-assignment': _typescript_eslint_utils_ts_eslint.RuleModule<"undefinedOptionalAssignment", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
245
+ name: string;
246
+ };
235
247
  'no-unbounded-array-chains': _typescript_eslint_utils_ts_eslint.RuleModule<"unboundedArrayChain", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
236
248
  name: string;
237
249
  };
package/dist/index.js CHANGED
@@ -1073,6 +1073,69 @@ var require_path_normalization_default = createRule12({
1073
1073
  }
1074
1074
  });
1075
1075
 
1076
+ // src/rules/no-undefined-optional-assignment.ts
1077
+ import { ESLintUtils as ESLintUtils13 } from "@typescript-eslint/utils";
1078
+ var createRule13 = ESLintUtils13.RuleCreator(
1079
+ (name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
1080
+ );
1081
+ function unionIncludesUndefined(typeNode) {
1082
+ return typeNode?.type === "TSUnionType" && typeNode.types.some((t) => t.type === "TSUndefinedKeyword");
1083
+ }
1084
+ function declaredType(identifier, scope) {
1085
+ const ref = scope.references.find((r) => r.identifier === identifier);
1086
+ const name = ref?.resolved?.defs[0]?.name;
1087
+ if (name?.type !== "Identifier") return void 0;
1088
+ return name.typeAnnotation?.typeAnnotation;
1089
+ }
1090
+ function checksDefined(expr, name) {
1091
+ if (expr.type !== "BinaryExpression" || expr.operator !== "!==" && expr.operator !== "!=") {
1092
+ return false;
1093
+ }
1094
+ const refsName = (n) => n.type === "Identifier" && n.name === name;
1095
+ const isNullish = (n) => n.type === "Identifier" && n.name === "undefined" || n.type === "Literal" && n.value === null;
1096
+ return refsName(expr.left) && isNullish(expr.right) || refsName(expr.right) && isNullish(expr.left);
1097
+ }
1098
+ function isGuarded(node, name) {
1099
+ for (let cur = node.parent; cur; cur = cur.parent) {
1100
+ if (cur.type === "LogicalExpression" && cur.operator === "&&" && checksDefined(cur.left, name)) {
1101
+ return true;
1102
+ }
1103
+ }
1104
+ return false;
1105
+ }
1106
+ var no_undefined_optional_assignment_default = createRule13({
1107
+ name: "no-undefined-optional-assignment",
1108
+ meta: {
1109
+ type: "suggestion",
1110
+ docs: {
1111
+ description: "Disallow assigning a possibly-undefined variable directly to an object property, which breaks `exactOptionalPropertyTypes`; use a conditional spread instead."
1112
+ },
1113
+ messages: {
1114
+ undefinedOptionalAssignment: "Assigning possibly-undefined '{{name}}' directly to '{{field}}' breaks exactOptionalPropertyTypes. Use a conditional spread: ...({{name}} !== undefined && {{ '{' }} {{field}}: {{name}} {{ '}' }})."
1115
+ },
1116
+ schema: []
1117
+ },
1118
+ defaultOptions: [],
1119
+ create(context) {
1120
+ return {
1121
+ Property(node) {
1122
+ if (node.computed || node.value.type !== "Identifier") return;
1123
+ if (node.key.type !== "Identifier" && node.key.type !== "Literal") return;
1124
+ const value = node.value;
1125
+ const scope = context.sourceCode.getScope(value);
1126
+ if (!unionIncludesUndefined(declaredType(value, scope))) return;
1127
+ if (isGuarded(node, value.name)) return;
1128
+ const field = node.key.type === "Identifier" ? node.key.name : String(node.key.value);
1129
+ context.report({
1130
+ node: value,
1131
+ messageId: "undefinedOptionalAssignment",
1132
+ data: { name: value.name, field }
1133
+ });
1134
+ }
1135
+ };
1136
+ }
1137
+ });
1138
+
1076
1139
  // src/rules/index.ts
1077
1140
  var rules = {
1078
1141
  "enforce-doc-exports": enforce_doc_exports_default,
@@ -1083,6 +1146,7 @@ var rules = {
1083
1146
  "no-nested-loops-in-critical": no_nested_loops_in_critical_default,
1084
1147
  "no-process-env-in-spawn": no_process_env_in_spawn_default,
1085
1148
  "no-sync-io-in-async": no_sync_io_in_async_default,
1149
+ "no-undefined-optional-assignment": no_undefined_optional_assignment_default,
1086
1150
  "no-unbounded-array-chains": no_unbounded_array_chains_default,
1087
1151
  "no-unix-shell-command": no_unix_shell_command_default,
1088
1152
  "require-boundary-schema": require_boundary_schema_default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-engineering/eslint-plugin",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "ESLint plugin for harness engineering architectural constraints",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",