From b635488a6cb36462d2b24d44c05e406893fe1884 Mon Sep 17 00:00:00 2001 From: oxc-bot <176400334+oxc-bot@users.noreply.github.com> Date: Sun, 3 Nov 2024 03:26:14 +0000 Subject: [PATCH] Release 0.11.0 --- .../guide/usage/linter/generated-rules.md | 16 +- .../rules/eslint/prefer-object-has-own.md | 54 +++++ .../usage/linter/rules/import/no-commonjs.md | 86 ++++++++ .../usage/linter/rules/oxc/no-map-spread.md | 203 ++++++++++++++++++ .../linter/rules/react/style-prop-object.md | 50 +++++ .../linter/rules/unicorn/no-useless-spread.md | 2 +- 6 files changed, 404 insertions(+), 7 deletions(-) create mode 100644 src/docs/guide/usage/linter/rules/eslint/prefer-object-has-own.md create mode 100644 src/docs/guide/usage/linter/rules/import/no-commonjs.md create mode 100644 src/docs/guide/usage/linter/rules/oxc/no-map-spread.md create mode 100644 src/docs/guide/usage/linter/rules/react/style-prop-object.md diff --git a/src/docs/guide/usage/linter/generated-rules.md b/src/docs/guide/usage/linter/generated-rules.md index d63eb57a9ae..24a79e794ee 100644 --- a/src/docs/guide/usage/linter/generated-rules.md +++ b/src/docs/guide/usage/linter/generated-rules.md @@ -2,7 +2,7 @@ The progress of all rule implementations is tracked [here](https://github.com/oxc-project/oxc/issues/481). -- Total number of rules: 441 +- Total number of rules: 445 - Rules turned on by default: 97 ## Correctness (172): @@ -177,7 +177,7 @@ Code that is outright wrong or useless. | [no-unnecessary-await](/docs/guide/usage/linter/rules/unicorn/no-unnecessary-await.html) | unicorn | ✅ | 🛠️ | | [no-useless-fallback-in-spread](/docs/guide/usage/linter/rules/unicorn/no-useless-fallback-in-spread.html) | unicorn | ✅ | 🛠️ | | [no-useless-length-check](/docs/guide/usage/linter/rules/unicorn/no-useless-length-check.html) | unicorn | ✅ | 🚧 | -| [no-useless-spread](/docs/guide/usage/linter/rules/unicorn/no-useless-spread.html) | unicorn | ✅ | 🛠️ | +| [no-useless-spread](/docs/guide/usage/linter/rules/unicorn/no-useless-spread.html) | unicorn | ✅ | ⚠️🛠️️ | | [prefer-set-size](/docs/guide/usage/linter/rules/unicorn/prefer-set-size.html) | unicorn | ✅ | 🛠️ | | [prefer-string-starts-ends-with](/docs/guide/usage/linter/rules/unicorn/prefer-string-starts-ends-with.html) | unicorn | ✅ | 🛠️ | | [no-conditional-tests](/docs/guide/usage/linter/rules/vitest/no-conditional-tests.html) | vitest | | | @@ -195,7 +195,7 @@ Code that can be written to run faster. | [jsx-no-new-function-as-prop](/docs/guide/usage/linter/rules/react_perf/jsx-no-new-function-as-prop.html) | react_perf | | | | [jsx-no-new-object-as-prop](/docs/guide/usage/linter/rules/react_perf/jsx-no-new-object-as-prop.html) | react_perf | | | -## Restriction (57): +## Restriction (58): Lints which prevent the use of language and library features. Must not be enabled as a whole, should be considered on a case-by-case basis before enabling. | Rule name | Source | Default | Fixable? | @@ -219,6 +219,7 @@ Lints which prevent the use of language and library features. Must not be enable | [no-void](/docs/guide/usage/linter/rules/eslint/no-void.html) | eslint | | 🚧 | | [unicode-bom](/docs/guide/usage/linter/rules/eslint/unicode-bom.html) | eslint | | 🛠️ | | [no-amd](/docs/guide/usage/linter/rules/import/no-amd.html) | import | | | +| [no-commonjs](/docs/guide/usage/linter/rules/import/no-commonjs.html) | import | | | | [no-cycle](/docs/guide/usage/linter/rules/import/no-cycle.html) | import | | | | [no-default-export](/docs/guide/usage/linter/rules/import/no-default-export.html) | import | | | | [no-dynamic-require](/docs/guide/usage/linter/rules/import/no-dynamic-require.html) | import | | | @@ -258,7 +259,7 @@ Lints which prevent the use of language and library features. Must not be enable | [prefer-node-protocol](/docs/guide/usage/linter/rules/unicorn/prefer-node-protocol.html) | unicorn | | 🛠️ | | [prefer-number-properties](/docs/guide/usage/linter/rules/unicorn/prefer-number-properties.html) | unicorn | | 🚧 | -## Suspicious (21): +## Suspicious (22): code that is most likely wrong or useless. | Rule name | Source | Default | Fixable? | @@ -279,6 +280,7 @@ code that is most likely wrong or useless. | [iframe-missing-sandbox](/docs/guide/usage/linter/rules/react/iframe-missing-sandbox.html) | react | | 🚧 | | [jsx-no-comment-textnodes](/docs/guide/usage/linter/rules/react/jsx-no-comment-textnodes.html) | react | | | | [react-in-jsx-scope](/docs/guide/usage/linter/rules/react/react-in-jsx-scope.html) | react | | | +| [style-prop-object](/docs/guide/usage/linter/rules/react/style-prop-object.html) | react | | | | [no-confusing-non-null-assertion](/docs/guide/usage/linter/rules/typescript/no-confusing-non-null-assertion.html) | typescript | | 🚧 | | [no-extraneous-class](/docs/guide/usage/linter/rules/typescript/no-extraneous-class.html) | typescript | | | | [no-unnecessary-type-constraint](/docs/guide/usage/linter/rules/typescript/no-unnecessary-type-constraint.html) | typescript | | | @@ -363,7 +365,7 @@ Lints which are rather strict or have occasional false positives. | [prefer-type-error](/docs/guide/usage/linter/rules/unicorn/prefer-type-error.html) | unicorn | | 🛠️ | | [require-number-to-fixed-digits-argument](/docs/guide/usage/linter/rules/unicorn/require-number-to-fixed-digits-argument.html) | unicorn | | 🛠️ | -## Style (103): +## Style (104): Code that should be written in a more idiomatic way. | Rule name | Source | Default | Fixable? | @@ -384,6 +386,7 @@ Code that should be written in a more idiomatic way. | [no-ternary](/docs/guide/usage/linter/rules/eslint/no-ternary.html) | eslint | | | | [prefer-exponentiation-operator](/docs/guide/usage/linter/rules/eslint/prefer-exponentiation-operator.html) | eslint | | | | [prefer-numeric-literals](/docs/guide/usage/linter/rules/eslint/prefer-numeric-literals.html) | eslint | | 🛠️ | +| [prefer-object-has-own](/docs/guide/usage/linter/rules/eslint/prefer-object-has-own.html) | eslint | | 🛠️ | | [sort-imports](/docs/guide/usage/linter/rules/eslint/sort-imports.html) | eslint | | 🛠️ | | [sort-keys](/docs/guide/usage/linter/rules/eslint/sort-keys.html) | eslint | | 🚧 | | [consistent-test-it](/docs/guide/usage/linter/rules/jest/consistent-test-it.html) | jest | | 🛠️ | @@ -472,7 +475,7 @@ Code that should be written in a more idiomatic way. | [prefer-to-be-object](/docs/guide/usage/linter/rules/vitest/prefer-to-be-object.html) | vitest | | 🛠️ | | [prefer-to-be-truthy](/docs/guide/usage/linter/rules/vitest/prefer-to-be-truthy.html) | vitest | | 🛠️ | -## Nursery (10): +## Nursery (11): New lints that are still under development. | Rule name | Source | Default | Fixable? | @@ -482,6 +485,7 @@ New lints that are still under development. | [no-undef](/docs/guide/usage/linter/rules/eslint/no-undef.html) | eslint | | | | [no-unreachable](/docs/guide/usage/linter/rules/eslint/no-unreachable.html) | eslint | | | | [export](/docs/guide/usage/linter/rules/import/export.html) | import | | | +| [no-map-spread](/docs/guide/usage/linter/rules/oxc/no-map-spread.html) | oxc | | 🛠️💡 | | [no-return-in-finally](/docs/guide/usage/linter/rules/promise/no-return-in-finally.html) | promise | | | | [require-render-return](/docs/guide/usage/linter/rules/react/require-render-return.html) | react | | | | [rules-of-hooks](/docs/guide/usage/linter/rules/react/rules-of-hooks.html) | react | | | diff --git a/src/docs/guide/usage/linter/rules/eslint/prefer-object-has-own.md b/src/docs/guide/usage/linter/rules/eslint/prefer-object-has-own.md new file mode 100644 index 00000000000..bb0ed049b50 --- /dev/null +++ b/src/docs/guide/usage/linter/rules/eslint/prefer-object-has-own.md @@ -0,0 +1,54 @@ + + +# eslint/prefer-object-has-own + +
+ +🛠️ An auto-fix is available for this rule for some violations. + +
+ +### What it does + +Disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()` + +### Why is this bad? + +It is very common to write code like: + +```javascript +if (Object.prototype.hasOwnProperty.call(object, "foo")) { + console.log("has property foo"); +} +``` + +This is a common practice because methods on Object.prototype can sometimes be unavailable or redefined (see the no-prototype-builtins rule). +Introduced in ES2022, Object.hasOwn() is a shorter alternative to Object.prototype.hasOwnProperty.call(): + +```javascript +if (Object.hasOwn(object, "foo")) { + console.log("has property foo"); +} +``` + +### Examples + +Examples of **incorrect** code for this rule: + +```js +Object.prototype.hasOwnProperty.call(obj, "a"); +Object.hasOwnProperty.call(obj, "a"); +({}).hasOwnProperty.call(obj, "a"); +const hasProperty = Object.prototype.hasOwnProperty.call(object, property); +``` + +Examples of **correct** code for this rule: + +```js +Object.hasOwn(obj, "a"); +const hasProperty = Object.hasOwn(object, property); +``` + +## References + +- [Rule Source](https://github.com/oxc-project/oxc/blob/main/crates/oxc_linter/src/rules/eslint/prefer_object_has_own.rs) diff --git a/src/docs/guide/usage/linter/rules/import/no-commonjs.md b/src/docs/guide/usage/linter/rules/import/no-commonjs.md new file mode 100644 index 00000000000..67d39cc25cc --- /dev/null +++ b/src/docs/guide/usage/linter/rules/import/no-commonjs.md @@ -0,0 +1,86 @@ + + +# import/no-commonjs + +
+
+ +### What it does + +Forbids the use of CommonJS `require` calls. Also forbids `module.exports` and `exports.*`. + +### Why is this bad? + +ESM modules or Typescript uses `import` and `export` syntax instead of CommonJS syntax. +This rule enforces the use of more modern module systems to improve maintainability and consistency across the codebase. + +### Examples + +Examples of **incorrect** code for this rule: + +```js +var mod = require("fs"); + +var exports = (module.exports = {}); + +exports.sayHello = function () { + return "Hello"; +}; + +module.exports = "Hola"; +``` + +Examples of **correct** code for this rule: + +```js +var a = b && require("c"); + +if (typeof window !== "undefined") { + require("somelib"); +} + +var fs = null; +try { + fs = require("fs"); +} catch (error) {} +``` + +### Allow require + +If `allowRequire` option is set to `true`, `require` calls are valid: + +```js +var mod = require("./mod"); +``` + +but `module.exports` is reported as usual. + +### Allow conditional require + +By default, conditional requires are allowed, If the `allowConditionalRequire` option is set to `false`, they will be reported. + +### Allow primitive modules + +If `allowPrimitiveModules` option is set to true, the following is valid: + +```js +module.exports = "foo"; +module.exports = function rule(context) { + return { + /* ... */ + }; +}; +``` + +but this is still reported: + +```js +module.exports = { x: "y" }; +exports.z = function bark() { + /* ... */ +}; +``` + +## References + +- [Rule Source](https://github.com/oxc-project/oxc/blob/main/crates/oxc_linter/src/rules/import/no_commonjs.rs) diff --git a/src/docs/guide/usage/linter/rules/oxc/no-map-spread.md b/src/docs/guide/usage/linter/rules/oxc/no-map-spread.md new file mode 100644 index 00000000000..da55f3fbcbf --- /dev/null +++ b/src/docs/guide/usage/linter/rules/oxc/no-map-spread.md @@ -0,0 +1,203 @@ + + +# oxc/no-map-spread + +
+ +🛠️💡 An auto-fix and a suggestion are available for this rule for some violations. + +
+ +### What it does + +Disallow the use of object or array spreads in +[`Array.prototype.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) +and +[`Array.prototype.flatMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) +to add properties/elements to array items. + +This rule only seeks to report cases where the spread operator is used +to merge objects or arrays, not where it is used to copy them. + +### Why is this bad? + +Spreading is commonly used to add properties to objects in an array or +to combine several objects together. Unfortunately, spreads incur a +re-allocation for a new object, plus `O(n)` memory copies. + +```ts +// each object in scores gets shallow-copied. Since `scores` is never +// reused, spreading is inefficient. +function getDisplayData() { + const scores: Array<{ username: string; score: number }> = getScores(); + const displayData = scores.map((score) => ({ ...score, rank: getRank(score) })); + return displayData; +} +``` + +Unless you expect objects in the mapped array to be mutated later, it is +better to use [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). + +```ts +// `score` is mutated in place and is more performant. +function getDisplayData() { + const scores: Array<{ username: string; score: number }> = getScores(); + const displayData = scores.map((score) => Object.assign(score, { rank: getRank(score) })); + return displayData; +} +``` + +### Protecting from Mutations + +There are valid use cases for spreading objects in `map` calls, +specifically when you want consumers of returned arrays to be able to +mutate them without affecting the original data. This rule makes a +best-effort attempt to avoid reporting on these cases. + +Spreads on class instance properties are completely ignored: + +```ts +class AuthorsDb { + #authors = []; + public getAuthorsWithBooks() { + return this.#authors.map((author) => ({ + // protects against mutations, giving the callee their own + // deep(ish) copy of the author object. + ...author, + books: getBooks(author), + })); + } +} +``` + +Spreads on arrays that are re-read after the `map` call are also ignored +by default. Configure this behavior with the `ignoreRereads` option. + +``` +/* "oxc/no-map-spread": ["error", { "ignoreRereads": true }] */ +const scores = getScores(); +const displayData = scores.map(score => ({ ...score, rank: getRank(score) })); +console.log(scores); // scores is re-read after the map call +``` + +#### Arrays + +In the case of array spreads, +[`Array.prototype.concat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) +or +[`Array.prototype.push`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) +should be used wherever possible. These have slignly different semantics +than array spreads, since spreading works on iterables while `concat` +and `push` work only on arrays. + +```ts +let arr = [1, 2, 3]; +let set = new Set([4]); + +let a = [...arr, ...set]; // [1, 2, 3, 4] +let b = arr.concat(set); // [1, 2, 3, Set(1)] + +// Alternative that is more performant than spreading but still has the +// same semantics. Unfortunately, it is more verbose. +let c = arr.concat(Array.from(set)); // [1, 2, 3, 4] + +// You could also use `Symbol.isConcatSpreadable` +set[Symbol.isConcatSpreadable] = true; +let d = arr.concat(set); // [1, 2, 3, 4] +``` + +### Automatic Fixing + +This rule can automatically fix violations caused by object spreads, but +does not fix arrays. Object spreads will get replaced with +[`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). Array fixing may be added in the future. + +Object expressions with a single element (the spread) are not fixed. + +```js +arr.map((x) => ({ ...x })); // not fixed +``` + +A `fix` is available (using `--fix`) for objects with "normal" elements before the +spread. Since `Object.apply` mutates the first argument, and a new +object will be created with those elements, the spread identifier will +not be mutated. In effect, the spread semantics are preserved + +```js +// before +arr.map(({ x, y }) => ({ x, ...y })); + +// after +arr.map(({ x, y }) => Object.assign({ x }, y)); +``` + +A suggestion (using `--fix-suggestions`) is provided when a spread is +the first property in an object. This fix mutates the spread identifier, +meaning it could have unintended side effects. + +```js +// before +arr.map(({ x, y }) => ({ ...x, y })); +arr.map(({ x, y }) => ({ ...x, y })); + +// after +arr.map(({ x, y }) => Object.assign(x, { y })); +arr.map(({ x, y }) => Object.assign(x, y)); +``` + +### Examples + +Examples of **incorrect** code for this rule: + +```js +const arr = [{ a: 1 }, { a: 2 }, { a: 3 }]; +const arr2 = arr.map((obj) => ({ ...obj, b: obj.a * 2 })); +``` + +Examples of **correct** code for this rule: + +```ts +const arr = [{ a: 1 }, { a: 2 }, { a: 3 }]; +arr.map((obj) => Object.assign(obj, { b: obj.a * 2 })); + +// instance properties are ignored +class UsersDb { + #users = []; + public get users() { + // clone users, providing caller with their own deep(ish) copy. + return this.#users.map((user) => ({ ...user })); + } +} +``` + +```tsx +function UsersTable({ users }) { + const usersWithRoles = users.map((user) => ({ ...user, role: getRole(user) })); + + return ( + + {usersWithRoles.map((user) => ( + + + + + ))} + + + {/* re-read of users */} + + + +
{user.name}{user.role}
Total users: {users.length}
+ ); +} +``` + +### References + +- [ECMA262 - Object spread evaluation semantics](https://262.ecma-international.org/15.0/index.html#sec-runtime-semantics-propertydefinitionevaluation) +- [JSPerf - `concat` vs array spread performance](https://jsperf.app/pihevu) + +## References + +- [Rule Source](https://github.com/oxc-project/oxc/blob/main/crates/oxc_linter/src/rules/oxc/no_map_spread.rs) diff --git a/src/docs/guide/usage/linter/rules/react/style-prop-object.md b/src/docs/guide/usage/linter/rules/react/style-prop-object.md new file mode 100644 index 00000000000..d7f2f83d2bb --- /dev/null +++ b/src/docs/guide/usage/linter/rules/react/style-prop-object.md @@ -0,0 +1,50 @@ + + +# react/style-prop-object + +
+
+ +### What it does + +Require that the value of the prop `style` be an object or a variable that is an object. + +### Why is this bad? + +The `style` prop expects an object mapping from style properties to values when using JSX. + +### Examples + +Examples of **incorrect** code for this rule: + +```jsx +
+
+ +const styles = true; +
+ +React.createElement("div", { style: "color: 'red'" }); +React.createElement("div", { style: true }); +React.createElement("Hello", { style: true }); +const styles = true; +React.createElement("div", { style: styles }); +``` + +Examples of **correct** code for this rule: + +```jsx +
+ +const styles = { color: "red" }; +
+ +React.createElement("div", { style: { color: 'red' }}); +React.createElement("Hello", { style: { color: 'red' }}); +const styles = { height: '100px' }; +React.createElement("div", { style: styles }); +``` + +## References + +- [Rule Source](https://github.com/oxc-project/oxc/blob/main/crates/oxc_linter/src/rules/react/style_prop_object.rs) diff --git a/src/docs/guide/usage/linter/rules/unicorn/no-useless-spread.md b/src/docs/guide/usage/linter/rules/unicorn/no-useless-spread.md index 08286837d64..a16e0423c52 100644 --- a/src/docs/guide/usage/linter/rules/unicorn/no-useless-spread.md +++ b/src/docs/guide/usage/linter/rules/unicorn/no-useless-spread.md @@ -7,7 +7,7 @@ This rule is turned on by default. -🛠️ An auto-fix is available for this rule for some violations. +⚠️🛠️️ A dangerous auto-fix is available for this rule.