Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d45e840
`no-immediate-mutation`: Support array
fisker Oct 22, 2025
5d85d8e
Support Object
fisker Oct 22, 2025
4dcb522
Tests
fisker Oct 22, 2025
d2c548f
ASI issue.
fisker Oct 22, 2025
0b86cd0
Ignore used
fisker Oct 22, 2025
0260724
Add docs
fisker Oct 22, 2025
ad3d72d
Linting
fisker Oct 22, 2025
0d82df4
More tests
fisker Oct 22, 2025
83cebe4
Improve ASI handling
fisker Oct 23, 2025
08fefc9
Refactor to prepare for other cases
fisker Oct 23, 2025
10dcfc0
Add Tests
fisker Oct 23, 2025
5c89312
`Set` and `WeakSet`
fisker Oct 23, 2025
61a8cb6
Extract code
fisker Oct 23, 2025
17fdadc
Refactor
fisker Oct 23, 2025
5f16926
Add tests for map
fisker Oct 23, 2025
26e44e1
Update docs
fisker Oct 23, 2025
d4ab577
Linting
fisker Oct 23, 2025
682b013
Update docs
fisker Oct 23, 2025
6cd9776
FIX CODEBASE
fisker Oct 23, 2025
8b346ff
Update docs
fisker Oct 23, 2025
2ce2613
Update report range
fisker Oct 23, 2025
37cbf59
Fix type
fisker Oct 23, 2025
1dad343
Remove from the `unopinionated` preset
fisker Oct 23, 2025
589aeb1
Remove restrictions on `const`, apply to `let` and `var`
fisker Oct 23, 2025
3cce5d5
Linting
fisker Oct 23, 2025
47df49b
Fix typescript case
fisker Oct 23, 2025
48b5535
Merge branch 'main' into no-immediate-mutation
fisker Oct 24, 2025
b702622
Apply changes
fisker Oct 24, 2025
7999a91
Fix ASI issue
fisker Oct 24, 2025
2391ad0
Improve ASI fix
fisker Oct 24, 2025
485d860
Merge branch 'main' into no-immediate-mutation
fisker Oct 25, 2025
1f21850
Improve `removeExpressionStatement`
fisker Oct 25, 2025
2e842cd
Object.assign
fisker Oct 25, 2025
72c8155
Fix
fisker Oct 25, 2025
136f6db
Fix
fisker Oct 25, 2025
dc4e50b
Support multiple objects
fisker Oct 25, 2025
8946ac4
Update docs
fisker Oct 25, 2025
9877664
Ignore optional
fisker Oct 25, 2025
841d1b0
One more test
fisker Oct 25, 2025
62ab29e
Prepare for assignment
fisker Oct 25, 2025
ca983d9
Support assign
fisker Oct 25, 2025
fe05077
Update messages
fisker Oct 25, 2025
f01734f
Test assign
fisker Oct 25, 2025
e3acef0
Add types
fisker Oct 25, 2025
b89f23a
Update no-immediate-mutation.md
sindresorhus Oct 26, 2025
f85002a
Merge branch 'main' into no-immediate-mutation
sindresorhus Oct 26, 2025
393639a
Update to-eslint-problem.js
sindresorhus Oct 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions docs/rules/no-immediate-mutation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Disallow immediate mutation after variable assignment

💼🚫 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). This rule is _disabled_ in the ☑️ `unopinionated` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config).

🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).

<!-- end auto-generated rule header -->
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->

When you create a variable and immediately mutate it, you should instead include those changes in the initial value.

- Assign variable to an array literal and immediately mutate with `Array#{push,unshift}(…)`.
- Assign variable to an object literal and immediately assign another property.
- Assign variable to an object literal and immediately mutate with `Object.assign(…)`.
- Assign variable to a `Set` or `WeakSet` from an array literal and immediately adding an new element with `{Set,WeakSet}.add(…)`.
- Assign variable to a `Map` or `WeakMap` from an array literal and immediately set another key with `{Map,WeakMap}.set(…, …)`.

## Examples

```js
// ❌
const array = [1, 2];
array.push(3, 4);

// ✅
const array = [1, 2, 3, 4];
```

```js
// ❌
const array = [3, 4];
array.unshift(1, 2);

// ✅
const array = [1, 2, 3, 4];
```

```js
// ❌
const object = {foo: 1};
object.bar = 2;

// ✅
const object = {foo: 1, bar: 2};
```

```js
// ❌
const object = {foo: 1};
Object.assign(object, {bar: 2});

// ✅
const object = {foo: 1, bar: 2};
```

```js
// ❌
const object = {foo: 1};
Object.assign(object, bar);

// ✅
const object = {foo: 1, ...bar};
```

```js
// ❌
const set = new Set([1, 2]);
set.add(3);

// ✅
const set = new Set([1, 2, 3]);
```

```js
// ❌
const weakSet = new WeakSet([foo, bar]);
weakSet.add(baz);

// ✅
const weakSet = new WeakSet([foo, bar, baz]);
```

