@taiga-ui/eslint-plugin-experience-next 0.468.0 → 0.469.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/README.md CHANGED
@@ -53,6 +53,7 @@ export default [
53
53
  | no-playwright-empty-fill | Enforce `clear()` over `fill('')` in Playwright tests | ✅ | 🔧 | |
54
54
  | no-project-as-in-ng-template | `ngProjectAs` has no effect inside `<ng-template>` or dynamic outlets | ✅ | | |
55
55
  | no-redundant-type-annotation | Disallow redundant type annotations when the type is already inferred from the initializer | ✅ | 🔧 | |
56
+ | no-side-effects-in-computed | Disallow observable side effects inside Angular `computed()` callbacks | ✅ | | |
56
57
  | no-signal-reads-after-await-in-reactive-context | Disallow signal reads after `await` inside reactive callbacks | ✅ | | |
57
58
  | no-string-literal-concat | Disallow string literal concatenation; merge adjacent literals into one | ✅ | 🔧 | |
58
59
  | no-untracked-outside-reactive-context | Disallow `untracked()` outside the synchronous body of reactive callbacks | ✅ | 🔧 | |
@@ -566,6 +567,37 @@ const doubled = computed(() => {
566
567
 
567
568
  ---
568
569
 
570
+ ## no-side-effects-in-computed
571
+
572
+ <sup>`✅ Recommended`</sup>
573
+
574
+ `computed()` should only derive a value from its inputs. This rule reports observable side effects inside Angular
575
+ `computed()` callbacks, including signal writes (`.set()`, `.update()`, `.mutate()`), assignments to captured state,
576
+ `++/--`, `delete`, and property mutations on objects that were not created inside the computation itself.
577
+
578
+ ```ts
579
+ // ❌ error
580
+ import {computed, signal} from '@angular/core';
581
+
582
+ const source = signal(0);
583
+ const target = signal(0);
584
+
585
+ const derived = computed(() => {
586
+ target.set(source() + 1);
587
+ return target();
588
+ });
589
+ ```
590
+
591
+ ```ts
592
+ // ✅ ok
593
+ import {computed, signal} from '@angular/core';
594
+
595
+ const source = signal(0);
596
+ const derived = computed(() => source() + 1);
597
+ ```
598
+
599
+ ---
600
+
569
601
  ## no-signal-reads-after-await-in-reactive-context
570
602
 
571
603
  <sup>`✅ Recommended`</sup>
package/index.d.ts CHANGED
@@ -53,6 +53,9 @@ declare const plugin: {
53
53
  } | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
54
54
  name: string;
55
55
  };
56
+ 'no-side-effects-in-computed': import("@typescript-eslint/utils/ts-eslint").RuleModule<"sideEffectInComputed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
57
+ name: string;
58
+ };
56
59
  'no-signal-reads-after-await-in-reactive-context': import("@typescript-eslint/utils/ts-eslint").RuleModule<"readAfterAwait", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
57
60
  name: string;
58
61
  };
