Skip to content

Commit

Permalink
feat: support flat config and eslint v9 (#1073)
Browse files Browse the repository at this point in the history
* feat: support flat config

* refactor: tweak test context

* fix: update docs & tweak vitepress

* fix: remove

* fix: add name for eslint inspector debugging

* test: eslint v9 testing

* fix: support eslint v8.x and v9 compatibility

* 🙈 Add .eslintignore file to skip test fixtures

* 🚚 Move fixtures to a separate __fixtures__ folder

* 🚨 Fix prettier warnings

* 🐛 Prettier broke @ts-expect-error

---------

Co-authored-by: Vinícius Hoyer <[email protected]>
  • Loading branch information
kazupon and vhoyer authored Apr 18, 2024
1 parent d9a8cb7 commit 3edcbc0
Show file tree
Hide file tree
Showing 28 changed files with 373 additions and 104 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__fixtures__/
dist/
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
- name: Lint and test
run: |
yarn install --frozen-lockfile
yarn prepublishOnly
yarn build
yarn lint
yarn prettier --check '**/*'
yarn test
yarn test:integration
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/.husky/
/.idea/
/dist/
/node_modules/
node_modules
/docs/.vitepress/dist
/docs/.vitepress/cache
yarn-error.log
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.eslintcache
.eslintignore
.gitignore
.husky
.npmignore
.prettierignore
LICENSE
yarn.lock
dist
__fixtures__
1 change: 1 addition & 0 deletions __tests__/integrations/__fixtures__/flat-config/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
11 changes: 11 additions & 0 deletions __tests__/integrations/__fixtures__/flat-config/a.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div>
<img src="foo" />
</div>
</template>

<script>
import { ref } from "vue";
const counter = ref(0);
console.log("counter", counter);
</script>
10 changes: 10 additions & 0 deletions __tests__/integrations/__fixtures__/flat-config/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import plugin from "eslint-plugin-vuejs-accessibility";

export default [
...plugin.configs["flat/recommended"],
{
rules: {
"vuejs-accessibility/alt-text": "warn"
}
}
];
11 changes: 11 additions & 0 deletions __tests__/integrations/__fixtures__/flat-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"private": true,
"name": "integration-test-for-flat-config",
"type": "module",
"version": "1.0.0",
"description": "Integration test for flat config",
"dependencies": {
"eslint": "^9.0.0",
"eslint-plugin-vuejs-accessibility": "file:../../../.."
}
}
13 changes: 13 additions & 0 deletions __tests__/integrations/__fixtures__/legacy-config/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"root": true,
"extends": ["plugin:vuejs-accessibility/recommended"],
"plugins": ["vuejs-accessibility"],
"parser": "vue-eslint-parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2019
},
"rules": {
"vuejs-accessibility/alt-text": "warn"
}
}
1 change: 1 addition & 0 deletions __tests__/integrations/__fixtures__/legacy-config/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
11 changes: 11 additions & 0 deletions __tests__/integrations/__fixtures__/legacy-config/a.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div>
<img src="foo" />
</div>
</template>

<script>
import { ref } from "vue";
const counter = ref(0);
console.log("counter", counter);
</script>
11 changes: 11 additions & 0 deletions __tests__/integrations/__fixtures__/legacy-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"private": true,
"name": "integration-test-for-legacy-config",
"type": "module",
"version": "1.0.0",
"description": "Integration test for legacy config",
"dependencies": {
"eslint": "^8.57.0-0",
"eslint-plugin-vuejs-accessibility": "file:../../../.."
}
}
40 changes: 40 additions & 0 deletions __tests__/integrations/flat-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import cp from "child_process";
import path from "path";
import semver from "semver";
import { readPackageJson } from "./helper";

const ESLINT = `.${path.sep}node_modules${path.sep}.bin${path.sep}eslint`;