```js
// ❌
const map = new Map([
['foo', 1],
]);
map.set('bar', 2);

// ✅
const map = new Map([
['foo', 1],
['bar', 2],
]);
```

```js
// ❌
const weakMap = new WeakMap([
[foo, 1],
]);
weakMap.set(bar, 2);

// ✅
const weakMap = new WeakMap([
[foo, 1],
[bar, 2],
]);
```
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export default [
| [no-empty-file](docs/rules/no-empty-file.md) | Disallow empty files. | ✅ ☑️ | | |
| [no-for-loop](docs/rules/no-for-loop.md) | Do not use a `for` loop that can be replaced with a `for-of` loop. | ✅ | 🔧 | 💡 |
| [no-hex-escape](docs/rules/no-hex-escape.md) | Enforce the use of Unicode escapes instead of hexadecimal escapes. | ✅ ☑️ | 🔧 | |
| [no-immediate-mutation](docs/rules/no-immediate-mutation.md) | Disallow immediate mutation after variable assignment. | ✅ | 🔧 | 💡 |
| [no-instanceof-builtins](docs/rules/no-instanceof-builtins.md) | Disallow `instanceof` with built-in objects | ✅ ☑️ | 🔧 | 💡 |
| [no-invalid-fetch-options](docs/rules/no-invalid-fetch-options.md) | Disallow invalid options in `fetch()` and `new Request()`. | ✅ ☑️ | | |
| [no-invalid-remove-event-listener](docs/rules/no-invalid-remove-event-listener.md) | Prevent calling `EventTarget#removeEventListener()` with the result of an expression. | ✅ ☑️ | | |
Expand Down
9 changes: 5 additions & 4 deletions rules/fix/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ export {
removeMemberExpressionProperty,
} from './replace-member-expression-property.js';
export {default as removeMethodCall} from './remove-method-call.js';
export {default as replaceTemplateElement} from './replace-template-element.js';
export {default as replaceReferenceIdentifier} from './replace-reference-identifier.js';
export {default as renameVariable} from './rename-variable.js';
export {default as replaceNodeOrTokenAndSpacesBefore} from './replace-node-or-token-and-spaces-before.js';
export {default as removeExpressionStatement} from './remove-expression-statement.js';
export {default as removeSpacesAfter} from './remove-spaces-after.js';
export {default as removeSpecifier} from './remove-specifier.js';
export {default as removeObjectProperty} from './remove-object-property.js';
export {default as renameVariable} from './rename-variable.js';
export {default as replaceTemplateElement} from './replace-template-element.js';
export {default as replaceReferenceIdentifier} from './replace-reference-identifier.js';
export {default as replaceNodeOrTokenAndSpacesBefore} from './replace-node-or-token-and-spaces-before.js';
export {default as fixSpaceAroundKeyword} from './fix-space-around-keywords.js';
export {default as replaceStringRaw} from './replace-string-raw.js';
export {default as addParenthesizesToReturnOrThrowExpression} from './add-parenthesizes-to-return-or-throw-expression.js';
34 changes: 34 additions & 0 deletions rules/fix/remove-expression-statement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {isSemicolonToken} from '@eslint-community/eslint-utils';
const isWhitespaceOnly = text => /^\s*$/.test(text);

function removeExpressionStatement(expressionStatement, context, fixer, preserveSemiColon = false) {
const {sourceCode} = context;
const {lines} = sourceCode;
let endToken = expressionStatement;

if (preserveSemiColon) {
const [penultimateToken, lastToken] = sourceCode.getLastTokens(expressionStatement, 2);

if (isSemicolonToken(lastToken)) {
endToken = penultimateToken;
}
}

const startLocation = sourceCode.getLoc(expressionStatement).start;
const endLocation = sourceCode.getLoc(endToken).end;

const textBefore = lines[startLocation.line - 1].slice(0, startLocation.column);
const textAfter = lines[endLocation.line - 1].slice(endLocation.column);

let [start] = sourceCode.getRange(expressionStatement);
let [, end] = sourceCode.getRange(endToken);

if (isWhitespaceOnly(textBefore) && isWhitespaceOnly(textAfter)) {
start = Math.max(0, start - textBefore.length - 1);
end += textAfter.length;
}

return fixer.removeRange([start, end]);
}

export default removeExpressionStatement;
1 change: 1 addition & 0 deletions rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export {default as 'no-document-cookie'} from './no-document-cookie.js';
export {default as 'no-empty-file'} from './no-empty-file.js';
export {default as 'no-for-loop'} from './no-for-loop.js';
export {default as 'no-hex-escape'} from './no-hex-escape.js';
export {default as 'no-immediate-mutation'} from './no-immediate-mutation.js';
export {default as 'no-instanceof-builtins'} from './no-instanceof-builtins.js';
export {default as 'no-invalid-fetch-options'} from './no-invalid-fetch-options.js';
export {default as 'no-invalid-remove-event-listener'} from './no-invalid-remove-event-listener.js';
Expand Down
Loading
Loading