package/index.esm.js CHANGED
@@ -908,6 +908,7 @@ var recommended = defineConfig([
908
908
  '@taiga-ui/experience-next/no-fully-untracked-effect': 'error',
909
909
  '@taiga-ui/experience-next/no-implicit-public': 'error',
910
910
  '@taiga-ui/experience-next/no-redundant-type-annotation': 'error',
911
+ '@taiga-ui/experience-next/no-side-effects-in-computed': 'error',
911
912
  '@taiga-ui/experience-next/no-signal-reads-after-await-in-reactive-context': 'error',
912
913
  '@taiga-ui/experience-next/no-untracked-outside-reactive-context': 'error',
913
914
  '@taiga-ui/experience-next/no-useless-untracked': 'error',
@@ -1333,8 +1334,8 @@ function intersect(a, b) {
1333
1334
  return a.some((type) => origin.has(type));
1334
1335
  }
1335
1336
 
1336
- const createRule$f = ESLintUtils.RuleCreator((name) => name);
1337
- var classPropertyNaming = createRule$f({
1337
+ const createRule$g = ESLintUtils.RuleCreator((name) => name);
1338
+ var classPropertyNaming = createRule$g({
1338
1339
  create(context, [configs]) {
1339
1340
  const parserServices = ESLintUtils.getParserServices(context);
1340
1341
  const typeChecker = parserServices.program.getTypeChecker();
@@ -1503,9 +1504,9 @@ function isExternalPureTuple(typeChecker, type) {
1503
1504
  return typeArgs.every((item) => isClassType(item));
1504
1505
  }
1505
1506
 
1506
- const createRule$e = ESLintUtils.RuleCreator((name) => name);
1507
+ const createRule$f = ESLintUtils.RuleCreator((name) => name);
1507
1508
  const MESSAGE_ID$7 = 'spreadArrays';
1508
- var flatExports = createRule$e({
1509
+ var flatExports = createRule$f({
1509
1510
  create(context) {
1510
1511
  const parserServices = ESLintUtils.getParserServices(context);
1511
1512
  const typeChecker = parserServices.program.getTypeChecker();
@@ -1711,8 +1712,8 @@ const config$2 = {
1711
1712
 
1712
1713
  const MESSAGE_ID$5 = 'invalid-injection-token-description';
1713
1714
  const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
1714
- const createRule$d = ESLintUtils.RuleCreator((name) => name);
1715
- const rule$g = createRule$d({
1715
+ const createRule$e = ESLintUtils.RuleCreator((name) => name);
1716
+ const rule$h = createRule$e({
1716
1717
  create(context) {
1717
1718
  return {
1718
1719
  'NewExpression[callee.name="InjectionToken"]'(node) {
@@ -1779,8 +1780,8 @@ const DEFAULT_OPTIONS = {
1779
1780
  importDeclaration: '^@taiga-ui*',
1780
1781
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
1781
1782
  };
1782
- const createRule$c = ESLintUtils.RuleCreator((name) => name);
1783
- const rule$f = createRule$c({
1783
+ const createRule$d = ESLintUtils.RuleCreator((name) => name);
1784
+ const rule$g = createRule$d({
1784
1785
  create(context) {
1785
1786
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
1786
1787
  const hasNonCodeExtension = (source) => {
@@ -1867,13 +1868,13 @@ const rule$f = createRule$c({
1867
1868
  name: 'no-deep-imports',
1868
1869
  });
1869
1870
 
1870
- const createRule$b = ESLintUtils.RuleCreator((name) => name);
1871
+ const createRule$c = ESLintUtils.RuleCreator((name) => name);
1871
1872
  const resolveCacheByOptions = new WeakMap();
1872
1873
  const nearestFileUpCache = new Map();
1873
1874
  const markerCache = new Map();
1874
1875
  const indexFileCache = new Map();
1875
1876
  const indexExportsCache = new Map();
1876
- var noDeepImportsToIndexedPackages = createRule$b({
1877
+ var noDeepImportsToIndexedPackages = createRule$c({
1877
1878
  create(context) {
1878
1879
  const parserServices = ESLintUtils.getParserServices(context);
1879
1880
  const program = parserServices.program;
@@ -2298,7 +2299,7 @@ const AFTER_RENDER_EFFECT_PHASES = new Map([
2298
2299
  ['read', 'afterRenderEffect().read'],
2299
2300
  ['write', 'afterRenderEffect().write'],
2300
2301
  ]);
2301
- function isReactiveCallback(node) {
2302
+ function isReactiveCallback$1(node) {
2302
2303
  return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
2303
2304
  node?.type === AST_NODE_TYPES.FunctionExpression);
2304
2305
  }
@@ -2322,7 +2323,7 @@ function isAngularCoreCall(node, program, exportedName) {
2322
2323
  }
2323
2324
  function appendFirstArgReactiveScope(scopes, call, kind) {
2324
2325
  const [arg] = call.arguments;
2325
- if (!isReactiveCallback(arg)) {
2326
+ if (!isReactiveCallback$1(arg)) {
2326
2327
  return;
2327
2328
  }
2328
2329
  scopes.push({ callback: arg, kind, owner: call, reportNode: call });
@@ -2334,7 +2335,7 @@ function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
2334
2335
  }
2335
2336
  const name = getPropertyName(property);
2336
2337
  const label = name ? labels.get(name) : null;
2337
- if (!label || !isReactiveCallback(property.value)) {
2338
+ if (!label || !isReactiveCallback$1(property.value)) {
2338
2339
  continue;
2339
2340
  }
2340
2341
  scopes.push({
@@ -2555,7 +2556,7 @@ function collectReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, progr
2555
2556
  });
2556
2557
  return reads;
2557
2558
  }
2558
- const rule$e = createUntrackedRule({
2559
+ const rule$f = createUntrackedRule({
2559
2560
  create(context) {
2560
2561
  const parserServices = ESLintUtils.getParserServices(context);
2561
2562
  const checker = parserServices.program.getTypeChecker();
@@ -2641,8 +2642,8 @@ const config$1 = {
2641
2642
  },
2642
2643
  };
2643
2644
 
2644
- const createRule$a = ESLintUtils.RuleCreator((name) => name);
2645
- const rule$d = createRule$a({
2645
+ const createRule$b = ESLintUtils.RuleCreator((name) => name);
2646
+ const rule$e = createRule$b({
2646
2647
  create(context) {
2647
2648
  const checkImplicitPublic = (node) => {
2648
2649
  const classRef = getClass(node);
@@ -2713,9 +2714,9 @@ function getClass(node) {
2713
2714
  return getClass(node.parent);
2714
2715
  }
2715
2716
 
2716
- const createRule$9 = ESLintUtils.RuleCreator((name) => name);
2717
+ const createRule$a = ESLintUtils.RuleCreator((name) => name);
2717
2718
  const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
2718
- const rule$c = createRule$9({
2719
+ const rule$d = createRule$a({
2719
2720
  create(context) {
2720
2721
  return {
2721
2722
  Program(node) {
@@ -2753,8 +2754,8 @@ const rule$c = createRule$9({
2753
2754
  name: 'no-legacy-peer-deps',
2754
2755
  });
2755
2756
 
2756
- const createRule$8 = ESLintUtils.RuleCreator((name) => name);
2757
- const rule$b = createRule$8({
2757
+ const createRule$9 = ESLintUtils.RuleCreator((name) => name);
2758
+ const rule$c = createRule$9({
2758
2759
  create(context) {
2759
2760
  const services = ESLintUtils.getParserServices(context);
2760
2761
  const checker = services.program.getTypeChecker();
@@ -2919,7 +2920,7 @@ const config = {
2919
2920
  },
2920
2921
  };
2921
2922
 
2922
- const createRule$7 = ESLintUtils.RuleCreator((name) => name);
2923
+ const createRule$8 = ESLintUtils.RuleCreator((name) => name);
2923
2924
  function collectArrayExpressions(node) {
2924
2925
  const result = [];
2925
2926
  if (node.type === AST_NODE_TYPES.ArrayExpression) {
@@ -2945,7 +2946,7 @@ function collectArrayExpressions(node) {
2945
2946
  }
2946
2947
  return result;
2947
2948
  }
2948
- const rule$a = createRule$7({
2949
+ const rule$b = createRule$8({
2949
2950
  create(context) {
2950
2951
  const parserServices = ESLintUtils.getParserServices(context);
2951
2952
  const typeChecker = parserServices.program.getTypeChecker();
@@ -3041,6 +3042,207 @@ const rule$a = createRule$7({
3041
3042
  name: 'no-redundant-type-annotation',
3042
3043
  });
3043
3044
 
3045
+ /**
3046
+ * Strips TypeScript-only wrapper nodes that have no runtime meaning:
3047
+ * `as` casts, non-null assertions (`!`), type assertions (`<T>expr`), and
3048
+ * optional-chain wrappers. Iterates until no more wrappers are found.
3049
+ */
3050
+ function unwrapExpression(expression) {
3051
+ let current = expression;
3052
+ let didUnwrap = true;
3053
+ while (didUnwrap) {
3054
+ didUnwrap = false;
3055
+ switch (current.type) {
3056
+ case AST_NODE_TYPES.ChainExpression:
3057
+ current = current.expression;
3058
+ didUnwrap = true;
3059
+ break;
3060
+ case AST_NODE_TYPES.TSAsExpression:
3061
+ current = current.expression;
3062
+ didUnwrap = true;
3063
+ break;
3064
+ case AST_NODE_TYPES.TSNonNullExpression:
3065
+ current = current.expression;
3066
+ didUnwrap = true;
3067
+ break;
3068
+ case AST_NODE_TYPES.TSTypeAssertion:
3069
+ current = current.expression;
3070
+ didUnwrap = true;
3071
+ break;
3072
+ }
3073
+ }
3074
+ return current;
3075
+ }
3076
+
3077
+ const createRule$7 = ESLintUtils.RuleCreator((name) => name);
3078
+ function isReactiveCallback(node) {
3079
+ return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
3080
+ node?.type === AST_NODE_TYPES.FunctionExpression);
3081
+ }
3082
+ function unwrapMutationTarget(node) {
3083
+ let current = node;
3084
+ while (current.type === AST_NODE_TYPES.TSAsExpression ||
3085
+ current.type === AST_NODE_TYPES.TSNonNullExpression ||
3086
+ current.type === AST_NODE_TYPES.TSTypeAssertion) {
3087
+ current = current.expression;
3088
+ }
3089
+ return current;
3090
+ }
3091
+ function collectMutationTargets(node) {
3092
+ const current = unwrapMutationTarget(node);
3093
+ switch (current.type) {
3094
+ case AST_NODE_TYPES.ArrayPattern:
3095
+ return current.elements.flatMap((element) => element ? collectMutationTargets(element) : []);
3096
+ case AST_NODE_TYPES.AssignmentPattern:
3097
+ return collectMutationTargets(current.left);
3098
+ case AST_NODE_TYPES.Identifier:
3099
+ return [current];
3100
+ case AST_NODE_TYPES.MemberExpression:
3101
+ return [current];
3102
+ case AST_NODE_TYPES.ObjectPattern:
3103
+ return current.properties.flatMap((property) => {
3104
+ if (property.type === AST_NODE_TYPES.RestElement) {
3105
+ return collectMutationTargets(property.argument);
3106
+ }
3107
+ return collectMutationTargets(property.value);
3108
+ });
3109
+ case AST_NODE_TYPES.RestElement:
3110
+ return collectMutationTargets(current.argument);
3111
+ default:
3112
+ return [];
3113
+ }
3114
+ }
3115
+ function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
3116
+ const tsNode = esTreeNodeToTSNodeMap.get(node);
3117
+ if (!tsNode) {
3118
+ return null;
3119
+ }
3120
+ return checker.getSymbolAtLocation(tsNode) ?? null;
3121
+ }
3122
+ function isLocalIdentifier(node, context) {
3123
+ const symbol = getSymbolAtNode(node, context.checker, context.esTreeNodeToTSNodeMap);
3124
+ if (!symbol) {
3125
+ return false;
3126
+ }
3127
+ return (symbol.declarations ?? []).some((declaration) => {
3128
+ const estreeDeclaration = context.tsNodeToESTreeNodeMap.get(declaration);
3129
+ return (!!estreeDeclaration &&
3130
+ isNodeInsideSynchronousReactiveScope(estreeDeclaration, context.callback));
3131
+ });
3132
+ }
3133
+ function isLocallyCreatedExpression(node, context) {
3134
+ const expression = unwrapExpression(node);
3135
+ switch (expression.type) {
3136
+ case AST_NODE_TYPES.ArrayExpression:
3137
+ case AST_NODE_TYPES.NewExpression:
3138
+ case AST_NODE_TYPES.ObjectExpression:
3139
+ return true;
3140
+ case AST_NODE_TYPES.Identifier:
3141
+ return isLocalIdentifier(expression, context);
3142
+ case AST_NODE_TYPES.MemberExpression:
3143
+ return isLocallyCreatedExpression(expression.object, context);
3144
+ default:
3145
+ return false;
3146
+ }
3147
+ }
3148
+ function hasObservableMutationTarget(node, context) {
3149
+ return collectMutationTargets(node).some((target) => {
3150
+ if (target.type === AST_NODE_TYPES.Identifier) {
3151
+ return !isLocalIdentifier(target, context);
3152
+ }
3153
+ return !isLocallyCreatedExpression(target.object, context);
3154
+ });
3155
+ }
3156
+ function reportSideEffect(node, inspectionContext, report) {
3157
+ const key = String(node.range);
3158
+ if (inspectionContext.reported.has(key)) {
3159
+ return;
3160
+ }
3161
+ inspectionContext.reported.add(key);
3162
+ report(node);
3163
+ }
3164
+ function inspectComputedBody(root, inspectionContext, report) {
3165
+ walkSynchronousAst(root, (node) => {
3166
+ if (node.type === AST_NODE_TYPES.CallExpression) {
3167
+ if (isWritableSignalWrite(node, inspectionContext.checker, inspectionContext.esTreeNodeToTSNodeMap)) {
3168
+ reportSideEffect(node, inspectionContext, report);
3169
+ }
3170
+ if (isAngularUntrackedCall(node, inspectionContext.program)) {
3171
+ const [arg] = node.arguments;
3172
+ if (isReactiveCallback(arg)) {
3173
+ inspectComputedBody(arg, inspectionContext, report);
3174
+ }
3175
+ return false;
3176
+ }
3177
+ if (node.callee.type === AST_NODE_TYPES.ArrowFunctionExpression ||
3178
+ node.callee.type === AST_NODE_TYPES.FunctionExpression) {
3179
+ inspectComputedBody(node.callee, inspectionContext, report);
3180
+ return false;
3181
+ }
3182
+ }
3183
+ if (node.type === AST_NODE_TYPES.AssignmentExpression &&
3184
+ hasObservableMutationTarget(node.left, inspectionContext)) {
3185
+ reportSideEffect(node.left, inspectionContext, report);
3186
+ }
3187
+ if (node.type === AST_NODE_TYPES.UpdateExpression &&
3188
+ hasObservableMutationTarget(node.argument, inspectionContext)) {
3189
+ reportSideEffect(node.argument, inspectionContext, report);
3190
+ }
3191
+ if (node.type === AST_NODE_TYPES.UnaryExpression &&
3192
+ node.operator === 'delete' &&
3193
+ hasObservableMutationTarget(node.argument, inspectionContext)) {
3194
+ reportSideEffect(node.argument, inspectionContext, report);
3195
+ }
3196
+ return;
3197
+ });
3198
+ }
3199
+ const rule$a = createRule$7({
3200
+ create(context) {
3201
+ const parserServices = ESLintUtils.getParserServices(context);
3202
+ const checker = parserServices.program.getTypeChecker();
3203
+ const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
3204
+ const tsNodeToESTreeNodeMap = parserServices.tsNodeToESTreeNodeMap;
3205
+ const { sourceCode } = context;
3206
+ const program = sourceCode.ast;
3207
+ return {
3208
+ CallExpression(node) {
3209
+ for (const scope of getReactiveScopes(node, program)) {
3210
+ if (scope.kind !== 'computed()') {
3211
+ continue;
3212
+ }
3213
+ const inspectionContext = {
3214
+ callback: scope.callback,
3215
+ checker,
3216
+ esTreeNodeToTSNodeMap,
3217
+ program,
3218
+ reported: new Set(),
3219
+ tsNodeToESTreeNodeMap,
3220
+ };
3221
+ inspectComputedBody(scope.callback, inspectionContext, (reportNode) => {
3222
+ context.report({
3223
+ data: { expression: sourceCode.getText(reportNode) },
3224
+ messageId: 'sideEffectInComputed',
3225
+ node: reportNode,
3226
+ });
3227
+ });
3228
+ }
3229
+ },
3230
+ };
3231
+ },
3232
+ meta: {
3233
+ docs: {
3234
+ description: 'Disallow observable side effects inside `computed()` callbacks so derivations stay pure',
3235
+ url: 'https://angular.dev/guide/signals',
3236
+ },
3237
+ messages: {
3238
+ sideEffectInComputed: '`{{ expression }}` causes a side effect inside `computed()`. Keep computed derivations pure and move state changes to `effect()` or outside the computation.',
3239
+ },
3240
+ schema: [],
3241
+ type: 'problem',
3242
+ },
3243
+ name: 'no-side-effects-in-computed',
3244
+ });
3245
+
3044
3246
  const rule$9 = createUntrackedRule({
3045
3247
  create(context) {
3046
3248
  const parserServices = ESLintUtils.getParserServices(context);
@@ -3819,38 +4021,6 @@ const rule$6 = createUntrackedRule({
3819
4021
  name: 'no-useless-untracked',
3820
4022
  });
3821
4023
 
3822
- /**
3823
- * Strips TypeScript-only wrapper nodes that have no runtime meaning:
3824
- * `as` casts, non-null assertions (`!`), type assertions (`<T>expr`), and
3825
- * optional-chain wrappers. Iterates until no more wrappers are found.
3826
- */
3827
- function unwrapExpression(expression) {
3828
- let current = expression;
3829
- let didUnwrap = true;
3830
- while (didUnwrap) {
3831
- didUnwrap = false;
3832
- switch (current.type) {
3833
- case AST_NODE_TYPES.ChainExpression:
3834
- current = current.expression;
3835
- didUnwrap = true;
3836
- break;
3837
- case AST_NODE_TYPES.TSAsExpression:
3838
- current = current.expression;
3839
- didUnwrap = true;
3840
- break;
3841
- case AST_NODE_TYPES.TSNonNullExpression:
3842
- current = current.expression;
3843
- didUnwrap = true;
3844
- break;
3845
- case AST_NODE_TYPES.TSTypeAssertion:
3846
- current = current.expression;
3847
- didUnwrap = true;
3848
- break;
3849
- }
3850
- }
3851
- return current;
3852
- }
3853
-
3854
4024
  const createRule$5 = ESLintUtils.RuleCreator((name) => name);
3855
4025
  const rule$5 = createRule$5({
3856
4026
  create(context, [{ printWidth }]) {
@@ -5415,16 +5585,17 @@ const plugin = {
5415
5585
  'decorator-key-sort': config$3,
5416
5586
  'flat-exports': flatExports,
5417
5587
  'html-logical-properties': config$2,
5418
- 'injection-token-description': rule$g,
5419
- 'no-deep-imports': rule$f,
5588
+ 'injection-token-description': rule$h,
5589
+ 'no-deep-imports': rule$g,
5420
5590
  'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
5421
- 'no-fully-untracked-effect': rule$e,
5591
+ 'no-fully-untracked-effect': rule$f,
5422
5592
  'no-href-with-router-link': config$1,
5423
- 'no-implicit-public': rule$d,
5424
- 'no-legacy-peer-deps': rule$c,
5425
- 'no-playwright-empty-fill': rule$b,
5593
+ 'no-implicit-public': rule$e,
5594
+ 'no-legacy-peer-deps': rule$d,
5595
+ 'no-playwright-empty-fill': rule$c,
5426
5596
  'no-project-as-in-ng-template': config,
5427
- 'no-redundant-type-annotation': rule$a,
5597
+ 'no-redundant-type-annotation': rule$b,
5598
+ 'no-side-effects-in-computed': rule$a,
5428
5599
  'no-signal-reads-after-await-in-reactive-context': rule$9,
5429
5600
  'no-string-literal-concat': rule$8,
5430
5601
  'no-untracked-outside-reactive-context': rule$7,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.468.0",
3
+ "version": "0.469.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const rule: ESLintUtils.RuleModule<"sideEffectInComputed", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default rule;