Skip to content

Commit

Permalink
1.5.3
Browse files Browse the repository at this point in the history
  • Loading branch information
SrBrahma committed Feb 12, 2024
1 parent 41ff24b commit 84d2108
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 144 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog


## 1.5.3

- Updated for Biome v1.5.3
- Now using https://biomejs.dev/linter/rules/ together with https://biomejs.dev/linter/rules-sources/ instead of https://github.com/biomejs/biome/discussions/3 as source of truth.

## 1.5.1

- Updated for Biome v1.5.1
Expand Down
Binary file modified bun.lockb
Binary file not shown.
21 changes: 13 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
"eqeqeq": "off",
"for-direction": "off",
"getter-return": "off",
"match_str_case_mismatch": "off",
"no-async-promise-executor": "off",
"no-case-declarations": "off",
"no-class-assign": "off",
Expand All @@ -29,6 +30,7 @@ module.exports = {
"no-dupe-keys": "off",
"no-duplicate-case": "off",
"no-else-return": "off",
"no-empty": "off",
"no-empty-character-class": "off",
"no-empty-pattern": "off",
"no-ex-assign": "off",
Expand Down Expand Up @@ -80,17 +82,23 @@ module.exports = {
"use-isnan": "off",
"valid-typeof": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/default-param-last": "off",
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/no-dupe-class-members": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-extra-non-null-assertion": "off",
"@typescript-eslint/no-extraneous-class": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-invalid-void-type": "off",
"@typescript-eslint/no-loss-of-precision": "off",
"@typescript-eslint/no-misused-new": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-redeclare": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/no-unnecessary-type-constraint": "off",
"@typescript-eslint/no-unsafe-declaration-merging": "off",
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/no-useless-empty-export": "off",
"@typescript-eslint/no-useless-template-literals": "off",
"@typescript-eslint/prefer-as-const": "off",
Expand All @@ -101,6 +109,7 @@ module.exports = {
"jsx-a11y/alt-text": "off",
"jsx-a11y/anchor-has-content": "off",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/aria-activedescendant-has-tabindex": "off",
"jsx-a11y/aria-props": "off",
"jsx-a11y/aria-proptypes": "off",
"jsx-a11y/aria-role": "off",
Expand All @@ -123,6 +132,7 @@ module.exports = {
"jsx-a11y/role-has-required-aria-props": "off",
"jsx-a11y/scope": "off",
"jsx-a11y/tabindex-no-positive": "off",
"mysticates/no-this-in-static": "off",
"react/button-has-type": "off",
"react/jsx-no-comment-textnodes": "off",
"react/jsx-no-duplicate-props": "off",
Expand All @@ -134,18 +144,13 @@ module.exports = {
"react/no-danger-with-children": "off",
"react/void-dom-elements-no-children": "off",
"react-hooks/exhaustive-deps": "off",
"simple-import-sort/imports": "off",
"stylistic/jsx-self-closing-comp": "off",
"unicorn/no-array-for-each": "off",
"unicorn/no-instanceof-array": "off",
"unicorn/no-static-only-class": "off",
"unicorn/no-typeof-undefined": "off",
"unicorn/no-useless-switch-case": "off",
"unicorn/prefer-array-flat-map": "off",
"simple-import-sort/imports": "off",
"@typescript-eslint/default-param-last": "off",
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/no-dupe-class-members": "off",
"@typescript-eslint/no-loss-of-precision": "off",
"@typescript-eslint/no-redeclare": "off",
"@typescript-eslint/no-useless-constructor": "off",
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-config-biome",
"version": "1.5.1",
"version": "1.5.3",
"description": "Disables ESLint rules that have a recommended and equivalent Biome rule",
"main": "index.js",
"scripts": {
Expand Down
19 changes: 19 additions & 0 deletions scripts/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import path from "path"

export const filenames = {
index: "index.js",
prettier: "eslint-config-prettier.js",
}
export const rootPath = path.resolve(import.meta.dir, "..")

export const extraRulesToDisable = [
"simple-import-sort/imports",
// Not being added for some reason, TODO check it and fix it
"no-new-symbol",
// https://github.com/biomejs/biome/pull/1801
"no-delete-var",
"no-return-assign",
"no-useless-computed-key",
"unicorn/no-static-only-class",
"unicorn/no-typeof-undefined",
]
76 changes: 76 additions & 0 deletions scripts/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { JSDOM } from "jsdom"

export const getRecommendedBiomeRules = async (): Promise<Array<string>> => {
const response = await fetch("https://biomejs.dev/linter/rules/")
const html = await response.text()

const dom = new JSDOM(html)
const document = dom.window.document

const rows = document.querySelectorAll("table tr")

const recommendedRules = [...rows]
.map((row) => {
const cells = row.querySelectorAll("td")
const rowTexts = [...cells].map((cell) => cell.textContent?.trim() ?? "")

return rowTexts
})
.filter((rowTexts) => rowTexts[2]?.includes("✅"))
.map((recommendedRowTexts) => recommendedRowTexts[0] ?? "")

return recommendedRules
}

type Equivalency = { eslint: string; biome: string }

export const getEquivalentRules = async (): Promise<Array<Equivalency>> => {
const response = await fetch("https://biomejs.dev/linter/rules-sources/")
const html = await response.text()

const dom = new JSDOM(html)
const document = dom.window.document

const tables = document.querySelectorAll("table")

const rowsTexts = [...tables].flatMap((table) => {
const tableHeaders = Array.from(table.querySelectorAll("th")).map(
(th) => th.textContent?.trim() ?? "",
)
const pluginName = tableHeaders[0]
?.split(" ")[0]
?.replace("eslint-plugin-", "")
?.replace("typescript", "@typescript-eslint")

if (!pluginName) throw new Error("Invalid plugin")

const prefix = ["Clippy", "ESLint"].includes(pluginName)
? ""
: `${pluginName}/`

const rows = [...table.querySelectorAll("tr")].filter((tr) =>
tr.querySelector("td"),
)

const rowsTexts = rows.map((row) => {
const rowTexts = [...row.querySelectorAll("td, td")].map(
(cell) => cell.textContent?.trim() ?? "",
)

rowTexts[0] = prefix + rowTexts[0]
// Sometimes they have `${biomeRuleName} (inspired)`. We get just the first word.
rowTexts[1] = rowTexts[1]?.split(" ")[0] ?? ""

return rowTexts
})

return rowsTexts
})

const equivalentRules: Array<Equivalency> = rowsTexts.flatMap((rowTexts) => ({
eslint: rowTexts[0] ?? "",
biome: rowTexts[1] ?? "",
}))

return equivalentRules
}
160 changes: 25 additions & 135 deletions scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,143 +1,33 @@
/**
* Code initially based on the html tables extraction: https://github.com/biomejs/biome/discussions/3#discussioncomment-7910787.
* Thanks, Dani Guardiola!
*
* Attribution for eslint-config-prettier is available at the ATTRIBUTION.md and in the eslint-config-prettier.js.
*/
/** Attribution for eslint-config-prettier is available at the ATTRIBUTION.md and in the eslint-config-prettier.js. */

import fs from "fs"
import path from "path"
import { JSDOM } from "jsdom"

const extraRulesToDisable = ["simple-import-sort/imports"]
const rootPath = path.resolve(import.meta.dir, "..")

const filenames = {
index: "index.js",
prettier: "eslint-config-prettier.js",
}

const getTdString = (row: Element, column: number) =>
(
row.querySelector(`td:nth-child(${column})`) as HTMLTableCellElement
).textContent?.trim()

type Plugin = { id: string; prefix: string }

/** Returns the ESLint rules for the equivalent Biome's rules that are recommended */
const getEslintEquivalentRulesForPlugin = (
plugin: Plugin,
document: Document,
): Array<string> => {
const table = (
document.querySelector(`#user-content-${plugin.id}`)?.parentNode as Element
).nextElementSibling as HTMLTableElement | null

if (!table) throw new Error(`Missing table for plugin ${plugin.id}`)

const rows = Array.from(table.querySelectorAll("tbody > tr"))

const eslintRules: Array<string> = []

rows.forEach((row) => {
const eslintRule = getTdString(row, 1)
const biomeRule = getTdString(row, 3)
const isBiomeRecommended = getTdString(row, 4)?.includes("✅")

if (eslintRule && biomeRule && isBiomeRecommended) {
eslintRules.push(plugin.prefix + eslintRule)
}
})

return eslintRules
}

const getEslintEquivalentRules = async (): Promise<Array<string>> => {
const plugins: Array<{ id: string; prefix: string }> = [
{ id: "eslint", prefix: "" },
{ id: "typescript-eslint", prefix: "@typescript-eslint/" },
{ id: "eslint-plugin-jest", prefix: "jest/" },
{ id: "eslint-plugin-jsx-a11y", prefix: "jsx-a11y/" },
{ id: "eslint-plugin-react", prefix: "react/" },
{ id: "eslint-plugin-react-hooks", prefix: "react-hooks/" },
{ id: "eslint-plugin-unicorn", prefix: "unicorn/" },
]

const response = await fetch("https://github.com/biomejs/biome/discussions/3")
const text = await response.text()
const dom = new JSDOM(text)
const document = dom.window.document

return plugins.flatMap((plugin) =>
getEslintEquivalentRulesForPlugin(plugin, document),
)
}

/** https://typescript-eslint.io/rules/#extension-rules */
const getTsExtensionsForRules = (rules: Array<string>): Array<string> => {
const glob = new Bun.Glob("*.js")
const tsExtensionRules = [
...glob.scanSync(
path.resolve(
rootPath,
"node_modules/@typescript-eslint/eslint-plugin/dist/rules",
),
),
]
.map((s) => s.replace(".js", ""))
.toSorted() // Sort to avoid diffs

const rulesToExtend = tsExtensionRules.filter((tsExtensionRule) =>
rules.includes(tsExtensionRule),
)

return rulesToExtend.map(
(ruleToExtend) => `@typescript-eslint/${ruleToExtend}`,
)
}

const writeFile = (rules: Array<string>) => {
fs.writeFileSync(
filenames.index,
`/**
* File automatically created by scripts/index.ts.
*
* These are ESLint rules that have corresponding and recommended Biome rules.
*/
module.exports = {
extends: ["./${filenames.prettier}"],
rules: {
${rules.map((rule) => ` "${rule}": "off",`).join("\n")}
}
}
`,
)
}

const createPrettierFile = async () => {
const response = await fetch(
"https://raw.githubusercontent.com/prettier/eslint-config-prettier/main/index.js",
)
const attribution = `/**
* eslint-config-prettier © 2017-2023 Simon Lydell and contributors
* https://github.com/prettier/eslint-config-prettier
*
* This code is licensed under the MIT License (MIT).
*
* File automatically created by scripts/index.ts.
*/
`
const text = attribution + (await response.text())

fs.writeFileSync(filenames.prettier, text)
}
import { extraRulesToDisable, filenames } from "./consts.js"
import { getEquivalentRules, getRecommendedBiomeRules } from "./fetch.js"
import { createPrettierFile } from "./prettier.js"
import { getJsBaseRules, getTsExtensionsForRules } from "./tsExtensions.js"
import { sortRules, writeFile } from "./utils.js"

const main = async () => {
const rules = [...(await getEslintEquivalentRules()), ...extraRulesToDisable]
const rulesWithTsExtends = [...rules, ...getTsExtensionsForRules(rules)]
const recommendedBiomeRules = await getRecommendedBiomeRules()
const equivalentRules = await getEquivalentRules()

const eslintRulesToDisable = recommendedBiomeRules
.map(
(biomeRule) =>
equivalentRules.find(
(equivalentRule) => equivalentRule.biome === biomeRule,
)?.eslint,
)
.filter(Boolean) as Array<string>

const rules = [...eslintRulesToDisable, ...extraRulesToDisable]
const rulesWithTsExtends = [
...rules,
...getJsBaseRules(rules),
...getTsExtensionsForRules(rules),
]
const rulesNoDuplicates = [...new Set(rulesWithTsExtends)]

writeFile(rulesNoDuplicates)
writeFile(sortRules(rulesNoDuplicates))
await createPrettierFile()

console.log(`Generated ${filenames.index} & ${filenames.prettier}!`)
Expand Down
20 changes: 20 additions & 0 deletions scripts/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import fs from "fs"
import { filenames } from "./consts.js"

export const createPrettierFile = async () => {
const response = await fetch(
"https://raw.githubusercontent.com/prettier/eslint-config-prettier/main/index.js",
)
const attribution = `/**
* eslint-config-prettier © 2017-2023 Simon Lydell and contributors
* https://github.com/prettier/eslint-config-prettier
*
* This code is licensed under the MIT License (MIT).
*
* File automatically created by scripts/index.ts.
*/
`
const text = attribution + (await response.text())

fs.writeFileSync(filenames.prettier, text)
}
Loading

0 comments on commit 84d2108

Please sign in to comment.