describe("Integration with flat config", () => {
let originalCwd: null | string = null;
const dirFixture = path.join(__dirname, "__fixtures__/flat-config");

beforeEach(() => {
originalCwd = process.cwd();
process.chdir(dirFixture);
cp.execSync("npm i -f", { stdio: "inherit" });
});
afterEach(() => {
originalCwd && process.chdir(originalCwd);
});

it("should work with config", () => {
expect.assertions(2);

const eslintPackageJson = readPackageJson(
path.resolve(dirFixture, "node_modules/eslint")
);

if (!semver.satisfies(process.version, eslintPackageJson.engines.node)) {
return;
}

const result = JSON.parse(
cp.execSync(`${ESLINT} a.vue --max-warnings 1 --format=json`, {
encoding: "utf-8"
})
);
expect(result.length).toBe(1);
expect(result[0].messages[0].messageId).toBe("imgMissingAlt");
});
});
8 changes: 8 additions & 0 deletions __tests__/integrations/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import fs from "fs";
import path from "path";

export function readPackageJson(base: string) {
return JSON.parse(
fs.readFileSync(path.resolve(base, "package.json"), "utf-8")
);
}
40 changes: 40 additions & 0 deletions __tests__/integrations/legacy-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import cp from "child_process";
import path from "path";
import semver from "semver";
import { readPackageJson } from "./helper";

const ESLINT = `.${path.sep}node_modules${path.sep}.bin${path.sep}eslint`;

describe("Integration with legacy config", () => {
let originalCwd: null | string = null;
const dirFixture = path.join(__dirname, "__fixtures__/legacy-config");

beforeEach(() => {
originalCwd = process.cwd();
process.chdir(dirFixture);
cp.execSync("npm i -f", { stdio: "inherit" });
});
afterEach(() => {
originalCwd && process.chdir(originalCwd);
});

it("should work with config", () => {
expect.assertions(2);

const eslintPackageJson = readPackageJson(
path.resolve(dirFixture, "node_modules/eslint")
);

if (!semver.satisfies(process.version, eslintPackageJson.engines.node)) {
return;
}

const result = JSON.parse(
cp.execSync(`${ESLINT} a.vue --max-warnings 1 --format=json`, {
encoding: "utf-8"
})
);
expect(result.length).toBe(1);
expect(result[0].messages[0].messageId).toBe("imgMissingAlt");
});
});
File renamed without changes.
28 changes: 27 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,33 @@ pnpm add -D eslint-plugin-vuejs-accessibility

## 📖 Usage

Add `vuejs-accessibility` to the plugins section of your `eslint` configuration. You can omit the `eslint-plugin-` prefix:
### Configuration (`eslint.config.js`)

Use `eslint.config.js` file to configure rules. This is the default in ESLint v9, but can be used starting from ESLint v8.57.0. See also: https://eslint.org/docs/latest/use/configure/configuration-files-new.

Example eslint.config.js:

```js
import pluginVueA11y from "eslint-plugin-vuejs-accessibility";

export default [
// add more generic rulesets here, such as:
// js.configs.recommended,
...pluginVueA11y.configs["flat/recommended"],
{
rules: {
// override/add rules settings here, such as:
// "vuejs-accessibility/alt-text": "error"
}
}
];
```

### Configuration (`.eslintrc`)

Use `.eslintrc.*` file to configure rules in ESLint < v9. See also: https://eslint.org/docs/latest/use/configure/.

Add `vuejs-accessibility` to the plugins section of your configuration. You can omit the `eslint-plugin-` prefix:

```json
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineLoader } from "vitepress";
import recommended from "../../src/configs/recommended";
import { rules } from "../.vitepress/rulesForSidebar";
import { rules as baseRules } from "../../src/configs/rules.js";
import { rules } from "../.vitepress/rulesForSidebar.js";

export type Data = Array<{
name: string;
Expand All @@ -23,8 +23,8 @@ export default defineLoader({
});

function getRecommendedRules() {
if (recommended.rules) {
return Object.keys(recommended.rules).map(removeRulePrefix);
if (baseRules) {
return Object.keys(baseRules).map(removeRulePrefix);
}
return [];
}
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
"version": "2.2.1",
"description": "An eslint plugin for checking Vue.js files for accessibility",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"lint": "eslint --cache .",
"build": "tsc -p tsconfig.build.json",
"prepublishOnly": "tsc -p tsconfig.build.json",
"test": "jest",
"test:integration": "jest --testTimeout 60000 --testRegex \".*\\.spec\\.ts$\"",
"release": "np",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs"
Expand Down Expand Up @@ -42,15 +47,18 @@
"@types/eslint-scope": "^3.7.2",
"@types/jest": "^29.2.5",
"@types/node": "^20.1.0",
"@types/semver": "^7.5.7",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^6.9.1",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.8.0",
"eslint-plugin-eslint-plugin": "^6.0.0",
"globals": "^14.0.0",
"husky": "^9.0.5",
"jest": "^29.2.2",
"np": "^10.0.0",
"prettier": "^3.0.0",
"pretty-quick": "^4.0.0",
"semver": "^7.6.0",
"ts-jest": "^29.0.3",
"ts-node": "^10.3.0",
"typescript": "^5.0.2",
Expand Down
34 changes: 34 additions & 0 deletions src/configs/flat/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import globals from "globals";
import { rules } from "../rules";

const recommended = [
{
name: "vuejs-accessibility:setup:base",
plugins: {
get "vuejs-accessibility"() {
return require("../../index");
}
},
languageOptions: {
sourceType: "module",
globals: globals.browser
}
},
{
name: "vuejs-accessibility:setup:with-files-rules-and-parser",
files: ["*.vue", "**/*.vue"],
plugins: {
get "vuejs-accessibility"() {
return require("../../index");
}
},
languageOptions: {
parser: require("vue-eslint-parser"),
sourceType: "module",
globals: globals.browser
},
rules
}
];

export = recommended;
24 changes: 2 additions & 22 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Linter } from "eslint";
import { rules } from "./rules";

const recommended: Linter.BaseConfig = {
parser: require.resolve("vue-eslint-parser"),
Expand All @@ -11,28 +12,7 @@ const recommended: Linter.BaseConfig = {
es6: true
},
plugins: ["vuejs-accessibility"],
rules: {
"vuejs-accessibility/alt-text": "error",
"vuejs-accessibility/anchor-has-content": "error",
"vuejs-accessibility/aria-props": "error",
"vuejs-accessibility/aria-role": "error",
"vuejs-accessibility/aria-unsupported-elements": "error",
"vuejs-accessibility/click-events-have-key-events": "error",
"vuejs-accessibility/form-control-has-label": "error",
"vuejs-accessibility/heading-has-content": "error",
"vuejs-accessibility/iframe-has-title": "error",
"vuejs-accessibility/interactive-supports-focus": "error",
"vuejs-accessibility/label-has-for": "error",
"vuejs-accessibility/media-has-caption": "error",
"vuejs-accessibility/mouse-events-have-key-events": "error",
"vuejs-accessibility/no-access-key": "error",
"vuejs-accessibility/no-autofocus": "error",
"vuejs-accessibility/no-distracting-elements": "error",
"vuejs-accessibility/no-redundant-roles": "error",
"vuejs-accessibility/no-static-element-interactions": "error",
"vuejs-accessibility/role-has-required-aria-props": "error",
"vuejs-accessibility/tabindex-no-positive": "error"
}
rules
};

export default recommended;
26 changes: 26 additions & 0 deletions src/configs/rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Linter } from "eslint";

const rules = {
"vuejs-accessibility/alt-text": "error",
"vuejs-accessibility/anchor-has-content": "error",
"vuejs-accessibility/aria-props": "error",
"vuejs-accessibility/aria-role": "error",
"vuejs-accessibility/aria-unsupported-elements": "error",
"vuejs-accessibility/click-events-have-key-events": "error",
"vuejs-accessibility/form-control-has-label": "error",
"vuejs-accessibility/heading-has-content": "error",
"vuejs-accessibility/iframe-has-title": "error",
"vuejs-accessibility/interactive-supports-focus": "error",
"vuejs-accessibility/label-has-for": "error",
"vuejs-accessibility/media-has-caption": "error",
"vuejs-accessibility/mouse-events-have-key-events": "error",
"vuejs-accessibility/no-access-key": "error",
"vuejs-accessibility/no-autofocus": "error",
"vuejs-accessibility/no-distracting-elements": "error",
"vuejs-accessibility/no-redundant-roles": "error",
"vuejs-accessibility/no-static-element-interactions": "error",
"vuejs-accessibility/role-has-required-aria-props": "error",
"vuejs-accessibility/tabindex-no-positive": "error"
} satisfies Linter.RulesRecord;

export { rules };
Loading

0 comments on commit 3edcbc0

Please sign in to comment.