Unify linting: replace Stylelint and markdownlint with ESLint (#4148)

We were running three separate lint tools (ESLint, Stylelint,
markdownlint-cli2) and a separate Prettier step. This PR consolidates
everything: `@eslint/css` and `@eslint/markdown` bring CSS and Markdown
linting into ESLint, so one tool now covers JS, CSS, and Markdown. The
Prettier step got merged into the lint scripts too, so there's now just
`test:lint` to check things and `lint:fix` to fix them.

Outcome: fewer tools to maintain, simpler/fewer scripts, fewer config
files.

As a side effect, the dependabot alerts
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/140
and
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/141
are resolved, as we no longer use the vulnerable `fast-uri` package
(which was a dependency of `stylelint`).
This commit is contained in:
Kristjan ESPERANTO
2026-05-09 12:01:18 +02:00
committed by GitHub
parent 67db41cfcb
commit 2850e14172
10 changed files with 743 additions and 1706 deletions
+5 -19
View File
@@ -8,25 +8,10 @@ We hold our code to standard, and these standards are documented below.
We use [prettier](https://prettier.io/) for automatic formatting a lot all our files. The configuration is in our `prettier.config.mjs` file.
To run prettier, use `node --run lint:prettier`.
And we use [ESLint](https://eslint.org) to lint our JavaScript, Markdown, and CSS files. The configuration is in our `eslint.config.mjs` file.
### JavaScript: Run ESLint
We use [ESLint](https://eslint.org) to lint our JavaScript files. The configuration is in our `eslint.config.mjs` file.
To run ESLint, use `node --run lint:js`.
### CSS: Run StyleLint
We use [StyleLint](https://stylelint.io) to lint our CSS. The configuration is in our `stylelint.config.mjs` file.
To run StyleLint, use `node --run lint:css`.
### Markdown: Run markdownlint
We use [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) to lint our markdown files. The configuration is in our `.markdownlint.json` file.
To run markdownlint, use `node --run lint:markdown`.
To check for formatting and linting errors, use `node --run test:lint`
To fix formatting and linting errors, use `node --run lint:fix`.
## Testing
@@ -43,6 +28,7 @@ The `package.json` scripts expose finer-grained test commands:
- `test:watch` keep Vitest in watch mode for fast local feedback
- `test:ui` open the Vitest UI dashboard (needs OS file-watch support enabled)
- `test:calendar` run the legacy calendar debug helper
- `test:css`, `test:markdown`, `test:prettier`, `test:spelling`, `test:js` lint-only scripts that enforce formatting, spelling, markdown style, and ESLint.
- `test:lint` run linter and formatter checks
- `test:spelling` run the spell checker
You can invoke any script with `node --run <script>` (or `npm run <script>`). Individual files can still be targeted directly, e.g. `npx vitest run tests/e2e/env_spec.js`.
+1 -1
View File
@@ -10,7 +10,7 @@ Hello and thank you for wanting to contribute to the MagicMirror² project!
> - What does the pull request accomplish? Use a list if needed.
> - If it includes major visual changes please add screenshots.
>
> 3. Please run `node --run lint:prettier` before submitting so that
> 3. Please run `node --run lint:fix` before submitting so that
> style issues are fixed.
**Note**: Sometimes the development moves very fast. It is highly
+1 -4
View File
@@ -33,10 +33,7 @@ jobs:
node --run install-mm:dev
- name: "Run linter tests"
run: |
node --run test:prettier
node --run test:js
node --run test:css
node --run test:markdown
node --run test:lint
test:
runs-on: ubuntu-24.04
timeout-minutes: 30
-6
View File
@@ -1,6 +0,0 @@
{
"line_length": false,
"no-duplicate-heading": false,
"no-inline-html": false,
"no-trailing-punctuation": false
}
-1
View File
@@ -290,7 +290,6 @@
"sthuber",
"Stieber",
"strinner",
"stylelintrc",
"sunaction",
"suncalc",
"suntimes",
+21
View File
@@ -1,8 +1,10 @@
import {defineConfig, globalIgnores} from "eslint/config";
import css from "@eslint/css";
import globals from "globals";
import {flatConfigs as importX} from "eslint-plugin-import-x";
import js from "@eslint/js";
import jsdocPlugin from "eslint-plugin-jsdoc";
import markdown from "@eslint/markdown";
import {configs as packageJsonConfigs} from "eslint-plugin-package-json";
import playwright from "eslint-plugin-playwright";
import stylistic from "@stylistic/eslint-plugin";
@@ -10,6 +12,25 @@ import vitest from "@vitest/eslint-plugin";
export default defineConfig([
globalIgnores(["config/**", "modules/**/*", "js/positions.js", "tests/configs/config_variables.js"]),
{
files: ["**/*.css"],
language: "css/css",
plugins: {css},
extends: ["css/recommended"],
rules: {
"css/no-invalid-properties": ["error", {allowUnknownVariables: true}],
"css/use-baseline": "off"
}
},
{
files: ["**/*.md"],
plugins: {markdown},
language: "markdown/gfm",
extends: ["markdown/recommended"],
rules: {
"markdown/no-missing-label-refs": "off"
}
},
{
files: ["**/*.js"],
languageOptions: {
+707 -1647
View File
File diff suppressed because it is too large Load Diff
+8 -15
View File
@@ -51,10 +51,7 @@
"config:check": "node js/check_config.js",
"install-mm": "npm install --no-audit --no-fund --no-update-notifier --only=prod --omit=dev",
"install-mm:dev": "npm install --no-audit --no-fund --no-update-notifier && npx playwright install chromium",
"lint:css": "stylelint 'css/**/*.css' 'defaultmodules/**/*.css' --fix",
"lint:js": "eslint --fix",
"lint:markdown": "markdownlint-cli2 . --fix",
"lint:prettier": "prettier . --write",
"lint:fix": "eslint --fix && prettier . --write",
"prepare": "[ -f node_modules/.bin/husky ] && husky || echo no husky installed.",
"server": "node ./serveronly",
"server:watch": "node ./serveronly/watcher.js",
@@ -69,21 +66,19 @@
"test": "vitest run",
"test:calendar": "node ./defaultmodules/calendar/debug.js",
"test:coverage": "vitest run --coverage",
"test:css": "stylelint 'css/**/*.css' 'defaultmodules/**/*.css'",
"test:e2e": "vitest run tests/e2e",
"test:electron": "vitest run tests/electron",
"test:js": "eslint",
"test:markdown": "markdownlint-cli2 .",
"test:prettier": "prettier . --check",
"test:lint": "eslint && prettier . --check",
"test:spelling": "cspell . --gitignore",
"test:ui": "vitest --ui",
"test:unit": "vitest run tests/unit",
"test:watch": "vitest"
},
"lint-staged": {
"*": "prettier --ignore-unknown --write",
"*.js": "eslint --fix",
"*.css": "stylelint --fix"
"*": [
"eslint --fix",
"prettier --write --ignore-unknown"
]
},
"dependencies": {
"@fontsource/roboto": "^5.2.10",
@@ -110,7 +105,9 @@
"weathericons": "^2.1.0"
},
"devDependencies": {
"@eslint/css": "^1.2.0",
"@eslint/js": "^10.0.1",
"@eslint/markdown": "^8.0.1",
"@stylistic/eslint-plugin": "^5.10.0",
"@vitest/coverage-v8": "^4.1.5",
"@vitest/eslint-plugin": "^1.6.16",
@@ -124,14 +121,10 @@
"husky": "^9.1.7",
"jsdom": "^29.1.1",
"lint-staged": "^17.0.2",
"markdownlint-cli2": "^0.22.1",
"msw": "^2.14.3",
"playwright": "^1.59.1",
"prettier": "^3.8.3",
"prettier-plugin-jinja-template": "^2.2.0",
"stylelint": "^17.11.0",
"stylelint-config-standard": "^40.0.0",
"stylelint-prettier": "^5.0.3",
"vitest": "^4.1.5"
},
"optionalDependencies": {
-6
View File
@@ -1,12 +1,6 @@
const config = {
plugins: ["prettier-plugin-jinja-template"],
overrides: [
{
files: "*.md",
options: {
parser: "markdown"
}
},
{
files: ["*.njk"],
options: {
-7
View File
@@ -1,7 +0,0 @@
const config = {
extends: ["stylelint-config-standard", "stylelint-prettier/recommended"],
root: true,
rules: {}
};
export default config;