├── .cspell.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github
├── CODEOWNERS
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── BUG.md
│ ├── DOCS.md
│ ├── FEATURE.md
│ ├── MODIFICATION.md
│ └── SUPPORT.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── dependency-review.yml
│ └── nodejs.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── commitlint.config.js
├── husky.config.js
├── jest.config.js
├── lint-staged.config.js
├── package-lock.json
├── package.json
├── src
├── ESLintError.js
├── getESLint.js
├── index.js
├── linter.js
├── options.js
├── options.json
├── utils.js
└── worker.js
├── test
├── .badeslintrc
├── autofix-stop.test.js
├── autofix.test.js
├── cached.test.js
├── child-compiler.test.js
├── circular-plugin.test.js
├── context.test.js
├── emit-error.test.js
├── emit-warning.test.js
├── empty.test.js
├── error.test.js
├── eslint-lint.test.js
├── eslint-options.test.js
├── eslint-path.test.js
├── eslintignore.test.js
├── exclude.test.js
├── fail-on-config.test.js
├── fail-on-error.test.js
├── fail-on-warning.test.js
├── fixtures
│ ├── [symbols]
│ │ ├── error.js
│ │ └── symbols-entry.js
│ ├── cache.js
│ ├── child-entry.js
│ ├── error-entry.js
│ ├── error-warn-entry.js
│ ├── error.js
│ ├── exclude-entry.js
│ ├── exclude-folder-entry.js
│ ├── fixable-clone-entry.js
│ ├── fixable.js
│ ├── flat-config.js
│ ├── folder
│ │ ├── error.js
│ │ └── readme.md
│ ├── full-of-problems-entry.js
│ ├── full-of-problems.js
│ ├── good-entry.js
│ ├── good.js
│ ├── ignore-entry.js
│ ├── ignore.js
│ ├── index.js
│ ├── lint-more-entry.js
│ ├── lint-more.js
│ ├── lint-one-entry.js
│ ├── lint-two-entry.js
│ ├── lint.js
│ ├── media
│ │ └── some-video.ts
│ ├── multiple-entry.js
│ ├── nonfixable-clone-entry.js
│ ├── nonfixable.js
│ ├── query-entry.js
│ ├── require.js
│ ├── resource-query-entry.js
│ ├── warn-entry.js
│ ├── warn-error-entry.js
│ └── warn.js
├── flat-config.test.js
├── formatter-custom.test.js
├── formatter-eslint.test.js
├── formatter-official.test.js
├── formatter-write.test.js
├── lint-dirty-modules-only.test.js
├── mock
│ ├── eslint
│ │ ├── index.js
│ │ └── package.json
│ └── formatter
│ │ └── index.js
├── multiple-instances.test.js
├── ok.test.js
├── parameters.test.js
├── query.test.js
├── quiet.test.js
├── resource-query.test.js
├── symbols.test.js
├── threads.test.js
├── utils.test.js
├── utils
│ ├── conf.js
│ └── pack.js
├── warning.test.js
└── watch.test.js
├── tsconfig.json
└── types
├── ESLintError.d.ts
├── getESLint.d.ts
├── index.d.ts
├── linter.d.ts
├── options.d.ts
├── utils.d.ts
└── worker.d.ts
/.cspell.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2",
3 | "language": "en,en-gb",
4 | "words": [
5 | "arrify",
6 | "notjs",
7 | "nonfixable",
8 | "dasdas",
9 | "badeslintrc",
10 | "autofix",
11 | "autofixing",
12 | "checkstyle",
13 | "commitlint",
14 | "indentifiers"
15 | ],
16 |
17 | "ignorePaths": [
18 | "CHANGELOG.md",
19 | "package.json",
20 | "coverage",
21 | "/test/output",
22 | "dist/**",
23 | "**/__snapshots__/**",
24 | "package-lock.json",
25 | "node_modules",
26 | "coverage",
27 | "*.log"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
5 | /test/output
6 | /types/**/*
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | '@webpack-contrib/eslint-config-webpack/rules/possible-errors',
5 | '@webpack-contrib/eslint-config-webpack/rules/best-practices',
6 | '@webpack-contrib/eslint-config-webpack/rules/variables',
7 | '@webpack-contrib/eslint-config-webpack/rules/node',
8 | '@webpack-contrib/eslint-config-webpack/rules/stylistic-issues',
9 | '@webpack-contrib/eslint-config-webpack/rules/es2015',
10 | 'prettier',
11 | ],
12 | parserOptions: {
13 | ecmaVersion: 2018,
14 | env: {
15 | es6: true,
16 | jest: true,
17 | },
18 | sourceType: 'module',
19 | },
20 | rules: {
21 | strict: 'error',
22 | 'global-require': 'off',
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | bin/* eol=lf
3 | package-lock.json -diff
4 | yarn.lock -diff
5 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These are the default owners for everything in
2 | # webpack-contrib
3 | @webpack-contrib/org-maintainers
4 |
5 | # Add repository specific users / groups
6 | # below here for libs that are not maintained by the org.
7 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing in @webpack-contrib
2 |
3 | We'd always love contributions to further improve the webpack / webpack-contrib ecosystem!
4 | Here are the guidelines we'd like you to follow:
5 |
6 | - [Questions and Problems](#question)
7 | - [Issues and Bugs](#issue)
8 | - [Feature Requests](#feature)
9 | - [Pull Request Submission Guidelines](#submit-pr)
10 | - [Commit Message Conventions](#commit)
11 |
12 | ## Got a Question or Problem?
13 |
14 | Please submit support requests and questions to StackOverflow using the tag [[webpack]](http://stackoverflow.com/tags/webpack).
15 | StackOverflow is better suited for this kind of support though you may also inquire in [Webpack Gitter](https://gitter.im/webpack/webpack).
16 | The issue tracker is for bug reports and feature discussions.
17 |
18 | ## Found an Issue or Bug?
19 |
20 | Before you submit an issue, please search the issue tracker, an issue for your problem may already exist, and the discussion might inform you of workarounds readily available.
21 |
22 | We want to fix all the issues as soon as possible, but before fixing a bug, we need to reproduce and confirm it. In order to reproduce bugs, we ask that you provide a minimal reproduction scenario (GitHub repo or failing test case). Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
23 |
24 | - version of Webpack used
25 | - version of the loader / plugin you are creating a bug report for
26 | - the use-case that fails
27 |
28 | A minimal reproduce scenario allows us to quickly confirm a bug (or point out config problems) as well as confirm that we are fixing the right problem.
29 |
30 | We will be insisting on a minimal reproduction scenario in order to save the maintainers' time and ultimately be able to fix more bugs. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase, but we really need to isolate the problem before we can fix it.
31 |
32 | Unfortunately, we are unable to investigate or fix bugs without a minimal reproduction, so if we don't hear back from you, we may have to close an issue that doesn't have enough info to be reproduced.
33 |
34 | ## Feature Requests?
35 |
36 | You can _request_ a new feature by creating an issue on GitHub.
37 |
38 | If you would like to _implement_ a new feature yourself, please **first submit an issue** with a proposal to ensure the idea aligns with the goals of the project.
39 |
40 | ## Pull Request Submission Guidelines
41 |
42 | Before you submit your Pull Request (PR) consider the following guidelines:
43 |
44 | - Search GitHub for an open or closed PR related to your submission to avoid duplicating effort.
45 | - Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit). This is important because release notes are automatically generated from these messages.
46 | - Complete the `Pull Request Template`. Pull requests that ignore the template will not be reviewed.
47 | - Please sign the `Contributor License Agreement (CLA)` when you open your pull request. We cannot accept your contribution without it. Be sure to sign using the primary email address associated with your local and GitHub account.
48 |
49 | ## Webpack Contrib Commit Conventions
50 |
51 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
52 | format that includes a **type**, a **scope** and a **subject**:
53 |
54 | ```
55 | ():
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | The **header** is mandatory and the **scope** of the header is optional.
63 |
64 | No line in the commit message should exceed 100 characters! This makes the message easier to read on GitHub as well as in various Git tools.
65 |
66 | The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
67 |
68 | Examples:
69 |
70 | ```
71 | docs(readme): update install instructions
72 | ```
73 |
74 | ```
75 | fix: refer to the `entrypoint` instead of the first `module`
76 | ```
77 |
78 | ### Revert
79 |
80 | If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit.
81 | In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted.
82 |
83 | ### Type
84 |
85 | Must be one of the following commit types:
86 |
87 | - **build**: Changes that affect the build system or external dependencies (example scopes: babel, npm)
88 | - **chore**: Changes that fall outside of build / docs that do not effect source code (example scopes: package, defaults)
89 | - **ci**: Changes to our CI configuration files and scripts (example scopes: circleci, travis)
90 | - **docs**: Documentation only changes (example scopes: readme, changelog)
91 | - **feat**: A new feature
92 | - **fix**: A bug fix
93 | - **perf**: A code change that improves performance
94 | - **refactor**: A code change that neither fixes a bug nor adds a feature
95 | - **revert**: Used when reverting a committed change
96 | - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons)
97 | - **test**: Addition of or updates to Jest tests
98 |
99 | ### Scope
100 |
101 | The scope is subjective & depends on the `type` see above. A good example of a scope would be a change to a particular class or module.
102 |
103 | ### Subject
104 |
105 | The subject contains a succinct description of the change:
106 |
107 | - use the imperative, present tense: "change" not "changed" or "changes"
108 | - don't capitalize the first letter
109 | - no dot (.) at the end
110 |
111 | ### Body
112 |
113 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" or "changes".
114 | The body should include the motivation for the change and contrast it with previous behavior.
115 |
116 | ### Footer
117 |
118 | The footer should include any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
119 |
120 | **Breaking Changes** must start with the word `BREAKING CHANGE:` followed by a space or two new lines. The rest of the breaking change details should be provided after this.
121 |
122 | Example
123 |
124 | ```
125 | BREAKING CHANGE: Updates to `Chunk.mapModules`.
126 |
127 | This release is not backwards compatible with `Webpack 2.x` due to breaking changes in webpack/webpack#4764
128 | Migration: see webpack/webpack#5225
129 |
130 | ```
131 |
132 | ## Testing Your Pull Request
133 |
134 | You may need to test your changes in a real-world project or a dependent module. Thankfully, GitHub provides a means to do this. To add a dependency to the `package.json` of such a project, use the following syntax:
135 |
136 | ```json
137 | {
138 | "devDependencies": {
139 | "eslint-webpack-plugin": "webpack-contrib/eslint-webpack-plugin#{id}/head"
140 | }
141 | }
142 | ```
143 |
144 | Where `{id}` is the # ID of your Pull Request.
145 |
146 | ## Contributor License Agreement
147 |
148 | When submitting your contribution, a CLA (Contributor License Agreement) bot will verify whether you have signed the [CLA](https://easycla.lfx.linuxfoundation.org/#/?version=2).
149 | If it is your first time, it will link you to the right place to sign it.
150 | However, if the email used in your commits doesn’t match the email associated with your GitHub account, the CLA bot won’t accept your contribution.
151 |
152 | Run `git config user.email` to see your Git email, and verify it with [your GitHub email](https://github.com/settings/emails).
153 |
154 | ## Thanks
155 |
156 | For your interest, time, understanding, and for following this simple guide.
157 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | open_collective: webpack
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BUG.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐛 Bug Report
3 | about: Something went awry and you'd like to tell us about it.
4 | ---
5 |
6 |
7 |
8 |
9 | ### Bug report
10 |
11 |
12 |
13 |
14 |
15 |
16 | ### Actual Behavior
17 |
18 |
19 |
20 | ### Expected Behavior
21 |
22 |
23 |
24 |
25 | ### How Do We Reproduce?
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | ### Please paste the results of `npx webpack-cli info` here, and mention other relevant information
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/DOCS.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 📚 Documentation
3 | about: Are the docs lacking or missing something? Do they need some new 🔥 hotness? Tell us here.
4 | ---
5 |
6 |
7 |
8 |
9 | Documentation Is:
10 |
11 |
12 |
13 | - [ ] Missing
14 | - [ ] Needed
15 | - [ ] Confusing
16 | - [ ] Not Sure?
17 |
18 | ### Please Explain in Detail...
19 |
20 |
21 |
22 |
23 |
24 |
25 | ### Your Proposal for Changes
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FEATURE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ✨ Feature Request
3 | about: Suggest an idea for this project
4 | ---
5 |
6 |
7 |
8 |
9 | ### Feature Proposal
10 |
11 |
12 |
13 |
14 |
15 |
16 | ### Feature Use Case
17 |
18 | ### Please paste the results of `npx webpack-cli info` here, and mention other relevant information
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/MODIFICATION.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🔧 Modification Request
3 | about: Would you like something work differently? Have an alternative approach? This is the template for you.
4 | ---
5 |
6 |
7 |
8 |
9 | ### Modification Proposal
10 |
11 |
12 |
13 |
14 |
15 |
16 | ### Expected Behavior / Situation
17 |
18 | ### Actual Behavior / Situation
19 |
20 | ### Please paste the results of `npx webpack-cli info` here, and mention other relevant information
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/SUPPORT.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🆘 Support, Help, and Advice
3 | about: 👉🏽 Need support, help, or advice? Don't open an issue! Head to https://github.com/webpack/webpack/discussions or StackOverflow.
4 | ---
5 |
6 | Hey there! If you need support, help, or advice then this is not the place to ask.
7 | Please visit [GitHub Discussions](https://github.com/webpack/webpack/discussions) or [StackOverflow](https://stackoverflow.com/questions/tagged/webpack) instead.
8 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
10 |
11 | This PR contains a:
12 |
13 | - [ ] **bugfix**
14 | - [ ] new **feature**
15 | - [ ] **code refactor**
16 | - [ ] **test update**
17 | - [ ] **typo fix**
18 | - [ ] **metadata update**
19 |
20 | ### Motivation / Use-Case
21 |
22 |
27 |
28 | ### Breaking Changes
29 |
30 |
34 |
35 | ### Additional Info
36 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | name: 'Dependency Review'
2 | on: [pull_request]
3 |
4 | permissions:
5 | contents: read
6 |
7 | jobs:
8 | dependency-review:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: 'Checkout Repository'
12 | uses: actions/checkout@v4
13 | - name: 'Dependency Review'
14 | uses: actions/dependency-review-action@v4
15 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: eslint-webpack-plugin
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - next
8 | pull_request:
9 | branches:
10 | - master
11 | - next
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | lint:
18 | name: Lint - ${{ matrix.os }} - Node v${{ matrix.node-version }}
19 |
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 |
23 | strategy:
24 | matrix:
25 | os: [ubuntu-latest]
26 | node-version: [lts/*]
27 |
28 | runs-on: ${{ matrix.os }}
29 |
30 | concurrency:
31 | group: lint-${{ matrix.os }}-v${{ matrix.node-version }}-${{ github.ref }}
32 | cancel-in-progress: true
33 |
34 | steps:
35 | - uses: actions/checkout@v4
36 | with:
37 | fetch-depth: 0
38 |
39 | - name: Use Node.js ${{ matrix.node-version }}
40 | uses: actions/setup-node@v4
41 | with:
42 | node-version: ${{ matrix.node-version }}
43 |
44 | - name: Install dependencies
45 | run: npm ci
46 |
47 | - name: Lint
48 | run: npm run lint
49 |
50 | - name: Security audit
51 | run: npm run security
52 |
53 | - name: Validate PR commits with commitlint
54 | if: github.event_name == 'pull_request'
55 | run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose
56 |
57 | test:
58 | name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version }}, Webpack ${{ matrix.webpack-version }}, Eslint ${{ matrix.eslint-version }}
59 |
60 | strategy:
61 | matrix:
62 | os: [ubuntu-latest, windows-latest, macos-latest]
63 | node-version: [18.x, 20.x, 22.x, 24.x]
64 | eslint-version: [8.x, 9.x]
65 | webpack-version: [latest]
66 |
67 | runs-on: ${{ matrix.os }}
68 |
69 | concurrency:
70 | group: test-${{ matrix.os }}-v${{ matrix.node-version }}-${{ matrix.eslint-version }}-${{ matrix.webpack-version }}-${{ github.ref }}
71 | cancel-in-progress: true
72 |
73 | steps:
74 | - uses: actions/checkout@v4
75 |
76 | - name: Use Node.js ${{ matrix.node-version }}
77 | uses: actions/setup-node@v4
78 | with:
79 | node-version: ${{ matrix.node-version }}
80 | cache: 'npm'
81 |
82 | - name: Install dependencies
83 | run: npm ci
84 |
85 | - name: Install webpack ${{ matrix.webpack-version }}
86 | run: npm i webpack@${{ matrix.webpack-version }}
87 |
88 | - name: Install eslint ${{ matrix.eslint-version }}
89 | run: npm i eslint@${{ matrix.eslint-version }}
90 |
91 | - name: Run tests for webpack version ${{ matrix.webpack-version }}
92 | run: npm run test:coverage -- --ci
93 |
94 | - name: Submit coverage data to codecov
95 | uses: codecov/codecov-action@v5
96 | with:
97 | token: ${{ secrets.CODECOV_TOKEN }}
98 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | yarn-debug.log*
5 | .eslintcache
6 |
7 | /coverage
8 | /dist
9 | /local
10 | /reports
11 | /node_modules
12 | /test/output
13 |
14 | .DS_Store
15 | Thumbs.db
16 | .idea
17 | .vscode
18 | *.sublime-project
19 | *.sublime-workspace
20 | *.iml
21 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
5 | /test/output
6 | CHANGELOG.md
7 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = { singleQuote: true };
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [5.0.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v5.0.0...v5.0.1) (2025-04-15)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * fail on error and fail on warning ([#275](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/275)) ([4ef97cd](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/4ef97cd46b5ced43d5a68444bce2d392ddd28166))
11 |
12 | ## [5.0.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v4.2.0...v5.0.0) (2025-02-26)
13 |
14 |
15 | ### ⚠ BREAKING CHANGES
16 |
17 | * Use flat config by default
18 | * Minimum supported Node.js version is 18.12.0
19 |
20 | ## [4.2.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v4.1.0...v4.2.0) (2024-05-24)
21 |
22 |
23 | ### Features
24 |
25 | * support eslint 9 ([#250](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/250)) ([d25e94c](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/d25e94c90b477d465bf4d7fb6450a9b4ed6936f4))
26 |
27 |
28 | ### Bug Fixes
29 |
30 | * use `stillValidModule` only `lintDirtyModulesOnly` is disabled ([#253](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/253)) ([b52d1e7](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/b52d1e743e77e657256919ca7b0cdb1d432a1fb9))
31 |
32 | ## [4.1.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v4.0.0...v4.1.0) (2024-03-14)
33 |
34 |
35 | ### Features
36 |
37 | * support flat config ([#238](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/238)) ([19cadbe](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/19cadbe3117fa7899eba2424d7bb07bc403160c5))
38 |
39 | ### [4.0.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v4.0.0...v4.0.1) (2023-04-10)
40 |
41 |
42 | ### Performance
43 |
44 | * enable `cache` by default ([#213](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/213))
45 |
46 | ## [4.0.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.2.0...v4.0.0) (2023-02-03)
47 |
48 |
49 | ### ⚠ BREAKING CHANGES
50 |
51 | * drop node v12 and eslint v7 (#159)
52 |
53 | ### Bug Fixes
54 |
55 | * lint modules that are cached with webpack's filesystem cache ([#197](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/197)) ([92f25ec](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/92f25ecf5f92d72bab4c80dac363a1c632e3500f)), closes [#130](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/130)
56 |
57 |
58 | * drop node v12 and eslint v7 ([#159](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/159)) ([1edd3c1](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/1edd3c1ab63ba864768d812401ce500318548e2d))
59 |
60 | ## [3.2.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.1.1...v3.2.0) (2022-06-23)
61 |
62 |
63 | ### Features
64 |
65 | * add eslint prefix to CLI output for better debugging ([#155](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/155)) ([67efb34](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/67efb34fc2ad11dcd6593fce21059aea271dcfc7))
66 | * removed cjs wrapper ([#152](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/152)) ([125becf](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/125becfaad348042b732d074b346bb3243dd7fa4))
67 | * support resourceQueryExclude option ([#165](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/165)) ([464120f](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/464120fa74f0f0ccca98322ef43a4c057528a964))
68 |
69 | ### [3.1.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.1.0...v3.1.1) (2021-11-11)
70 |
71 |
72 | ### Bug Fixes
73 |
74 | * child compiler lint ([#127](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/127)) ([18d5f23](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/18d5f23ecefd1a242f62722e8487ef2bcc39bc8c))
75 |
76 | ## [3.1.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.0.1...v3.1.0) (2021-10-27)
77 |
78 |
79 | ### Features
80 |
81 | * support eslint 8 ([#121](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/121)) ([caeb2dd](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/caeb2dd198ea4261091650e62cb30d2a54530f1d))
82 |
83 | ### [3.0.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.0.0...v3.0.1) (2021-07-20)
84 |
85 |
86 | ### Bug Fixes
87 |
88 | * crash with `ERR_REQUIRE_ESM` error ([26b4db3](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/26b4db37f1b39d597c26fbab52374425e2c455ac))
89 |
90 | ## [3.0.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.5.4...v3.0.0) (2021-07-19)
91 |
92 |
93 | ### ⚠ BREAKING CHANGES
94 |
95 | * webpack v4 and nodejs v10 dropped (#105)
96 |
97 | * webpack v4 and nodejs v10 dropped ([#105](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/105)) ([594d84c](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/594d84cc800057f889b23a27e9bc31e2eb5e32f1))
98 |
99 | ### [2.5.4](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.5.3...v2.5.4) (2021-04-19)
100 |
101 |
102 | ### Bug Fixes
103 |
104 | * context with symbols ([#94](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/94)) ([6fc6874](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/6fc6874f4ee295eea372dcfa0a86799b355dab70))
105 | * resolve paths and normalize ([#97](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/97)) ([818b825](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/818b825db119dde0b53b24d96688f1af89344b29))
106 | * use `finishModules` if thread is less than or equal 1 ([#95](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/95)) ([c12e7be](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/c12e7be0be49f95fa8f2d9ae354acba3bd412b5c))
107 |
108 | ### [2.5.3](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.5.2...v2.5.3) (2021-03-24)
109 |
110 |
111 | ### Bug Fixes
112 |
113 | * allow multiple instances ([#92](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/92)) ([0cdd621](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/0cdd621be597c643cad6c4a41c7fed31c29fb1a5))
114 | * match hidden directories for path exclusions ([#87](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/87)) ([bb8750c](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/bb8750cb8a1cb4f6297a07b579ad4e394e11d968))
115 |
116 | ### [2.5.2](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.5.1...v2.5.2) (2021-02-18)
117 |
118 |
119 | ### Bug Fixes
120 |
121 | * **fail-on-error:** show eslint errors when failOnError is disabled ([#85](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/85)) ([69be533](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/69be5338a8f72ffdbee055ab926cf4d84047fd35))
122 |
123 | ### [2.5.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.5.0...v2.5.1) (2021-02-12)
124 |
125 |
126 | ### Bug Fixes
127 |
128 | * exclude node_modules root ([#80](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/80)) ([be0391e](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/be0391e28322e220cf628f842f35b3d800c284c0))
129 | * prevent lint all files ([#77](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/77)) ([f57cb8e](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/f57cb8e1f01c522e562f0af3460d900d7fbba94f))
130 | * update types ([#81](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/81)) ([90608da](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/90608da93f13ae2de70c2696d4284c1558a3f301))
131 |
132 | ## [2.5.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.4.3...v2.5.0) (2021-02-04)
133 |
134 |
135 | ### Refactor
136 |
137 | * Updates to emitError and emitWarning
138 |
139 | Setting only emitError to true will no longer exclusively print files with errors
140 | and disregard the files with warnings. Similarly, setting only emitWarning to true
141 | will no longer exclusively print files with warnings disregard the files with errors.
142 |
143 | * fix: use quiet to override emitError and emitWarning
144 |
145 | - quiet is essentially syntactic sugar for setting emitError to true
146 | and emitWarning to false
147 |
148 | ### Bug Fixes
149 |
150 | * fails when `failOnError` or `failOnWarning` enabled ([#72](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/72)) ([8a72a8a](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/8a72a8ad26b8decb800f955d8f4d362f280c4d0f))
151 | * lint dirty modules only ([#67](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/67)) ([f7f372e](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/f7f372e800e75fcd2928655648fee01266c6d158))
152 | * threads multi-compiler ([#69](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/69)) ([cef4f74](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/cef4f7473707fb3f069ec44c54b5ed2d27d931f8))
153 | * types ([#66](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/66)) ([4daddf5](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/4daddf5335b2c78203482d7e7f6d82a909277212))
154 | * Fix emit warning error quiet (#46) ([d38165b](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/d38165bef1e2d73a9d53f42d80b926c9eab12707)), closes [#46](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/46) [#19](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/19) [#19](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/19)
155 |
156 | ### [2.4.3](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.4.2...v2.4.3) (2021-01-19)
157 |
158 |
159 | ### Bug Fixes
160 |
161 | * crash on `split` ([#62](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/62)) ([db38f61](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/db38f611965cfdec83984364e0b982bbd7a678e0))
162 |
163 | ### [2.4.2](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.4.1...v2.4.2) (2021-01-19)
164 |
165 |
166 | ### Bug Fixes
167 |
168 | * strip resource query ([#58](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/58)) ([f0a2d81](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/f0a2d81a4feecf87e13649f2930f773c04fa3814))
169 |
170 | ### [2.4.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.4.0...v2.4.1) (2020-11-30)
171 |
172 |
173 | ### Bug Fixes
174 |
175 | * [#43](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/43), [#44](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/44), [#45](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/45) ([#47](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/47)) ([4b8d4de](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/4b8d4def970381126f70c8407eb708c1c975bbf5))
176 | * recompile speedups ([#55](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/55)) ([d862d92](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/d862d9291853c6b7430a0dbdc965b16db0723925))
177 |
178 | ## [2.4.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.3.0...v2.4.0) (2020-11-20)
179 |
180 |
181 | ### Features
182 |
183 | * threads ([#39](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/39)) ([1e38fc7](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/1e38fc77fd575d9e56be0da6a206ded54a8f7c34))
184 |
185 | ## [2.3.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.2.1...v2.3.0) (2020-11-13)
186 |
187 |
188 | ### Features
189 |
190 | * exclude option ([#41](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/41)) ([dc2fac7](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/dc2fac7918c0733f26fa5a1683315bf439370559))
191 |
192 | ### [2.2.1](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v2.2.0...v2.2.1) (2020-11-07)
193 |
194 |
195 | ### Bug Fixes
196 |
197 | * folder pattern ([#36](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/36)) ([e79741e](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/e79741ee22d04c8c6e4d6f11d6869434ed5b339d))
198 |
199 | ## [2.2.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v1.0.0...v2.2.0) (2020-11-04)
200 |
201 |
202 | ### Features
203 |
204 | * feat: only use the import graph, update tests ([#28](https://github.com/webpack-contrib/eslint-webpack-plugin/pull/28)) ([47612f16](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/47612f16894f22f4b5c3848595bba48ca8eb9b0f))
205 |
206 | ### Bug Fixes
207 |
208 | * fix: use compiler.outputFileSystem to write report
209 |
210 | * fix: use fs callback forms because webpack5 does not work with promisify on outputFileSystem methods
211 |
212 | * fix: do not accumulate more taps as watchRuns occur
213 |
214 | * fix: windows path escape, cleanup watch-fixture
215 |
216 | ## [2.1.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v1.0.0...v2.1.0) (2020-07-30)
217 |
218 |
219 | ### Features
220 |
221 | * support typescript ([#8](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/8)) ([6634d96](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/6634d96e7e80dd2d7097479f13a48115e0544f59))
222 |
223 | ## [2.0.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v1.0.0...v2.0.0) (2020-07-26)
224 |
225 |
226 | ### Features
227 |
228 | * ESLint class migration ([#11](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/11)) ([efd5e7d](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/efd5e7d01b8569c5dcb2808f618f56e4857fcf52)), closes [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10) [#10](https://github.com/webpack-contrib/eslint-webpack-plugin/issues/10)
229 |
230 | ### ⚠ BREAKING CHANGES
231 |
232 | * minimum supported eslint version is `7`
233 |
234 | ## [1.0.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v0.1.0...v1.0.0) (2020-05-04)
235 |
236 | ### ⚠ BREAKING CHANGES
237 |
238 | * minimum supported Node.js version is `10.13`
239 | * minimum supported eslint version is `6.0.0`
240 |
241 | ## [0.1.0](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v0.0.1...v0.1.0) (2019-12-20)
242 |
243 |
244 | ### Features
245 |
246 | * support webpack 5 ([b7f3679](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/b7f3679a8d5e5166376caec2a28ed38d6772bcca))
247 |
248 |
249 | ### Bug Fixes
250 |
251 | * hooks and callback error ([3e7c36e](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/3e7c36e78e7c05bb5559adced2f92317affbf1ff))
252 | * resolve file glob patterns ([d5c8820](https://github.com/webpack-contrib/eslint-webpack-plugin/commit/d5c8820d9467e8794a4aa3944bf6ded746d79411))
253 |
254 | ### 0.0.1 (2019-11-12)
255 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | 'Software'), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [![npm][npm]][npm-url]
7 | [![node][node]][node-url]
8 | [![tests][tests]][tests-url]
9 | [![coverage][cover]][cover-url]
10 | [![discussion][discussion]][discussion-url]
11 | [![size][size]][size-url]
12 |
13 | # eslint-webpack-plugin
14 |
15 | > This version of eslint-webpack-plugin only supports webpack 5. For the webpack 4, see the [2.x branch](https://github.com/webpack-contrib/eslint-webpack-plugin/tree/2.x).
16 |
17 | This plugin uses [`ESlint`](https://eslint.org/) to find and fix problems in your JavaScript code during the Webpack build process.
18 |
19 | ## Getting Started
20 |
21 | To begin, you'll need to install `eslint-webpack-plugin`:
22 |
23 | ```console
24 | npm install eslint-webpack-plugin --save-dev
25 | ```
26 |
27 | or
28 |
29 | ```console
30 | yarn add -D eslint-webpack-plugin
31 | ```
32 |
33 | or
34 |
35 | ```console
36 | pnpm add -D eslint-webpack-plugin
37 | ```
38 |
39 | > [!NOTE]
40 | >
41 | > You also need to install `eslint >= 8` from npm, if you haven't already:
42 |
43 | ```console
44 | npm install eslint --save-dev
45 | ```
46 |
47 | or
48 |
49 | ```console
50 | yarn add -D eslint
51 | ```
52 |
53 | or
54 |
55 | ```console
56 | pnpm add -D eslint
57 | ```
58 |
59 | Then add the plugin to your webpack configuration. For example:
60 |
61 | ```js
62 | const ESLintPlugin = require('eslint-webpack-plugin');
63 |
64 | module.exports = {
65 | // ...
66 | plugins: [new ESLintPlugin(options)],
67 | // ...
68 | };
69 | ```
70 |
71 | ## Options
72 |
73 | You can pass [ESLint Node.js API options](https://eslint.org/docs/developer-guide/nodejs-api#-new-eslintoptions).
74 |
75 | > [!NOTE]
76 | >
77 | > The config option you provide will be passed to the `ESLint` class.
78 | > This is a different set of options than what you'd specify in `package.json` or `.eslintrc`.
79 | > See the [ESlint docs](https://eslint.org/docs/developer-guide/nodejs-api#-new-eslintoptions) for more details.
80 |
81 | > [!WARNING]
82 | >
83 | > In eslint-webpack-plugin version 1 the options were passed to the now-deprecated [CLIEngine](https://eslint.org/docs/developer-guide/nodejs-api#cliengine).
84 |
85 | ### `cache`
86 |
87 | - Type:
88 |
89 | ```ts
90 | type cache = boolean;
91 | ```
92 |
93 | - Default: `true`
94 |
95 | The cache is enabled by default to decrease execution time.
96 |
97 | ### `cacheLocation`
98 |
99 | - Type:
100 |
101 | ```ts
102 | type cacheLocation = string;
103 | ```
104 |
105 | - Default: `node_modules/.cache/eslint-webpack-plugin/.eslintcache`
106 |
107 | Specify the path to the cache location. Can be a file or a directory.
108 |
109 | ### `configType`
110 |
111 | - Type:
112 |
113 | ```ts
114 | type configType = 'flat' | 'eslintrc';
115 | ```
116 |
117 | - Default: `flat`
118 |
119 | Specify the type of configuration to use with ESLint.
120 |
121 | - `eslintrc` is the classic configuration format available in most ESLint versions.
122 | - `flat` is the new format introduced in ESLint 8.21.0.
123 |
124 | The new configuration format is explained in its [own documentation](https://eslint.org/docs/latest/use/configure/configuration-files-new).
125 |
126 | ### `context`
127 |
128 | - Type:
129 |
130 | ```ts
131 | type context = string;
132 | ```
133 |
134 | - Default: `compiler.context`
135 |
136 | Base directory for linting.
137 |
138 | ### `eslintPath`
139 |
140 | - Type:
141 |
142 | ```ts
143 | type eslintPath = string;
144 | ```
145 |
146 | - Default: `eslint`
147 |
148 | Path to `eslint` instance that will be used for linting.
149 | If the `eslintPath` is a folder like a official ESlint, or specify a `formatter` option, now you don't have to install `eslint`.
150 |
151 | ### `extensions`
152 |
153 | - Type:
154 |
155 | ```ts
156 | type extensions = string | Array;
157 | ```
158 |
159 | - Default: `'js'`
160 |
161 | Specify file extensions that should be checked.
162 |
163 | ### `exclude`
164 |
165 | - Type:
166 |
167 | ```ts
168 | type exclude = string | Array;
169 | ```
170 |
171 | - Default: `'node_modules'`
172 |
173 | Specify the files/directories to exclude. Must be relative to `options.context`.
174 |
175 | ### `resourceQueryExclude`
176 |
177 | - Type:
178 |
179 | ```ts
180 | type resourceQueryExclude = RegExp | Array;
181 | ```
182 |
183 | - Default: `[]`
184 |
185 | Specify the resource query to exclude.
186 |
187 | ### `files`
188 |
189 | - Type:
190 |
191 | ```ts
192 | type files = string | Array;
193 | ```
194 |
195 | - Default: `null`
196 |
197 | Specify directories, files, or globs. Must be relative to `options.context`.
198 | Directories are traversed recursively looking for files matching `options.extensions`.
199 | File and glob patterns ignore `options.extensions`.
200 |
201 | ### `fix`
202 |
203 | - Type:
204 |
205 | ```ts
206 | type fix = boolean;
207 | ```
208 |
209 | - Default: `false`
210 |
211 | Will enable [ESLint autofix feature](https://eslint.org/docs/developer-guide/nodejs-api#-eslintoutputfixesresults).
212 |
213 | **Be careful: this option will modify source files.**
214 |
215 | ### `formatter`
216 |
217 | - Type:
218 |
219 | ```ts
220 | type formatter = string| (
221 | results: Array,
222 | data?: import('eslint').ESLint.LintResultData | undefined
223 | ) => string
224 | ```
225 |
226 | - Default: `'stylish'`
227 |
228 | Accepts a function that receives an array of ESLint messages (object) as its argument and must return a string as output.
229 | You can use official [ESlint formatters](https://eslint.org/docs/user-guide/formatters/).
230 |
231 | ### `lintDirtyModulesOnly`
232 |
233 | - Type:
234 |
235 | ```ts
236 | type lintDirtyModulesOnly = boolean;
237 | ```
238 |
239 | - Default: `false`
240 |
241 | Lint only changed files, skipping initial lint on build start.
242 |
243 | ### `threads`
244 |
245 | - Type:
246 |
247 | ```ts
248 | type threads = boolean | number;
249 | ```
250 |
251 | - Default: `false`
252 |
253 | Will run lint tasks across a thread pool. The pool size is automatic unless you specify a number.
254 |
255 | ### Errors and Warning
256 |
257 | **By default the plugin will auto adjust error reporting depending on eslint errors/warnings counts.**
258 | You can still force this behavior by using `emitError` **or** `emitWarning` options:
259 |
260 | #### `emitError`
261 |
262 | - Type:
263 |
264 | ```ts
265 | type emitError = boolean;
266 | ```
267 |
268 | - Default: `true`
269 |
270 | The errors found will always be emitted, to disable set to `false`.
271 |
272 | #### `emitWarning`
273 |
274 | - Type:
275 |
276 | ```ts
277 | type emitWarning = boolean;
278 | ```
279 |
280 | - Default: `true`
281 |
282 | The warnings found will always be emitted, to disable set to `false`.
283 |
284 | #### `failOnError`
285 |
286 | - Type:
287 |
288 | ```ts
289 | type failOnError = boolean;
290 | ```
291 |
292 | - Default: `true`
293 |
294 | Will cause the module build to fail if any errors are found, to disable set to `false`.
295 |
296 | #### `failOnWarning`
297 |
298 | - Type:
299 |
300 | ```ts
301 | type failOnWarning = boolean;
302 | ```
303 |
304 | - Default: `false`
305 |
306 | Will cause the module build to fail if any warnings are found, if set to `true`.
307 |
308 | #### `quiet`
309 |
310 | - Type:
311 |
312 | ```ts
313 | type quiet = boolean;
314 | ```
315 |
316 | - Default: `false`
317 |
318 | Will process and report errors only and ignore warnings, if set to `true`.
319 |
320 | #### `outputReport`
321 |
322 | - Type:
323 |
324 | ```ts
325 | type outputReport =
326 | | boolean
327 | | {
328 | filePath?: string | undefined;
329 | formatter?:
330 | | (
331 | | string
332 | | ((
333 | results: Array,
334 | data?: import('eslint').ESLint.LintResultData | undefined,
335 | ) => string)
336 | )
337 | | undefined;
338 | };
339 | ```
340 |
341 | - Default: `false`
342 |
343 | Write ESLint results to a file, for example a checkstyle xml file for use for reporting on Jenkins CI.
344 |
345 | - filePath: Path to output report file (relative to output.path or absolute).
346 | - formatter: You can pass in a different `formatter` for the output file.
347 | if none is passed in the default/configured formatter will be used.
348 |
349 | ## Changelog
350 |
351 | [Changelog](CHANGELOG.md)
352 |
353 | ## Contributing
354 |
355 | We welcome all contributions!
356 | If you're new here, please take a moment to review our contributing guidelines.
357 |
358 | [CONTRIBUTING](./.github/CONTRIBUTING.md)
359 |
360 | ## License
361 |
362 | [MIT](./LICENSE)
363 |
364 | [npm]: https://img.shields.io/npm/v/eslint-webpack-plugin.svg
365 | [npm-url]: https://npmjs.com/package/eslint-webpack-plugin
366 | [node]: https://img.shields.io/node/v/eslint-webpack-plugin.svg
367 | [node-url]: https://nodejs.org
368 | [tests]: https://github.com/webpack-contrib/eslint-webpack-plugin/workflows/eslint-webpack-plugin/badge.svg
369 | [tests-url]: https://github.com/webpack-contrib/eslint-webpack-plugin/actions
370 | [cover]: https://codecov.io/gh/webpack-contrib/eslint-webpack-plugin/branch/master/graph/badge.svg
371 | [cover-url]: https://codecov.io/gh/webpack-contrib/eslint-webpack-plugin
372 | [discussion]: https://img.shields.io/github/discussions/webpack/webpack
373 | [discussion-url]: https://github.com/webpack/webpack/discussions
374 | [size]: https://packagephobia.now.sh/badge?p=eslint-webpack-plugin
375 | [size-url]: https://packagephobia.now.sh/result?p=eslint-webpack-plugin
376 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const MIN_BABEL_VERSION = 7;
2 |
3 | module.exports = (api) => {
4 | api.assertVersion(MIN_BABEL_VERSION);
5 | api.cache(true);
6 |
7 | return {
8 | presets: [
9 | [
10 | '@babel/preset-env',
11 | {
12 | targets: {
13 | node: '14.15.0',
14 | },
15 | },
16 | ],
17 | ],
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | rules: {
4 | 'header-max-length': [0],
5 | 'body-max-line-length': [0],
6 | 'footer-max-line-length': [0],
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/husky.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hooks: {
3 | 'pre-commit': 'lint-staged',
4 | 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | collectCoverage: true,
3 | testEnvironment: 'node',
4 | testTimeout: 60000,
5 | transformIgnorePatterns: ['node_modules/(?!(arrify)/)'],
6 | };
7 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.js': ['eslint --fix', 'prettier --write', 'cspell'],
3 | '*.{json,md,yml,css,ts}': ['prettier --write'],
4 | };
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-webpack-plugin",
3 | "version": "5.0.1",
4 | "description": "A ESLint plugin for webpack",
5 | "license": "MIT",
6 | "repository": "webpack-contrib/eslint-webpack-plugin",
7 | "author": "Ricardo Gobbo de Souza ",
8 | "homepage": "https://github.com/webpack-contrib/eslint-webpack-plugin",
9 | "bugs": "https://github.com/webpack-contrib/eslint-webpack-plugin/issues",
10 | "funding": {
11 | "type": "opencollective",
12 | "url": "https://opencollective.com/webpack"
13 | },
14 | "main": "dist/index.js",
15 | "types": "types/index.d.ts",
16 | "engines": {
17 | "node": ">= 18.12.0"
18 | },
19 | "scripts": {
20 | "start": "npm run build -- -w",
21 | "clean": "del-cli dist types",
22 | "prebuild": "npm run clean",
23 | "build:types": "tsc --declaration --emitDeclarationOnly --outDir types && prettier \"types/**/*.ts\" --write",
24 | "build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files",
25 | "build": "npm-run-all -p \"build:**\"",
26 | "commitlint": "commitlint --from=master",
27 | "security": "npm audit",
28 | "lint:prettier": "prettier -w --list-different .",
29 | "lint:js": "exit 0; //eslint --cache .",
30 | "lint:spelling": "cspell \"**/*.*\"",
31 | "lint:types": "tsc --pretty --noEmit",
32 | "lint": "npm-run-all -l -p \"lint:**\"",
33 | "fix:js": "npm run lint:js -- --fix",
34 | "fix:prettier": "npm run lint:prettier -- --write",
35 | "fix": "npm-run-all -l fix:js fix:prettier",
36 | "test:only": "cross-env NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest --testTimeout=60000",
37 | "test:watch": "npm run test:only -- --watch",
38 | "test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
39 | "pretest": "npm run lint",
40 | "test": "npm run test:coverage",
41 | "prepare": "npm run build",
42 | "release": "standard-version"
43 | },
44 | "files": [
45 | "dist",
46 | "types"
47 | ],
48 | "peerDependencies": {
49 | "eslint": "^8.0.0 || ^9.0.0",
50 | "webpack": "^5.0.0"
51 | },
52 | "dependencies": {
53 | "@types/eslint": "^9.6.1",
54 | "flatted": "^3.3.3",
55 | "jest-worker": "^29.7.0",
56 | "micromatch": "^4.0.8",
57 | "normalize-path": "^3.0.0",
58 | "schema-utils": "^4.3.0"
59 | },
60 | "devDependencies": {
61 | "@babel/cli": "^7.27.0",
62 | "@babel/core": "^7.26.10",
63 | "@babel/preset-env": "^7.26.9",
64 | "@commitlint/cli": "^19.8.0",
65 | "@commitlint/config-conventional": "^19.8.0",
66 | "@types/fs-extra": "^11.0.4",
67 | "@types/micromatch": "^4.0.9",
68 | "@types/node": "^22.14.0",
69 | "@types/normalize-path": "^3.0.2",
70 | "@types/webpack": "^5.28.5",
71 | "@webpack-contrib/eslint-config-webpack": "^3.0.0",
72 | "babel-jest": "^29.7.0",
73 | "chokidar": "^4.0.3",
74 | "cross-env": "^7.0.3",
75 | "cspell": "^8.18.1",
76 | "del": "^8.0.0",
77 | "del-cli": "^6.0.0",
78 | "eslint": "^9.24.0",
79 | "eslint-config-prettier": "^10.1.2",
80 | "fs-extra": "^11.3.0",
81 | "husky": "^9.1.7",
82 | "jest": "^29.7.0",
83 | "lint-staged": "^15.5.0",
84 | "npm-run-all": "^4.1.5",
85 | "prettier": "^3.5.3",
86 | "standard-version": "^9.5.0",
87 | "typescript": "^5.8.3",
88 | "webpack": "^5.99.5"
89 | },
90 | "keywords": [
91 | "eslint",
92 | "lint",
93 | "linter",
94 | "plugin",
95 | "webpack"
96 | ]
97 | }
98 |
--------------------------------------------------------------------------------
/src/ESLintError.js:
--------------------------------------------------------------------------------
1 | class ESLintError extends Error {
2 | /**
3 | * @param {string=} messages
4 | */
5 | constructor(messages) {
6 | super(`[eslint] ${messages}`);
7 | this.name = 'ESLintError';
8 | this.stack = '';
9 | }
10 | }
11 |
12 | module.exports = ESLintError;
13 |
--------------------------------------------------------------------------------
/src/getESLint.js:
--------------------------------------------------------------------------------
1 | const { cpus } = require('os');
2 |
3 | const { Worker: JestWorker } = require('jest-worker');
4 |
5 | // @ts-ignore
6 | const { setup, lintFiles } = require('./worker');
7 | const { getESLintOptions } = require('./options');
8 | const { jsonStringifyReplacerSortKeys } = require('./utils');
9 | const { stringify } = require('flatted');
10 |
11 | /** @type {{[key: string]: any}} */
12 | const cache = {};
13 |
14 | /** @typedef {import('eslint').ESLint} ESLint */
15 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
16 | /** @typedef {import('./options').Options} Options */
17 | /** @typedef {() => Promise} AsyncTask */
18 | /** @typedef {(files: string|string[]) => Promise} LintTask */
19 | /** @typedef {{threads: number, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */
20 | /** @typedef {JestWorker & {lintFiles: LintTask}} Worker */
21 |
22 | /**
23 | * @param {Options} options
24 | * @returns {Promise}
25 | */
26 | async function loadESLint(options) {
27 | const { eslintPath } = options;
28 | const eslint = await setup({
29 | eslintPath,
30 | configType: options.configType,
31 | eslintOptions: getESLintOptions(options),
32 | });
33 |
34 | return {
35 | threads: 1,
36 | lintFiles,
37 | eslint,
38 | // no-op for non-threaded
39 | cleanup: async () => {},
40 | };
41 | }
42 |
43 | /**
44 | * @param {string|undefined} key
45 | * @param {number} poolSize
46 | * @param {Options} options
47 | * @returns {Promise}
48 | */
49 | async function loadESLintThreaded(key, poolSize, options) {
50 | const cacheKey = getCacheKey(key, options);
51 | const { eslintPath = 'eslint' } = options;
52 | const source = require.resolve('./worker');
53 | const workerOptions = {
54 | enableWorkerThreads: true,
55 | numWorkers: poolSize,
56 | setupArgs: [
57 | {
58 | eslintPath,
59 | configType: options.configType,
60 | eslintOptions: getESLintOptions(options),
61 | },
62 | ],
63 | };
64 |
65 | const local = await loadESLint(options);
66 |
67 | let worker = /** @type {Worker?} */ (new JestWorker(source, workerOptions));
68 |
69 | /** @type {Linter} */
70 | const context = {
71 | ...local,
72 | threads: poolSize,
73 | lintFiles: async (files) =>
74 | (worker && (await worker.lintFiles(files))) ||
75 | /* istanbul ignore next */ [],
76 | cleanup: async () => {
77 | cache[cacheKey] = local;
78 | context.lintFiles = (files) => local.lintFiles(files);
79 | if (worker) {
80 | worker.end();
81 | worker = null;
82 | }
83 | },
84 | };
85 |
86 | return context;
87 | }
88 |
89 | /**
90 | * @param {string|undefined} key
91 | * @param {Options} options
92 | * @returns {Promise}
93 | */
94 | async function getESLint(key, { threads, ...options }) {
95 | const max =
96 | typeof threads !== 'number'
97 | ? threads
98 | ? cpus().length - 1
99 | : 1
100 | : /* istanbul ignore next */
101 | threads;
102 |
103 | const cacheKey = getCacheKey(key, { threads, ...options });
104 | if (!cache[cacheKey]) {
105 | cache[cacheKey] =
106 | max > 1
107 | ? await loadESLintThreaded(key, max, options)
108 | : await loadESLint(options);
109 | }
110 | return cache[cacheKey];
111 | }
112 |
113 | /**
114 | * @param {string|undefined} key
115 | * @param {Options} options
116 | * @returns {string}
117 | */
118 | function getCacheKey(key, options) {
119 | return stringify({ key, options }, jsonStringifyReplacerSortKeys);
120 | }
121 |
122 | module.exports = {
123 | loadESLint,
124 | loadESLintThreaded,
125 | getESLint,
126 | };
127 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const { isAbsolute, join } = require('path');
2 |
3 | const { isMatch } = require('micromatch');
4 |
5 | const { getOptions } = require('./options');
6 | const linter = require('./linter');
7 | const { arrify, parseFiles, parseFoldersToGlobs } = require('./utils');
8 |
9 | /** @typedef {import('webpack').Compiler} Compiler */
10 | /** @typedef {import('webpack').Module} Module */
11 | /** @typedef {import('webpack').NormalModule} NormalModule */
12 | /** @typedef {import('./options').Options} Options */
13 |
14 | const ESLINT_PLUGIN = 'ESLintWebpackPlugin';
15 | const DEFAULT_FOLDER_TO_EXCLUDE = '**/node_modules/**';
16 |
17 | let compilerId = 0;
18 |
19 | class ESLintWebpackPlugin {
20 | /**
21 | * @param {Options} options
22 | */
23 | constructor(options = {}) {
24 | this.key = ESLINT_PLUGIN;
25 | this.options = getOptions(options);
26 | this.run = this.run.bind(this);
27 | }
28 |
29 | /**
30 | * @param {Compiler} compiler
31 | * @returns {void}
32 | */
33 | apply(compiler) {
34 | // Generate key for each compilation,
35 | // this differentiates one from the other when being cached.
36 | this.key = compiler.name || `${this.key}_${(compilerId += 1)}`;
37 |
38 | const excludedFiles = parseFiles(
39 | this.options.exclude || [],
40 | this.getContext(compiler),
41 | );
42 | const resourceQueries = arrify(this.options.resourceQueryExclude || []);
43 | const excludedResourceQueries = resourceQueries.map((item) =>
44 | item instanceof RegExp ? item : new RegExp(item),
45 | );
46 |
47 | const options = {
48 | ...this.options,
49 | exclude: excludedFiles,
50 | resourceQueryExclude: excludedResourceQueries,
51 | extensions: arrify(this.options.extensions),
52 | files: parseFiles(this.options.files || '', this.getContext(compiler)),
53 | };
54 |
55 | const foldersToExclude = this.options.exclude
56 | ? options.exclude
57 | : DEFAULT_FOLDER_TO_EXCLUDE;
58 | const exclude = parseFoldersToGlobs(foldersToExclude);
59 | const wanted = parseFoldersToGlobs(options.files, options.extensions);
60 |
61 | // If `lintDirtyModulesOnly` is disabled,
62 | // execute the linter on the build
63 | if (!this.options.lintDirtyModulesOnly) {
64 | compiler.hooks.run.tapPromise(this.key, (c) =>
65 | this.run(c, options, wanted, exclude),
66 | );
67 | }
68 |
69 | let hasCompilerRunByDirtyModule = this.options.lintDirtyModulesOnly;
70 |
71 | compiler.hooks.watchRun.tapPromise(this.key, (c) => {
72 | if (!hasCompilerRunByDirtyModule)
73 | return this.run(c, options, wanted, exclude);
74 |
75 | hasCompilerRunByDirtyModule = false;
76 |
77 | return Promise.resolve();
78 | });
79 | }
80 |
81 | /**
82 | * @param {Compiler} compiler
83 | * @param {Omit & {resourceQueryExclude: RegExp[]}} options
84 | * @param {string[]} wanted
85 | * @param {string[]} exclude
86 | */
87 | async run(compiler, options, wanted, exclude) {
88 | const isCompilerHooked = compiler.hooks.compilation.taps.find(
89 | ({ name }) => name === this.key,
90 | );
91 |
92 | if (isCompilerHooked) return;
93 |
94 | compiler.hooks.compilation.tap(this.key, async (compilation) => {
95 | /** @type {import('./linter').Linter} */
96 | let lint;
97 | /** @type {import('./linter').Reporter} */
98 | let report;
99 | /** @type number */
100 | let threads;
101 |
102 | try {
103 | ({ lint, report, threads } = await linter(
104 | this.key,
105 | options,
106 | compilation,
107 | ));
108 | } catch (e) {
109 | compilation.errors.push(e);
110 | return;
111 | }
112 |
113 | /** @type {string[]} */
114 | const files = [];
115 |
116 | // Add the file to be linted
117 | compilation.hooks.succeedModule.tap(this.key, addFile);
118 |
119 | if (!this.options.lintDirtyModulesOnly) {
120 | compilation.hooks.stillValidModule.tap(this.key, addFile);
121 | }
122 |
123 | /**
124 | * @param {Module} module
125 | */
126 | function addFile(module) {
127 | const { resource } = /** @type {NormalModule} */ (module);
128 |
129 | if (!resource) return;
130 |
131 | const [file, query] = resource.split('?');
132 | const isFileNotListed = file && !files.includes(file);
133 | const isFileWanted =
134 | isMatch(file, wanted, { dot: true }) &&
135 | !isMatch(file, exclude, { dot: true });
136 | const isQueryNotExclude = options.resourceQueryExclude.every(
137 | (reg) => !reg.test(query),
138 | );
139 |
140 | if (isFileNotListed && isFileWanted && isQueryNotExclude) {
141 | files.push(file);
142 |
143 | if (threads > 1) lint(file);
144 | }
145 | }
146 |
147 | // Lint all files added
148 | compilation.hooks.finishModules.tap(this.key, () => {
149 | if (files.length > 0 && threads <= 1) lint(files);
150 | });
151 |
152 | // await and interpret results
153 | compilation.hooks.additionalAssets.tapAsync(
154 | this.key,
155 | async function (callback) {
156 | const { errors, warnings, generateReportAsset } = await report();
157 |
158 | if (warnings) {
159 | // @ts-ignore
160 | compilation.warnings.push(warnings);
161 | }
162 |
163 | if (errors) {
164 | // @ts-ignore
165 | compilation.errors.push(errors);
166 | }
167 |
168 | if (generateReportAsset) {
169 | await generateReportAsset(compilation);
170 | }
171 |
172 | if (warnings && options.failOnWarning) {
173 | callback(warnings);
174 | } else if (errors && options.failOnError) {
175 | callback(errors);
176 | } else {
177 | callback();
178 | }
179 | },
180 | );
181 | });
182 | }
183 |
184 | /**
185 | *
186 | * @param {Compiler} compiler
187 | * @returns {string}
188 | */
189 | getContext(compiler) {
190 | const compilerContext = String(compiler.options.context);
191 | const optionContext = this.options.context;
192 |
193 | if (!optionContext) return compilerContext;
194 |
195 | if (isAbsolute(optionContext)) return optionContext;
196 |
197 | return join(compilerContext, optionContext);
198 | }
199 | }
200 |
201 | module.exports = ESLintWebpackPlugin;
202 |
--------------------------------------------------------------------------------
/src/linter.js:
--------------------------------------------------------------------------------
1 | const { dirname, isAbsolute, join } = require('path');
2 |
3 | const ESLintError = require('./ESLintError');
4 | const { getESLint } = require('./getESLint');
5 | const { arrify } = require('./utils');
6 |
7 | /** @typedef {import('eslint').ESLint} ESLint */
8 | /** @typedef {import('eslint').ESLint.Formatter} Formatter */
9 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
10 | /** @typedef {import('webpack').Compiler} Compiler */
11 | /** @typedef {import('webpack').Compilation} Compilation */
12 | /** @typedef {import('./options').Options} Options */
13 | /** @typedef {import('./options').FormatterFunction} FormatterFunction */
14 | /** @typedef {(compilation: Compilation) => Promise} GenerateReport */
15 | /** @typedef {{errors?: ESLintError, warnings?: ESLintError, generateReportAsset?: GenerateReport}} Report */
16 | /** @typedef {() => Promise} Reporter */
17 | /** @typedef {(files: string|string[]) => void} Linter */
18 | /** @typedef {{[files: string]: LintResult}} LintResultMap */
19 |
20 | /** @type {WeakMap} */
21 | const resultStorage = new WeakMap();
22 |
23 | /**
24 | * @param {string|undefined} key
25 | * @param {Options} options
26 | * @param {Compilation} compilation
27 | * @returns {Promise<{lint: Linter, report: Reporter, threads: number}>}
28 | */
29 | async function linter(key, options, compilation) {
30 | /** @type {ESLint} */
31 | let eslint;
32 |
33 | /** @type {(files: string|string[]) => Promise} */
34 | let lintFiles;
35 |
36 | /** @type {() => Promise} */
37 | let cleanup;
38 |
39 | /** @type number */
40 | let threads;
41 |
42 | /** @type {Promise[]} */
43 | const rawResults = [];
44 |
45 | const crossRunResultStorage = getResultStorage(compilation);
46 |
47 | try {
48 | ({ eslint, lintFiles, cleanup, threads } = await getESLint(key, options));
49 | } catch (e) {
50 | throw new ESLintError(e.message);
51 | }
52 |
53 | return {
54 | lint,
55 | report,
56 | threads,
57 | };
58 |
59 | /**
60 | * @param {string | string[]} files
61 | */
62 | function lint(files) {
63 | for (const file of arrify(files)) {
64 | delete crossRunResultStorage[file];
65 | }
66 | rawResults.push(
67 | lintFiles(files).catch((e) => {
68 | // @ts-ignore
69 | compilation.errors.push(new ESLintError(e.message));
70 | return [];
71 | }),
72 | );
73 | }
74 |
75 | async function report() {
76 | // Filter out ignored files.
77 | let results = await removeIgnoredWarnings(
78 | eslint,
79 | // Get the current results, resetting the rawResults to empty
80 | await flatten(rawResults.splice(0, rawResults.length)),
81 | );
82 |
83 | await cleanup();
84 |
85 | for (const result of results) {
86 | crossRunResultStorage[result.filePath] = result;
87 | }
88 |
89 | results = Object.values(crossRunResultStorage);
90 |
91 | // do not analyze if there are no results or eslint config
92 | if (!results || results.length < 1) {
93 | return {};
94 | }
95 |
96 | const formatter = await loadFormatter(eslint, options.formatter);
97 | const { errors, warnings } = await formatResults(
98 | formatter,
99 | parseResults(options, results),
100 | );
101 |
102 | return {
103 | errors,
104 | warnings,
105 | generateReportAsset,
106 | };
107 |
108 | /**
109 | * @param {Compilation} compilation
110 | * @returns {Promise}
111 | */
112 | async function generateReportAsset({ compiler }) {
113 | const { outputReport } = options;
114 | /**
115 | * @param {string} name
116 | * @param {string | Buffer} content
117 | */
118 | const save = (name, content) =>
119 | /** @type {Promise} */ (
120 | new Promise((finish, bail) => {
121 | if (!compiler.outputFileSystem) return;
122 |
123 | const { mkdir, writeFile } = compiler.outputFileSystem;
124 |
125 | mkdir(dirname(name), { recursive: true }, (err) => {
126 | /* istanbul ignore if */
127 | if (err) bail(err);
128 | else
129 | writeFile(name, content, (/** @type {any} */ err2) => {
130 | /* istanbul ignore if */
131 | if (err2) bail(err2);
132 | else finish();
133 | });
134 | });
135 | })
136 | );
137 |
138 | if (!outputReport || !outputReport.filePath) {
139 | return;
140 | }
141 |
142 | const content = await (outputReport.formatter
143 | ? (await loadFormatter(eslint, outputReport.formatter)).format(results)
144 | : formatter.format(results));
145 |
146 | let { filePath } = outputReport;
147 | if (!isAbsolute(filePath)) {
148 | filePath = join(compiler.outputPath, filePath);
149 | }
150 |
151 | await save(filePath, content);
152 | }
153 | }
154 | }
155 |
156 | /**
157 | * @param {Formatter} formatter
158 | * @param {{ errors: LintResult[]; warnings: LintResult[]; }} results
159 | * @returns {Promise<{errors?: ESLintError, warnings?: ESLintError}>}
160 | */
161 | async function formatResults(formatter, results) {
162 | let errors;
163 | let warnings;
164 | if (results.warnings.length > 0) {
165 | warnings = new ESLintError(await formatter.format(results.warnings));
166 | }
167 |
168 | if (results.errors.length > 0) {
169 | errors = new ESLintError(await formatter.format(results.errors));
170 | }
171 |
172 | return {
173 | errors,
174 | warnings,
175 | };
176 | }
177 |
178 | /**
179 | * @param {Options} options
180 | * @param {LintResult[]} results
181 | * @returns {{errors: LintResult[], warnings: LintResult[]}}
182 | */
183 | function parseResults(options, results) {
184 | /** @type {LintResult[]} */
185 | const errors = [];
186 |
187 | /** @type {LintResult[]} */
188 | const warnings = [];
189 |
190 | results.forEach((file) => {
191 | if (fileHasErrors(file)) {
192 | const messages = file.messages.filter(
193 | (message) => options.emitError && message.severity === 2,
194 | );
195 |
196 | if (messages.length > 0) {
197 | errors.push({ ...file, messages });
198 | }
199 | }
200 |
201 | if (fileHasWarnings(file)) {
202 | const messages = file.messages.filter(
203 | (message) => options.emitWarning && message.severity === 1,
204 | );
205 |
206 | if (messages.length > 0) {
207 | warnings.push({ ...file, messages });
208 | }
209 | }
210 | });
211 |
212 | return {
213 | errors,
214 | warnings,
215 | };
216 | }
217 |
218 | /**
219 | * @param {LintResult} file
220 | * @returns {boolean}
221 | */
222 | function fileHasErrors(file) {
223 | return file.errorCount > 0;
224 | }
225 |
226 | /**
227 | * @param {LintResult} file
228 | * @returns {boolean}
229 | */
230 | function fileHasWarnings(file) {
231 | return file.warningCount > 0;
232 | }
233 |
234 | /**
235 | * @param {ESLint} eslint
236 | * @param {string|FormatterFunction=} formatter
237 | * @returns {Promise}
238 | */
239 | async function loadFormatter(eslint, formatter) {
240 | if (typeof formatter === 'function') {
241 | return { format: formatter };
242 | }
243 |
244 | if (typeof formatter === 'string') {
245 | try {
246 | return eslint.loadFormatter(formatter);
247 | } catch (_) {
248 | // Load the default formatter.
249 | }
250 | }
251 |
252 | return eslint.loadFormatter();
253 | }
254 |
255 | /**
256 | * @param {ESLint} eslint
257 | * @param {LintResult[]} results
258 | * @returns {Promise}
259 | */
260 | async function removeIgnoredWarnings(eslint, results) {
261 | const filterPromises = results.map(async (result) => {
262 | // Short circuit the call to isPathIgnored.
263 | // fatal is false for ignored file warnings.
264 | // ruleId is unset for internal ESLint errors.
265 | // line is unset for warnings not involving file contents.
266 | const { messages, warningCount, errorCount, filePath } = result;
267 | const [firstMessage] = messages;
268 | const hasWarning = warningCount === 1 && errorCount === 0;
269 | const ignored =
270 | messages.length === 0 ||
271 | (hasWarning &&
272 | !firstMessage.fatal &&
273 | !firstMessage.ruleId &&
274 | !firstMessage.line &&
275 | (await eslint.isPathIgnored(filePath)));
276 | return ignored ? false : result;
277 | });
278 |
279 | return (await Promise.all(filterPromises)).filter(
280 | (result) => result !== false,
281 | );
282 | }
283 |
284 | /**
285 | * @param {Promise[]} results
286 | * @returns {Promise}
287 | */
288 | async function flatten(results) {
289 | /**
290 | * @param {LintResult[]} acc
291 | * @param {LintResult[]} list
292 | */
293 | const flat = (acc, list) => [...acc, ...list];
294 | return (await Promise.all(results)).reduce(flat, []);
295 | }
296 |
297 | /**
298 | * @param {Compilation} compilation
299 | * @returns {LintResultMap}
300 | */
301 | function getResultStorage({ compiler }) {
302 | let storage = resultStorage.get(compiler);
303 | if (!storage) {
304 | resultStorage.set(compiler, (storage = {}));
305 | }
306 | return storage;
307 | }
308 |
309 | module.exports = linter;
310 |
--------------------------------------------------------------------------------
/src/options.js:
--------------------------------------------------------------------------------
1 | const { validate } = require('schema-utils');
2 |
3 | const schema = require('./options.json');
4 |
5 | /** @typedef {import("eslint").ESLint.Options} ESLintOptions */
6 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
7 |
8 | /**
9 | * @callback FormatterFunction
10 | * @param {LintResult[]} results
11 | * @returns {string}
12 | */
13 |
14 | /**
15 | * @typedef {Object} OutputReport
16 | * @property {string=} filePath
17 | * @property {string|FormatterFunction=} formatter
18 | */
19 |
20 | /**
21 | * @typedef {Object} PluginOptions
22 | * @property {string=} context
23 | * @property {boolean=} emitError
24 | * @property {boolean=} emitWarning
25 | * @property {string=} eslintPath
26 | * @property {string|string[]=} exclude
27 | * @property {string|string[]=} extensions
28 | * @property {boolean=} failOnError
29 | * @property {boolean=} failOnWarning
30 | * @property {string|string[]=} files
31 | * @property {boolean=} fix
32 | * @property {string|FormatterFunction=} formatter
33 | * @property {boolean=} lintDirtyModulesOnly
34 | * @property {boolean=} quiet
35 | * @property {OutputReport=} outputReport
36 | * @property {number|boolean=} threads
37 | * @property {RegExp|RegExp[]=} resourceQueryExclude
38 | * @property {string=} configType
39 | */
40 |
41 | /** @typedef {PluginOptions & ESLintOptions} Options */
42 |
43 | /**
44 | * @param {Options} pluginOptions
45 | * @returns {PluginOptions}
46 | */
47 | function getOptions(pluginOptions) {
48 | const options = {
49 | cache: true,
50 | cacheLocation: 'node_modules/.cache/eslint-webpack-plugin/.eslintcache',
51 | configType: 'flat',
52 | extensions: 'js',
53 | emitError: true,
54 | emitWarning: true,
55 | failOnError: true,
56 | resourceQueryExclude: [],
57 | ...pluginOptions,
58 | ...(pluginOptions.quiet ? { emitError: true, emitWarning: false } : {}),
59 | };
60 |
61 | // @ts-ignore
62 | validate(schema, options, {
63 | name: 'ESLint Webpack Plugin',
64 | baseDataPath: 'options',
65 | });
66 |
67 | return options;
68 | }
69 |
70 | /**
71 | * @param {Options} loaderOptions
72 | * @returns {ESLintOptions}
73 | */
74 | function getESLintOptions(loaderOptions) {
75 | const eslintOptions = { ...loaderOptions };
76 |
77 | // Keep the fix option because it is common to both the loader and ESLint.
78 | const { fix, extensions, ...eslintOnlyOptions } = schema.properties;
79 |
80 | // No need to guard the for-in because schema.properties has hardcoded keys.
81 | // eslint-disable-next-line guard-for-in
82 | for (const option in eslintOnlyOptions) {
83 | // @ts-ignore
84 | delete eslintOptions[option];
85 | }
86 |
87 | // Some options aren't available in flat mode
88 | if (loaderOptions.configType === 'flat') {
89 | delete eslintOptions.extensions;
90 | }
91 |
92 | return eslintOptions;
93 | }
94 |
95 | module.exports = {
96 | getOptions,
97 | getESLintOptions,
98 | };
99 |
--------------------------------------------------------------------------------
/src/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "additionalProperties": true,
4 | "properties": {
5 | "configType": {
6 | "description": "Specify the type of configuration to use with ESLint, `flat` or `eslintrc`.",
7 | "type": "string"
8 | },
9 | "context": {
10 | "description": "A string indicating the root of your files.",
11 | "type": "string"
12 | },
13 | "emitError": {
14 | "description": "The errors found will always be emitted, to disable set to `false`.",
15 | "type": "boolean"
16 | },
17 | "emitWarning": {
18 | "description": "The warnings found will always be emitted, to disable set to `false`.",
19 | "type": "boolean"
20 | },
21 | "eslintPath": {
22 | "description": "Path to `eslint` instance that will be used for linting. If the `eslintPath` is a folder like a official eslint, or specify a `formatter` option. now you don't have to install `eslint`.",
23 | "type": "string"
24 | },
25 | "exclude": {
26 | "description": "Specify the files and/or directories to exclude. Must be relative to `options.context`.",
27 | "anyOf": [{ "type": "string" }, { "type": "array" }]
28 | },
29 | "resourceQueryExclude": {
30 | "description": "Specify the resource query to exclude.",
31 | "anyOf": [{ "instanceof": "RegExp" }, { "type": "array" }]
32 | },
33 | "failOnError": {
34 | "description": "Will cause the module build to fail if there are any errors, to disable set to `false`.",
35 | "type": "boolean"
36 | },
37 | "failOnWarning": {
38 | "description": "Will cause the module build to fail if there are any warnings, if set to `true`.",
39 | "type": "boolean"
40 | },
41 | "files": {
42 | "description": "Specify the files and/or directories to traverse. Must be relative to `options.context`.",
43 | "anyOf": [{ "type": "string" }, { "type": "array" }]
44 | },
45 | "extensions": {
46 | "description": "Specify extensions that should be checked.",
47 | "anyOf": [{ "type": "string" }, { "type": "array" }]
48 | },
49 | "fix": {
50 | "description": "Will enable ESLint autofix feature",
51 | "type": "boolean"
52 | },
53 | "formatter": {
54 | "description": "Accepts a function that will have one argument: an array of eslint messages (object). The function must return the output as a string.",
55 | "anyOf": [{ "type": "string" }, { "instanceof": "Function" }]
56 | },
57 | "lintDirtyModulesOnly": {
58 | "description": "Lint only changed files, skip lint on start.",
59 | "type": "boolean"
60 | },
61 | "quiet": {
62 | "description": "Will process and report errors only and ignore warnings, if set to `true`.",
63 | "type": "boolean"
64 | },
65 | "outputReport": {
66 | "description": "Write the output of the errors to a file, for example a checkstyle xml file for use for reporting on Jenkins CI",
67 | "anyOf": [
68 | {
69 | "type": "boolean"
70 | },
71 | {
72 | "type": "object",
73 | "additionalProperties": false,
74 | "properties": {
75 | "filePath": {
76 | "description": "The `filePath` is relative to the webpack config: output.path",
77 | "anyOf": [{ "type": "string" }]
78 | },
79 | "formatter": {
80 | "description": "You can pass in a different formatter for the output file, if none is passed in the default/configured formatter will be used",
81 | "anyOf": [{ "type": "string" }, { "instanceof": "Function" }]
82 | }
83 | }
84 | }
85 | ]
86 | },
87 | "threads": {
88 | "description": "Default is false. Set to true for an auto-selected pool size based on number of cpus. Set to a number greater than 1 to set an explicit pool size. Set to false, 1, or less to disable and only run in main process.",
89 | "anyOf": [{ "type": "number" }, { "type": "boolean" }]
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const { statSync } = require('fs');
3 |
4 | const normalizePath = require('normalize-path');
5 |
6 | /**
7 | * @template T
8 | * @param {T} value
9 | * @return {
10 | T extends (null | undefined)
11 | ? []
12 | : T extends string
13 | ? [string]
14 | : T extends readonly unknown[]
15 | ? T
16 | : T extends Iterable
17 | ? T[]
18 | : [T]
19 | }
20 | */
21 | /* istanbul ignore next */
22 | function arrify(value) {
23 | // eslint-disable-next-line no-undefined
24 | if (value === null || value === undefined) {
25 | // @ts-ignore
26 | return [];
27 | }
28 |
29 | if (Array.isArray(value)) {
30 | // @ts-ignore
31 | return value;
32 | }
33 |
34 | if (typeof value === 'string') {
35 | // @ts-ignore
36 | return [value];
37 | }
38 |
39 | // @ts-ignore
40 | if (typeof value[Symbol.iterator] === 'function') {
41 | // @ts-ignore
42 | return [...value];
43 | }
44 |
45 | // @ts-ignore
46 | return [value];
47 | }
48 |
49 | /**
50 | * @param {string|string[]} files
51 | * @param {string} context
52 | * @returns {string[]}
53 | */
54 | function parseFiles(files, context) {
55 | return arrify(files).map((/** @type {string} */ file) =>
56 | normalizePath(resolve(context, file)),
57 | );
58 | }
59 |
60 | /**
61 | * @param {string|string[]} patterns
62 | * @param {string|string[]} extensions
63 | * @returns {string[]}
64 | */
65 | function parseFoldersToGlobs(patterns, extensions = []) {
66 | const extensionsList = arrify(extensions);
67 | const [prefix, postfix] = extensionsList.length > 1 ? ['{', '}'] : ['', ''];
68 | const extensionsGlob = extensionsList
69 | .map((/** @type {string} */ extension) => extension.replace(/^\./u, ''))
70 | .join(',');
71 |
72 | return arrify(patterns).map((/** @type {string} */ pattern) => {
73 | try {
74 | // The patterns are absolute because they are prepended with the context.
75 | const stats = statSync(pattern);
76 | /* istanbul ignore else */
77 | if (stats.isDirectory()) {
78 | return pattern.replace(
79 | /[/\\]*?$/u,
80 | `/**${
81 | extensionsGlob ? `/*.${prefix + extensionsGlob + postfix}` : ''
82 | }`,
83 | );
84 | }
85 | } catch (_) {
86 | // Return the pattern as is on error.
87 | }
88 | return pattern;
89 | });
90 | }
91 |
92 | /**
93 | * @param {string} _ key, but unused
94 | * @param {any} value
95 | */
96 | const jsonStringifyReplacerSortKeys = (_, value) => {
97 | /**
98 | * @param {{ [x: string]: any; }} sorted
99 | * @param {string | number} key
100 | */
101 | const insert = (sorted, key) => {
102 | // eslint-disable-next-line no-param-reassign
103 | sorted[key] = value[key];
104 | return sorted;
105 | };
106 |
107 | if (value instanceof Object && !(value instanceof Array)) {
108 | let sorted = Object.keys(value).sort().reduce(insert, {});
109 | Object.keys(value).forEach((key) => delete value[key]);
110 | Object.assign(value, sorted);
111 | }
112 |
113 | return value;
114 | };
115 |
116 | module.exports = {
117 | arrify,
118 | parseFiles,
119 | parseFoldersToGlobs,
120 | jsonStringifyReplacerSortKeys,
121 | };
122 |
--------------------------------------------------------------------------------
/src/worker.js:
--------------------------------------------------------------------------------
1 | /** @typedef {import('eslint').ESLint} ESLint */
2 | /** @typedef {import('eslint').ESLint.Options} ESLintOptions */
3 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
4 | /** @typedef {{new (arg0: ESLintOptions): ESLint; outputFixes: (arg0: LintResult[]) => any;}} ESLintClass */
5 |
6 | Object.assign(module.exports, {
7 | lintFiles,
8 | setup,
9 | });
10 |
11 | /** @type {ESLintClass} */
12 | let ESLint;
13 |
14 | /** @type {ESLint} */
15 | let eslint;
16 |
17 | /** @type {boolean} */
18 | let fix;
19 |
20 | /**
21 | * @typedef {object} setupOptions
22 | * @property {string=} eslintPath - import path of eslint
23 | * @property {string=} configType
24 | * @property {ESLintOptions} eslintOptions - linter options
25 | *
26 | * @param {setupOptions} arg0 - setup worker
27 | */
28 | function setup({ eslintPath, configType, eslintOptions }) {
29 | fix = !!(eslintOptions && eslintOptions.fix);
30 | const eslintModule = require(eslintPath || 'eslint');
31 |
32 | if (eslintModule.ESLint && parseFloat(eslintModule.ESLint.version) >= 9) {
33 | return eslintModule
34 | .loadESLint({ useFlatConfig: configType === 'flat' })
35 | .then((/** @type {ESLintClass} */ classESLint) => {
36 | ESLint = classESLint;
37 | eslint = new ESLint(eslintOptions);
38 | return eslint;
39 | });
40 | }
41 |
42 | let FlatESLint;
43 |
44 | if (eslintModule.LegacyESLint) {
45 | ESLint = eslintModule.LegacyESLint;
46 | ({ FlatESLint } = eslintModule);
47 | } else {
48 | ({ ESLint } = eslintModule);
49 |
50 | if (configType === 'flat') {
51 | throw new Error(
52 | "Couldn't find FlatESLint, you might need to set eslintPath to 'eslint/use-at-your-own-risk'",
53 | );
54 | }
55 | }
56 |
57 | if (configType === 'flat') {
58 | eslint = new FlatESLint(eslintOptions);
59 | } else {
60 | eslint = new ESLint(eslintOptions);
61 | }
62 |
63 | return eslint;
64 | }
65 |
66 | /**
67 | * @param {string | string[]} files
68 | */
69 | async function lintFiles(files) {
70 | /** @type {LintResult[]} */
71 | const result = await eslint.lintFiles(files);
72 | // if enabled, use eslint autofixing where possible
73 | if (fix) {
74 | await ESLint.outputFixes(result);
75 | }
76 | return result;
77 | }
78 |
--------------------------------------------------------------------------------
/test/.badeslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "bar"
3 | }
4 |
--------------------------------------------------------------------------------
/test/autofix-stop.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import { copySync, removeSync } from 'fs-extra';
4 | import chokidar from 'chokidar';
5 |
6 | import pack from './utils/pack';
7 |
8 | describe('autofix stop', () => {
9 | const entry = join(__dirname, 'fixtures/nonfixable-clone.js');
10 |
11 | let changed = false;
12 | let watcher;
13 |
14 | beforeAll(() => {
15 | copySync(join(__dirname, 'fixtures/nonfixable.js'), entry);
16 |
17 | watcher = chokidar.watch(entry);
18 | watcher.on('change', () => {
19 | changed = true;
20 | });
21 | });
22 |
23 | afterAll(() => {
24 | watcher.close();
25 | removeSync(entry);
26 | });
27 |
28 | it('should not change file if there are no fixable errors/warnings', async () => {
29 | const compiler = pack('nonfixable-clone', { fix: true });
30 |
31 | await compiler.runAsync();
32 | expect(changed).toBe(false);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/test/autofix.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import { copySync, removeSync, readFileSync } from 'fs-extra';
4 |
5 | import pack from './utils/pack';
6 |
7 | describe('autofix stop', () => {
8 | const entry = join(__dirname, 'fixtures/fixable-clone.js');
9 |
10 | beforeAll(() => {
11 | copySync(join(__dirname, 'fixtures/fixable.js'), entry);
12 | });
13 |
14 | afterAll(() => {
15 | removeSync(entry);
16 | });
17 |
18 | test.each([[{}], [{ threads: false }]])(
19 | 'should not throw error if file ok after auto-fixing',
20 | async (cfg) => {
21 | const compiler = pack('fixable-clone', {
22 | ...cfg,
23 | fix: true,
24 | extensions: ['js', 'cjs', 'mjs'],
25 | overrideConfig: {
26 | rules: { semi: ['error', 'always'] },
27 | },
28 | });
29 |
30 | const stats = await compiler.runAsync();
31 | expect(stats.hasWarnings()).toBe(false);
32 | expect(stats.hasErrors()).toBe(false);
33 | expect(readFileSync(entry).toString('utf8')).toMatchInlineSnapshot(`
34 | "function foo() {
35 | return true;
36 | }
37 |
38 | foo();
39 | "
40 | `);
41 | },
42 | );
43 | });
44 |
--------------------------------------------------------------------------------
/test/cached.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import { removeSync } from 'fs-extra';
4 |
5 | import webpack from 'webpack';
6 |
7 | import conf from './utils/conf';
8 |
9 | describe('error (cached module)', () => {
10 | const cacheLocation = join(__dirname, 'cache');
11 |
12 | beforeEach(() => {
13 | removeSync(cacheLocation);
14 | });
15 |
16 | afterAll(() => {
17 | removeSync(cacheLocation);
18 | });
19 |
20 | it('should return error even if module is cached', (done) => {
21 | const config = conf('error');
22 | config.cache = {
23 | type: 'filesystem',
24 | idleTimeout: 0,
25 | idleTimeoutAfterLargeChanges: 0,
26 | idleTimeoutForInitialStore: 0,
27 | cacheLocation,
28 | };
29 |
30 | const c1 = webpack(config);
31 |
32 | c1.run((err1, stats1) => {
33 | expect(err1).toBeNull();
34 | expect(stats1.hasWarnings()).toBe(false);
35 | expect(stats1.hasErrors()).toBe(true);
36 |
37 | c1.close(() => {
38 | const c2 = webpack(config);
39 | c2.run((err2, stats2) => {
40 | expect(err2).toBeNull();
41 | expect(stats2.hasWarnings()).toBe(false);
42 | expect(stats2.hasErrors()).toBe(true);
43 |
44 | done();
45 | });
46 | });
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/child-compiler.test.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 |
3 | import conf from './utils/conf';
4 |
5 | const PLUGIN_NAME = 'ChildPlugin';
6 | class ChildPlugin {
7 | constructor(options) {
8 | this.options = webpack.config.getNormalizedWebpackOptions(options);
9 | }
10 |
11 | apply(compiler) {
12 | compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => {
13 | const childCompiler = compilation.createChildCompiler(PLUGIN_NAME);
14 | webpack.EntryOptionPlugin.applyEntryOption(
15 | childCompiler,
16 | compilation.compiler.context,
17 | this.options.entry,
18 | );
19 | childCompiler.runAsChild(() => {
20 | callback();
21 | });
22 | });
23 | }
24 | }
25 |
26 | describe('child compiler', () => {
27 | it('should have linting process', (done) => {
28 | const config = conf('good', { threads: false });
29 | config.plugins.push(
30 | new ChildPlugin({
31 | entry: {
32 | child: './child-entry',
33 | },
34 | }),
35 | );
36 | webpack(config).run((err, stats) => {
37 | expect(err).toBeNull();
38 | expect(stats.hasErrors()).toBe(false);
39 | expect(stats.hasWarnings()).toBe(true);
40 | done();
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/circular-plugin.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 | import { ESLint } from 'eslint';
3 |
4 | (ESLint && parseFloat(ESLint.version) < 9 ? describe.skip : describe)(
5 | 'circular plugin',
6 | () => {
7 | it('should support plugins with circular configs', async () => {
8 | const plugin = {
9 | configs: {},
10 | rules: {},
11 | processors: {},
12 | };
13 |
14 | Object.assign(plugin.configs, {
15 | recommended: {
16 | plugins: {
17 | self: plugin,
18 | },
19 | rules: {},
20 | },
21 | });
22 |
23 | const loaderOptions = {
24 | configType: 'flat',
25 | overrideConfig: {
26 | plugins: { plugin: plugin },
27 | },
28 | overrideConfigFile: true,
29 | };
30 |
31 | const compiler = pack('good', loaderOptions);
32 |
33 | const stats = await compiler.runAsync();
34 | expect(stats.hasWarnings()).toBe(false);
35 | expect(stats.hasErrors()).toBe(false);
36 | });
37 | },
38 | );
39 |
--------------------------------------------------------------------------------
/test/context.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('context', () => {
6 | it('absolute', async () => {
7 | const compiler = pack('good', { context: join(__dirname, 'fixtures') });
8 |
9 | const stats = await compiler.runAsync();
10 | expect(stats.hasWarnings()).toBe(false);
11 | expect(stats.hasErrors()).toBe(false);
12 | });
13 |
14 | it('relative', async () => {
15 | const compiler = pack('good', { context: '../fixtures/' });
16 |
17 | const stats = await compiler.runAsync();
18 | expect(stats.hasWarnings()).toBe(false);
19 | expect(stats.hasErrors()).toBe(false);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/emit-error.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('emit error', () => {
4 | it('should not emit errors if emitError is false', async () => {
5 | const compiler = pack('error', { emitError: false });
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasErrors()).toBe(false);
9 | });
10 |
11 | it('should emit errors if emitError is undefined', async () => {
12 | const compiler = pack('error', {});
13 |
14 | const stats = await compiler.runAsync();
15 | expect(stats.hasErrors()).toBe(true);
16 | });
17 |
18 | it('should emit errors if emitError is true', async () => {
19 | const compiler = pack('error', { emitError: true });
20 |
21 | const stats = await compiler.runAsync();
22 | expect(stats.hasErrors()).toBe(true);
23 | });
24 |
25 | it('should emit errors, but not warnings if emitError is true and emitWarning is false', async () => {
26 | const compiler = pack('full-of-problems', {
27 | emitError: true,
28 | emitWarning: false,
29 | });
30 |
31 | const stats = await compiler.runAsync();
32 | expect(stats.hasWarnings()).toBe(false);
33 | expect(stats.hasErrors()).toBe(true);
34 | });
35 |
36 | it('should emit errors and warnings if emitError is true and emitWarning is undefined', async () => {
37 | const compiler = pack('full-of-problems', { emitError: true });
38 |
39 | const stats = await compiler.runAsync();
40 | expect(stats.hasWarnings()).toBe(true);
41 | expect(stats.hasErrors()).toBe(true);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/emit-warning.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('emit warning', () => {
4 | it('should not emit warnings if emitWarning is false', async () => {
5 | const compiler = pack('warn', { emitWarning: false });
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | });
10 |
11 | it('should emit warnings if emitWarning is undefined', async () => {
12 | const compiler = pack('warn', {});
13 |
14 | const stats = await compiler.runAsync();
15 | expect(stats.hasWarnings()).toBe(true);
16 | });
17 |
18 | it('should emit warnings if emitWarning is true', async () => {
19 | const compiler = pack('warn', { emitWarning: true });
20 |
21 | const stats = await compiler.runAsync();
22 | expect(stats.hasWarnings()).toBe(true);
23 | });
24 |
25 | it('should emit warnings, but not warnings if emitWarning is true and emitError is false', async () => {
26 | const compiler = pack('full-of-problems', {
27 | emitWarning: true,
28 | emitError: false,
29 | });
30 |
31 | const stats = await compiler.runAsync();
32 | expect(stats.hasWarnings()).toBe(true);
33 | expect(stats.hasErrors()).toBe(false);
34 | });
35 |
36 | it('should emit warnings and errors if emitWarning is true and emitError is undefined', async () => {
37 | const compiler = pack('full-of-problems', { emitWarning: true });
38 |
39 | const stats = await compiler.runAsync();
40 | expect(stats.hasWarnings()).toBe(true);
41 | expect(stats.hasErrors()).toBe(true);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/empty.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import webpack from 'webpack';
4 |
5 | import ESLintPlugin from '../src';
6 |
7 | describe('empty', () => {
8 | it('no error when no files matching', (done) => {
9 | const compiler = webpack({
10 | context: join(__dirname, 'fixtures', 'empty'),
11 | mode: 'development',
12 | entry: '../',
13 | plugins: [new ESLintPlugin({ configType: 'eslintrc' })],
14 | });
15 |
16 | compiler.run((err, stats) => {
17 | expect(stats.hasWarnings()).toBe(false);
18 | expect(stats.hasErrors()).toBe(false);
19 | done();
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/error.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('error', () => {
4 | afterEach(() => {
5 | jest.restoreAllMocks();
6 | });
7 |
8 | it('should return error if file is bad', async () => {
9 | const compiler = pack('error');
10 |
11 | const stats = await compiler.runAsync();
12 | expect(stats.hasWarnings()).toBe(false);
13 | expect(stats.hasErrors()).toBe(true);
14 | });
15 |
16 | it('should propagate eslint exceptions as errors', async () => {
17 | jest.mock('eslint', () => {
18 | return {
19 | ESLint: function ESLint() {
20 | this.lintFiles = async () => Promise.reject(new Error('Oh no!'));
21 | },
22 | };
23 | });
24 |
25 | const compiler = pack('good', { threads: false });
26 |
27 | const stats = await compiler.runAsync();
28 | expect(stats.hasWarnings()).toBe(false);
29 | expect(stats.hasErrors()).toBe(true);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/eslint-lint.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('eslint lint', () => {
4 | const mockLintFiles = jest.fn().mockReturnValue([]);
5 |
6 | beforeAll(() => {
7 | jest.mock('eslint', () => {
8 | return {
9 | ESLint: function ESLint() {
10 | this.lintFiles = mockLintFiles;
11 | },
12 | };
13 | });
14 | });
15 |
16 | beforeEach(() => {
17 | mockLintFiles.mockClear();
18 | });
19 |
20 | it('should lint one file', async () => {
21 | const compiler = pack('lint-one', { threads: false });
22 |
23 | await compiler.runAsync();
24 | expect(mockLintFiles).toHaveBeenCalledTimes(1);
25 | });
26 |
27 | it('should lint two files', async () => {
28 | const compiler = pack('lint-two', { threads: false });
29 |
30 | await compiler.runAsync();
31 | const files = [
32 | expect.stringMatching('lint-two-entry.js'),
33 | expect.stringMatching('lint.js'),
34 | ];
35 | expect(mockLintFiles).toHaveBeenCalledWith(files);
36 | });
37 |
38 | it('should lint more files', async () => {
39 | const compiler = pack('lint-more', { threads: false });
40 |
41 | await compiler.runAsync();
42 | const files = [
43 | expect.stringMatching('lint-more-entry.js'),
44 | expect.stringMatching('lint-more.js'),
45 | expect.stringMatching('lint.js'),
46 | ];
47 | expect(mockLintFiles).toHaveBeenCalledWith(files);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/eslint-options.test.js:
--------------------------------------------------------------------------------
1 | import { getESLintOptions } from '../src/options';
2 |
3 | describe('eslint options', () => {
4 | it('should filter loader options', () => {
5 | const options = {
6 | formatter: 'table',
7 | ignore: false,
8 | };
9 | expect(getESLintOptions(options)).toStrictEqual({
10 | ignore: false,
11 | });
12 | });
13 |
14 | it('should keep the fix option', () => {
15 | // The fix option should be kept because it is common to both the loader and ESLint.
16 | const options = {
17 | eslintPath: 'some/place/where/eslint/lives',
18 | formatter: 'table',
19 | fix: true,
20 | emitError: false,
21 | emitWarning: false,
22 | failOnError: true,
23 | failOnWarning: true,
24 | quiet: false,
25 | outputReport: true,
26 | };
27 | expect(getESLintOptions(options)).toStrictEqual({
28 | fix: true,
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/eslint-path.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('eslint path', () => {
6 | it('should use another instance of eslint via eslintPath config', async () => {
7 | const eslintPath = join(__dirname, 'mock/eslint');
8 | const compiler = pack('good', { eslintPath });
9 |
10 | const stats = await compiler.runAsync();
11 | expect(stats.hasWarnings()).toBe(false);
12 | expect(stats.hasErrors()).toBe(true);
13 | expect(stats.compilation.errors[0].message).toContain('Fake error');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/test/eslintignore.test.js:
--------------------------------------------------------------------------------
1 | import ESLintError from '../src/ESLintError';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('eslintignore', () => {
6 | it('should ignores files present in .eslintignore', async () => {
7 | const compiler = pack('ignore', { ignore: true });
8 |
9 | const stats = await compiler.runAsync();
10 | expect(stats.hasWarnings()).toBe(false);
11 | expect(
12 | stats.compilation.errors.filter((x) => x instanceof ESLintError),
13 | ).toEqual([]);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/test/exclude.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('exclude', () => {
4 | it('should exclude with globs', async () => {
5 | const compiler = pack('exclude', { exclude: ['*error*'] });
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | expect(stats.hasErrors()).toBe(false);
10 | });
11 |
12 | it('should exclude files', async () => {
13 | const compiler = pack('exclude', { exclude: ['error.js'] });
14 |
15 | const stats = await compiler.runAsync();
16 | expect(stats.hasWarnings()).toBe(false);
17 | expect(stats.hasErrors()).toBe(false);
18 | });
19 |
20 | it('should exclude folders', async () => {
21 | const compiler = pack('exclude-folder', { exclude: ['folder'] });
22 |
23 | const stats = await compiler.runAsync();
24 | expect(stats.hasWarnings()).toBe(false);
25 | expect(stats.hasErrors()).toBe(false);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/fail-on-config.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('fail on config', () => {
6 | it('fails when .eslintrc is not a proper format', async () => {
7 | const overrideConfigFile = join(__dirname, '.badeslintrc');
8 | const compiler = pack('error', { overrideConfigFile });
9 |
10 | const stats = await compiler.runAsync();
11 | const { errors } = stats.compilation;
12 | expect(stats.hasWarnings()).toBe(false);
13 | expect(stats.hasErrors()).toBe(true);
14 | expect(errors).toHaveLength(1);
15 | expect(errors[0].message).toMatch(
16 | /ESLint configuration in --config is invalid/i,
17 | );
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/fail-on-error.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('fail on error', () => {
4 | it('should emits errors', async () => {
5 | const compiler = pack('error', { failOnError: true });
6 |
7 | await expect(compiler.runAsync()).rejects.toThrow('error');
8 | });
9 |
10 | it('should emit warnings when disabled', async () => {
11 | const compiler = pack('error', { failOnError: false });
12 |
13 | const stats = await compiler.runAsync();
14 | expect(stats.hasErrors()).toBe(true);
15 | });
16 |
17 | it('should correctly identifies a success', async () => {
18 | const compiler = pack('good', { failOnError: true });
19 |
20 | const stats = await compiler.runAsync();
21 | expect(stats.hasErrors()).toBe(false);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/fail-on-warning.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('fail on warning', () => {
4 | it('should emits errors', async () => {
5 | const compiler = pack('warn', { failOnWarning: true });
6 |
7 | await expect(compiler.runAsync()).rejects.toThrow('warning');
8 | });
9 |
10 | it('should correctly identifies a success', async () => {
11 | const compiler = pack('good', { failOnWarning: true });
12 |
13 | const stats = await compiler.runAsync();
14 | expect(stats.hasErrors()).toBe(false);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/fixtures/[symbols]/error.js:
--------------------------------------------------------------------------------
1 | var foo = stuff
2 |
--------------------------------------------------------------------------------
/test/fixtures/[symbols]/symbols-entry.js:
--------------------------------------------------------------------------------
1 | require('./error');
2 |
--------------------------------------------------------------------------------
/test/fixtures/cache.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | require("./require")
4 |
5 | function cacheIt() {
6 | return "cache"
7 | }
8 |
9 | cacheIt()
10 |
--------------------------------------------------------------------------------
/test/fixtures/child-entry.js:
--------------------------------------------------------------------------------
1 | console.log("Hello from child-entry.js");
2 |
--------------------------------------------------------------------------------
/test/fixtures/error-entry.js:
--------------------------------------------------------------------------------
1 | require('./error');
2 |
--------------------------------------------------------------------------------
/test/fixtures/error-warn-entry.js:
--------------------------------------------------------------------------------
1 | require('./error');
2 | require('./warn');
3 |
--------------------------------------------------------------------------------
/test/fixtures/error.js:
--------------------------------------------------------------------------------
1 | var foo = stuff
2 |
--------------------------------------------------------------------------------
/test/fixtures/exclude-entry.js:
--------------------------------------------------------------------------------
1 | require("./error")
2 |
--------------------------------------------------------------------------------
/test/fixtures/exclude-folder-entry.js:
--------------------------------------------------------------------------------
1 | require("./folder/error")
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixable-clone-entry.js:
--------------------------------------------------------------------------------
1 | require('./fixable-clone');
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixable.js:
--------------------------------------------------------------------------------
1 | function foo() {
2 | return true
3 | }
4 |
5 | foo()
6 |
--------------------------------------------------------------------------------
/test/fixtures/flat-config.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = [
3 | {
4 | files: ["*.js"],
5 | rules: {}
6 | }
7 | ];
8 |
--------------------------------------------------------------------------------
/test/fixtures/folder/error.js:
--------------------------------------------------------------------------------
1 | var foo = stuff
2 |
--------------------------------------------------------------------------------
/test/fixtures/folder/readme.md:
--------------------------------------------------------------------------------
1 | This folder is used as a fixture for dirty file watching.
2 |
--------------------------------------------------------------------------------
/test/fixtures/full-of-problems-entry.js:
--------------------------------------------------------------------------------
1 | require('./full-of-problems');
--------------------------------------------------------------------------------
/test/fixtures/full-of-problems.js:
--------------------------------------------------------------------------------
1 | /* eslint consistent-return: "error" */
2 | /* eslint indent: ["warn", 4] */
3 | /* eslint no-else-return: "warn" */
4 | /* eslint semi: ["warn", "always"] */
5 | /* eslint space-unary-ops: "error" */
6 |
7 | function addOne(i) {
8 | if (i != NaN) {
9 | return i ++
10 | } else {
11 | return
12 | }
13 | };
--------------------------------------------------------------------------------
/test/fixtures/good-entry.js:
--------------------------------------------------------------------------------
1 | require('./good');
2 |
--------------------------------------------------------------------------------
/test/fixtures/good.js:
--------------------------------------------------------------------------------
1 | function test() {
2 | return "value"
3 | }
4 |
5 | test()
6 |
--------------------------------------------------------------------------------
/test/fixtures/ignore-entry.js:
--------------------------------------------------------------------------------
1 | require('./ignore');
2 |
--------------------------------------------------------------------------------
/test/fixtures/ignore.js:
--------------------------------------------------------------------------------
1 | // this file should be totally ignore since it's present in .eslintignore
2 | var -dasdas;
3 |
--------------------------------------------------------------------------------
/test/fixtures/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {}
2 |
--------------------------------------------------------------------------------
/test/fixtures/lint-more-entry.js:
--------------------------------------------------------------------------------
1 | require('./lint-more')
2 |
--------------------------------------------------------------------------------
/test/fixtures/lint-more.js:
--------------------------------------------------------------------------------
1 | require('./lint')
2 |
--------------------------------------------------------------------------------
/test/fixtures/lint-one-entry.js:
--------------------------------------------------------------------------------
1 | //
2 |
--------------------------------------------------------------------------------
/test/fixtures/lint-two-entry.js:
--------------------------------------------------------------------------------
1 | require('./lint')
2 | require('./lint?foo=bar')
3 |
--------------------------------------------------------------------------------
/test/fixtures/lint.js:
--------------------------------------------------------------------------------
1 | function test() {
2 | return "value"
3 | }
4 |
5 | test()
6 |
--------------------------------------------------------------------------------
/test/fixtures/media/some-video.ts:
--------------------------------------------------------------------------------
1 | G@ B�% � �� ��HFFmpeg Service01w|C������������������������������������������������������������������������������������������������������������������������������������������������G@ �
2 | � � *�������������������������������������������������������������������������������������������������������������������������������������������������������������������������GP � � � � � � ��
3 | eng b_��������������������������������������������������������������������������������������������������������������������������������������������������������GA 0P {~ � ��
4 | 1 � �a �- ��� � @ �� �'�
5 |
--------------------------------------------------------------------------------
/test/fixtures/multiple-entry.js:
--------------------------------------------------------------------------------
1 | require('./good');
2 | require('./error');
3 |
--------------------------------------------------------------------------------
/test/fixtures/nonfixable-clone-entry.js:
--------------------------------------------------------------------------------
1 | require('./nonfixable-clone');
2 |
--------------------------------------------------------------------------------
/test/fixtures/nonfixable.js:
--------------------------------------------------------------------------------
1 | function foo() {
2 | return stuff;
3 | }
4 |
5 | foo();
6 |
--------------------------------------------------------------------------------
/test/fixtures/query-entry.js:
--------------------------------------------------------------------------------
1 | // disable: eslint-disable-next-line import/no-unresolved
2 | require('./good?some-query=1234');
3 | // disable: eslint-disable-next-line import/no-unresolved
4 | require('alias-ignore');
5 |
--------------------------------------------------------------------------------
/test/fixtures/require.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | require("./good")
--------------------------------------------------------------------------------
/test/fixtures/resource-query-entry.js:
--------------------------------------------------------------------------------
1 | // disable: eslint-disable-next-line import/no-unresolved
2 | require('./media/some-video.ts?media');
3 |
--------------------------------------------------------------------------------
/test/fixtures/warn-entry.js:
--------------------------------------------------------------------------------
1 | require('./warn');
2 |
--------------------------------------------------------------------------------
/test/fixtures/warn-error-entry.js:
--------------------------------------------------------------------------------
1 | require('./warn');
2 | require('./error');
3 |
--------------------------------------------------------------------------------
/test/fixtures/warn.js:
--------------------------------------------------------------------------------
1 | /* eslint no-unused-vars: warn */
2 | const foo = this
3 |
--------------------------------------------------------------------------------
/test/flat-config.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import pack from './utils/pack';
4 | import { ESLint } from 'eslint';
5 |
6 | (ESLint && parseFloat(ESLint.version) >= 9 ? describe.skip : describe)(
7 | 'succeed on flat-configuration',
8 | () => {
9 | it('cannot load FlatESLint class on default ESLint module', async () => {
10 | const overrideConfigFile = join(__dirname, 'fixtures', 'flat-config.js');
11 | const compiler = pack('full-of-problems', {
12 | configType: 'flat',
13 | overrideConfigFile,
14 | threads: 1,
15 | });
16 |
17 | const stats = await compiler.runAsync();
18 | const { errors } = stats.compilation;
19 |
20 | expect(stats.hasErrors()).toBe(true);
21 | expect(errors).toHaveLength(1);
22 | expect(errors[0].message).toMatch(
23 | /Couldn't find FlatESLint, you might need to set eslintPath to 'eslint\/use-at-your-own-risk'/i,
24 | );
25 | });
26 |
27 | (process.version.match(/^v(\d+\.\d+)/)[1] >= 20 ? it : it.skip)(
28 | 'finds errors on files',
29 | async () => {
30 | const overrideConfigFile = join(
31 | __dirname,
32 | 'fixtures',
33 | 'flat-config.js',
34 | );
35 | const compiler = pack('full-of-problems', {
36 | configType: 'flat',
37 | // needed for now
38 | eslintPath: 'eslint/use-at-your-own-risk',
39 | overrideConfigFile,
40 | threads: 1,
41 | });
42 |
43 | const stats = await compiler.runAsync();
44 | const { errors } = stats.compilation;
45 |
46 | expect(stats.hasErrors()).toBe(true);
47 | expect(errors).toHaveLength(1);
48 | expect(errors[0].message).toMatch(/full-of-problems\.js/i);
49 | expect(stats.hasWarnings()).toBe(true);
50 | },
51 | );
52 | },
53 | );
54 |
--------------------------------------------------------------------------------
/test/formatter-custom.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('formatter eslint', () => {
4 | it('should use custom formatter as function', async () => {
5 | const formatter = require('./mock/formatter');
6 | const compiler = pack('error', { formatter });
7 |
8 | const stats = await compiler.runAsync();
9 | expect(stats.hasWarnings()).toBe(false);
10 | expect(stats.hasErrors()).toBe(true);
11 | expect(stats.compilation.errors[0].message).toBeTruthy();
12 | const message = JSON.parse(
13 | stats.compilation.errors[0].message.replace('[eslint] ', ''),
14 | );
15 | expect(message.formatter).toEqual('mock');
16 | expect(message.results).toBeTruthy();
17 | });
18 |
19 | it('should use custom formatter as string', async () => {
20 | const formatter = './test/mock/formatter';
21 | const compiler = pack('error', { formatter });
22 |
23 | const stats = await compiler.runAsync();
24 | expect(stats.hasWarnings()).toBe(false);
25 | expect(stats.hasErrors()).toBe(true);
26 | expect(stats.compilation.errors[0].message).toBeTruthy();
27 | const message = JSON.parse(
28 | stats.compilation.errors[0].message.replace('[eslint] ', ''),
29 | );
30 | expect(message.formatter).toEqual('mock');
31 | expect(message.results).toBeTruthy();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/formatter-eslint.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('formatter eslint', () => {
4 | it('should use eslint formatter', async () => {
5 | const compiler = pack('error');
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | expect(stats.hasErrors()).toBe(true);
10 | expect(stats.compilation.errors[0].message).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/formatter-official.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('formatter official', () => {
4 | it('should use official formatter', async () => {
5 | const compiler = pack('error', { formatter: 'json' });
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | expect(stats.hasErrors()).toBe(true);
10 | expect(stats.compilation.errors[0].message).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/formatter-write.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import { readFileSync, removeSync } from 'fs-extra';
4 | import webpack from 'webpack';
5 |
6 | import conf from './utils/conf';
7 |
8 | describe('formatter write', () => {
9 | it('should write results to relative file with a custom formatter', (done) => {
10 | const outputFilename = 'outputReport-relative.txt';
11 | const config = conf('error', {
12 | formatter: 'json',
13 | outputReport: {
14 | formatter: 'json',
15 | filePath: outputFilename,
16 | },
17 | });
18 |
19 | const outputFilepath = join(config.output.path, outputFilename);
20 | removeSync(outputFilepath);
21 |
22 | const compiler = webpack(config);
23 | compiler.run((err, stats) => {
24 | const contents = readFileSync(outputFilepath, 'utf8');
25 |
26 | expect(err).toBeNull();
27 | expect(stats.hasWarnings()).toBe(false);
28 | expect(stats.hasErrors()).toBe(true);
29 | expect(stats.compilation.errors[0].message).toBe(`[eslint] ${contents}`);
30 | done();
31 | });
32 | });
33 |
34 | it('should write results to absolute file with a same formatter', (done) => {
35 | const outputFilename = 'outputReport-absolute.txt';
36 | const outputFilepath = join(__dirname, 'output', outputFilename);
37 | const config = conf('error', {
38 | outputReport: {
39 | filePath: outputFilepath,
40 | },
41 | });
42 |
43 | removeSync(outputFilepath);
44 |
45 | const compiler = webpack(config);
46 | compiler.run((err, stats) => {
47 | const contents = readFileSync(outputFilepath, 'utf8');
48 |
49 | expect(err).toBeNull();
50 | expect(stats.hasWarnings()).toBe(false);
51 | expect(stats.hasErrors()).toBe(true);
52 | expect(stats.compilation.errors[0].message).toBe(`[eslint] ${contents}`);
53 | done();
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/lint-dirty-modules-only.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 | import { writeFileSync } from 'fs';
3 |
4 | import { removeSync } from 'fs-extra';
5 |
6 | import pack from './utils/pack';
7 |
8 | const target = join(__dirname, 'fixtures', 'lint-dirty-modules-only-entry.js');
9 |
10 | describe('lint dirty modules only', () => {
11 | let watch;
12 | afterEach(() => {
13 | if (watch) {
14 | watch.close();
15 | }
16 | removeSync(target);
17 | });
18 |
19 | it('skips linting on initial run', (done) => {
20 | writeFileSync(target, 'const foo = false\n');
21 |
22 | let next = firstPass;
23 | const compiler = pack('lint-dirty-modules-only', {
24 | lintDirtyModulesOnly: true,
25 | });
26 | watch = compiler.watch({}, (err, stats) => next(err, stats));
27 |
28 | function firstPass(err, stats) {
29 | expect(err).toBeNull();
30 | expect(stats.hasWarnings()).toBe(false);
31 | expect(stats.hasErrors()).toBe(false);
32 |
33 | next = secondPass;
34 |
35 | writeFileSync(target, 'const bar = false;\n');
36 | }
37 |
38 | function secondPass(err, stats) {
39 | expect(err).toBeNull();
40 | expect(stats.hasWarnings()).toBe(false);
41 | expect(stats.hasErrors()).toBe(true);
42 | const { errors } = stats.compilation;
43 | expect(errors.length).toBe(1);
44 | expect(stats.compilation.errors[0].message).toEqual(
45 | expect.stringMatching('no-unused-vars'),
46 | );
47 | done();
48 | }
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/test/mock/eslint/index.js:
--------------------------------------------------------------------------------
1 | class ESLintMock {
2 | // Disabled because these are simplified mock methods.
3 | // eslint-disable-next-line class-methods-use-this
4 | async lintFiles() {
5 | return [
6 | {
7 | filePath: '',
8 | messages: [
9 | {
10 | ruleId: 'no-undef',
11 | severity: 2,
12 | message: 'Fake error',
13 | line: 1,
14 | column: 11,
15 | },
16 | ],
17 | errorCount: 2,
18 | warningCount: 0,
19 | fixableErrorCount: 0,
20 | fixableWarningCount: 0,
21 | source: '',
22 | },
23 | ];
24 | }
25 | // eslint-disable-next-line class-methods-use-this
26 | async loadFormatter() {
27 | return {
28 | format(results) {
29 | return JSON.stringify(results);
30 | },
31 | };
32 | }
33 | }
34 |
35 | module.exports = {
36 | ESLint: ESLintMock,
37 | };
38 |
--------------------------------------------------------------------------------
/test/mock/eslint/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint",
3 | "version": "7.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/test/mock/formatter/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function format(results) {
2 | return JSON.stringify({
3 | formatter: 'mock',
4 | results,
5 | });
6 | };
7 |
--------------------------------------------------------------------------------
/test/multiple-instances.test.js:
--------------------------------------------------------------------------------
1 | import ESLintPlugin from '../src';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('multiple instances', () => {
6 | it("should don't fail", async () => {
7 | const compiler = pack(
8 | 'multiple',
9 | {},
10 | {
11 | plugins: [
12 | new ESLintPlugin({
13 | configType: 'eslintrc',
14 | ignore: false,
15 | exclude: 'error.js',
16 | }),
17 | new ESLintPlugin({
18 | configType: 'eslintrc',
19 | ignore: false,
20 | exclude: 'error.js',
21 | }),
22 | ],
23 | },
24 | );
25 |
26 | const stats = await compiler.runAsync();
27 | expect(stats.hasWarnings()).toBe(false);
28 | expect(stats.hasErrors()).toBe(false);
29 | });
30 |
31 | it('should fail on first instance', async () => {
32 | const compiler = pack(
33 | 'multiple',
34 | {},
35 | {
36 | plugins: [
37 | new ESLintPlugin({
38 | configType: 'eslintrc',
39 | ignore: false,
40 | exclude: 'good.js',
41 | }),
42 | new ESLintPlugin({
43 | configType: 'eslintrc',
44 | ignore: false,
45 | exclude: 'error.js',
46 | }),
47 | ],
48 | },
49 | );
50 |
51 | await expect(compiler.runAsync()).rejects.toThrow();
52 | });
53 |
54 | it('should fail on second instance', async () => {
55 | const compiler = pack(
56 | 'multiple',
57 | {},
58 | {
59 | plugins: [
60 | new ESLintPlugin({
61 | configType: 'eslintrc',
62 | ignore: false,
63 | exclude: 'error.js',
64 | }),
65 | new ESLintPlugin({
66 | configType: 'eslintrc',
67 | ignore: false,
68 | exclude: 'good.js',
69 | }),
70 | ],
71 | },
72 | );
73 |
74 | await expect(compiler.runAsync()).rejects.toThrow();
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/test/ok.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('ok', () => {
4 | it("should don't throw error if file is ok", async () => {
5 | const compiler = pack('good');
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | expect(stats.hasErrors()).toBe(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/parameters.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('parameters', () => {
4 | it('should supports query strings parameters', async () => {
5 | const loaderOptions = {
6 | overrideConfig: {
7 | rules: { semi: 0 },
8 | },
9 | };
10 | const compiler = pack('good', loaderOptions);
11 |
12 | const stats = await compiler.runAsync();
13 | expect(stats.hasWarnings()).toBe(false);
14 | expect(stats.hasErrors()).toBe(false);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/query.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('query', () => {
4 | it('should correctly resolve file despite query path', async () => {
5 | const compiler = pack(
6 | 'query',
7 | {},
8 | {
9 | resolve: {
10 | alias: {
11 | 'alias-ignore': false,
12 | },
13 | },
14 | },
15 | );
16 |
17 | const stats = await compiler.runAsync();
18 | expect(stats.hasWarnings()).toBe(false);
19 | expect(stats.hasErrors()).toBe(false);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/quiet.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('quiet', () => {
4 | it('should not emit warnings if quiet is set', async () => {
5 | const compiler = pack('warn', { quiet: true });
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(false);
9 | expect(stats.hasErrors()).toBe(false);
10 | });
11 |
12 | it('should emit errors, but not emit warnings if quiet is set', async () => {
13 | const compiler = pack('full-of-problems', { quiet: true });
14 |
15 | const stats = await compiler.runAsync();
16 | expect(stats.hasWarnings()).toBe(false);
17 | expect(stats.hasErrors()).toBe(true);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/resource-query.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('resource-query', () => {
4 | it('should exclude the match resource query', async () => {
5 | const compiler = pack(
6 | 'resource-query',
7 | {
8 | resourceQueryExclude: /media/,
9 | extensions: ['.js', '.ts'],
10 | },
11 | {
12 | module: { rules: [{ resourceQuery: /media/, type: 'asset/source' }] },
13 | },
14 | );
15 |
16 | const stats = await compiler.runAsync();
17 | expect(stats.hasWarnings()).toBe(false);
18 | expect(stats.hasErrors()).toBe(false);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/symbols.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import pack from './utils/pack';
4 |
5 | describe('symbols', () => {
6 | afterEach(() => {
7 | jest.restoreAllMocks();
8 | });
9 |
10 | it('should return error', async () => {
11 | const compiler = pack(
12 | 'symbols',
13 | {},
14 | { context: join(__dirname, 'fixtures/[symbols]') },
15 | );
16 |
17 | const stats = await compiler.runAsync();
18 | expect(stats.hasWarnings()).toBe(false);
19 | expect(stats.hasErrors()).toBe(true);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/threads.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jest */
2 | import { join } from 'path';
3 |
4 | import { loadESLint, loadESLintThreaded } from '../src/getESLint';
5 |
6 | describe('Threading', () => {
7 | test('Threaded interface should look like non-threaded interface', async () => {
8 | const single = await loadESLint({});
9 | const threaded = await loadESLintThreaded('foo', 1, {});
10 | for (const key of Object.keys(single)) {
11 | expect(typeof single[key]).toEqual(typeof threaded[key]);
12 | }
13 |
14 | expect(single.Eslint).toBe(threaded.Eslint);
15 | expect(single.eslint).not.toBe(threaded.eslint);
16 | expect(single.lintFiles).not.toBe(threaded.lintFiles);
17 | expect(single.cleanup).not.toBe(threaded.cleanup);
18 |
19 | single.cleanup();
20 | threaded.cleanup();
21 | });
22 |
23 | test('Threaded should lint files', async () => {
24 | const threaded = await loadESLintThreaded('bar', 1, { ignore: false });
25 | try {
26 | const [good, bad] = await Promise.all([
27 | threaded.lintFiles(join(__dirname, 'fixtures/good.js')),
28 | threaded.lintFiles(join(__dirname, 'fixtures/error.js')),
29 | ]);
30 | expect(good[0].errorCount).toBe(0);
31 | expect(good[0].warningCount).toBe(0);
32 | expect(bad[0].errorCount).toBe(3);
33 | expect(bad[0].warningCount).toBe(0);
34 | } finally {
35 | threaded.cleanup();
36 | }
37 | });
38 |
39 | describe('worker coverage', () => {
40 | beforeEach(() => {
41 | jest.resetModules();
42 | });
43 |
44 | test('worker can start', async () => {
45 | const mockThread = { parentPort: { on: jest.fn() }, workerData: {} };
46 | const mockLintFiles = jest.fn();
47 | jest.mock('eslint', () => {
48 | return {
49 | ESLint: Object.assign(
50 | function ESLint() {
51 | this.lintFiles = mockLintFiles;
52 | },
53 | {
54 | outputFixes: jest.fn(),
55 | },
56 | ),
57 | };
58 | });
59 | jest.mock('worker_threads', () => mockThread);
60 | const { setup, lintFiles } = require('../src/worker');
61 |
62 | await setup({});
63 | await lintFiles('foo');
64 | expect(mockLintFiles).toHaveBeenCalledWith('foo');
65 | await setup({ eslintOptions: { fix: true } });
66 | await lintFiles('foo');
67 | });
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/test/utils.test.js:
--------------------------------------------------------------------------------
1 | import { parseFoldersToGlobs, parseFiles } from '../src/utils';
2 |
3 | jest.mock('fs', () => {
4 | return {
5 | statSync(pattern) {
6 | return {
7 | isDirectory() {
8 | return pattern.indexOf('/path/') === 0;
9 | },
10 | };
11 | },
12 | };
13 | });
14 |
15 | test('parseFiles should return relative files from context', () => {
16 | expect(
17 | parseFiles(
18 | ['**/*', '../package-a/src/**/', '../package-b/src/**/'],
19 | 'main/src',
20 | ),
21 | ).toEqual(
22 | expect.arrayContaining([
23 | expect.stringContaining('main/src/**/*'),
24 | expect.stringContaining('main/package-a/src/**'),
25 | expect.stringContaining('main/package-b/src/**'),
26 | ]),
27 | );
28 | });
29 |
30 | test('parseFoldersToGlobs should return globs for folders', () => {
31 | const withoutSlash = '/path/to/code';
32 | const withSlash = `${withoutSlash}/`;
33 |
34 | expect(parseFoldersToGlobs(withoutSlash, 'js')).toMatchInlineSnapshot(`
35 | [
36 | "/path/to/code/**/*.js",
37 | ]
38 | `);
39 | expect(parseFoldersToGlobs(withSlash, 'js')).toMatchInlineSnapshot(`
40 | [
41 | "/path/to/code/**/*.js",
42 | ]
43 | `);
44 |
45 | expect(
46 | parseFoldersToGlobs(
47 | [withoutSlash, withSlash, '/some/file.js'],
48 | ['js', 'cjs', 'mjs'],
49 | ),
50 | ).toMatchInlineSnapshot(`
51 | [
52 | "/path/to/code/**/*.{js,cjs,mjs}",
53 | "/path/to/code/**/*.{js,cjs,mjs}",
54 | "/some/file.js",
55 | ]
56 | `);
57 |
58 | expect(parseFoldersToGlobs(withoutSlash)).toMatchInlineSnapshot(`
59 | [
60 | "/path/to/code/**",
61 | ]
62 | `);
63 |
64 | expect(parseFoldersToGlobs(withSlash)).toMatchInlineSnapshot(`
65 | [
66 | "/path/to/code/**",
67 | ]
68 | `);
69 | });
70 |
71 | test('parseFoldersToGlobs should return unmodified globs for globs (ignoring extensions)', () => {
72 | expect(parseFoldersToGlobs('**.notjs', 'js')).toMatchInlineSnapshot(`
73 | [
74 | "**.notjs",
75 | ]
76 | `);
77 | });
78 |
--------------------------------------------------------------------------------
/test/utils/conf.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 |
3 | import ESLintPlugin from '../../src';
4 |
5 | export default (entry, pluginConf = {}, webpackConf = {}) => {
6 | const testDir = join(__dirname, '..');
7 |
8 | return {
9 | entry: `./${entry}-entry.js`,
10 | context: join(testDir, 'fixtures'),
11 | mode: 'development',
12 | output: {
13 | path: join(testDir, 'output'),
14 | },
15 | plugins: [
16 | new ESLintPlugin({
17 | configType: 'eslintrc',
18 | // this disables the use of .eslintignore, since it contains the fixtures
19 | // folder to skip it on the global linting, but here we want the opposite
20 | // (we only use .eslintignore on the test that checks this)
21 | ignore: false,
22 | // TODO: update tests to run both states: test.each([[{threads: false}], [{threads: true}]])('it should...', async ({threads}) => {...})
23 | threads: true,
24 | failOnError: false,
25 | ...pluginConf,
26 | }),
27 | ],
28 | ...webpackConf,
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/test/utils/pack.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 |
3 | import conf from './conf';
4 |
5 | /**
6 | * new a test webpack compiler
7 | * @param {String} context
8 | * @param {import('../../src/options').Options} pluginConf
9 | * @param {webpack.Configuration} webpackConf
10 | * @returns {ReturnType}
11 | */
12 | export default (context, pluginConf = {}, webpackConf = {}) => {
13 | const compiler = webpack(conf(context, pluginConf, webpackConf));
14 |
15 | return {
16 | runAsync() {
17 | return new Promise((resolve, reject) => {
18 | compiler.run((err, stats) => {
19 | if (err) {
20 | reject(err);
21 | } else {
22 | resolve(stats);
23 | }
24 | });
25 | });
26 | },
27 | watch(options, fn) {
28 | return compiler.watch(options, fn);
29 | },
30 | };
31 | };
32 |
--------------------------------------------------------------------------------
/test/warning.test.js:
--------------------------------------------------------------------------------
1 | import pack from './utils/pack';
2 |
3 | describe('warning', () => {
4 | it('should emit warnings', async () => {
5 | const compiler = pack('warn');
6 |
7 | const stats = await compiler.runAsync();
8 | expect(stats.hasWarnings()).toBe(true);
9 | expect(stats.hasErrors()).toBe(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/watch.test.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 | import { writeFileSync } from 'fs';
3 |
4 | import { removeSync } from 'fs-extra';
5 |
6 | import pack from './utils/pack';
7 |
8 | const target = join(__dirname, 'fixtures', 'watch-entry.js');
9 | const target2 = join(__dirname, 'fixtures', 'watch-leaf.js');
10 | const targetExpectedPattern = expect.stringMatching(
11 | target.replace(/\\/g, '\\\\'),
12 | );
13 |
14 | describe('watch', () => {
15 | let watch;
16 | afterEach(() => {
17 | if (watch) {
18 | watch.close();
19 | }
20 | removeSync(target);
21 | removeSync(target2);
22 | });
23 |
24 | it('should watch', (done) => {
25 | const compiler = pack('good');
26 |
27 | watch = compiler.watch({}, (err, stats) => {
28 | expect(err).toBeNull();
29 | expect(stats.hasWarnings()).toBe(false);
30 | expect(stats.hasErrors()).toBe(false);
31 | done();
32 | });
33 | });
34 |
35 | it('should watch with unique messages', (done) => {
36 | writeFileSync(target, 'var foo = stuff\n');
37 |
38 | let next = firstPass;
39 | const compiler = pack('watch');
40 | watch = compiler.watch({}, (err, stats) => next(err, stats));
41 |
42 | function firstPass(err, stats) {
43 | expect(err).toBeNull();
44 | expect(stats.hasWarnings()).toBe(false);
45 | expect(stats.hasErrors()).toBe(true);
46 | const { errors } = stats.compilation;
47 | expect(errors.length).toBe(1);
48 | const [{ message }] = errors;
49 | expect(message).toEqual(targetExpectedPattern);
50 | expect(message).toEqual(expect.stringMatching('\\(3 errors,'));
51 |
52 | next = secondPass;
53 |
54 | writeFileSync(target2, 'let bar = false;\n');
55 | writeFileSync(
56 | target,
57 | "const x = require('./watch-leaf')\n\nconst foo = false;\n",
58 | );
59 | }
60 |
61 | function secondPass(err, stats) {
62 | expect(err).toBeNull();
63 | expect(stats.hasWarnings()).toBe(false);
64 | expect(stats.hasErrors()).toBe(true);
65 | const { errors } = stats.compilation;
66 | expect(errors.length).toBe(1);
67 | const [{ message }] = errors;
68 | expect(message).toEqual(targetExpectedPattern);
69 | expect(message).toEqual(expect.stringMatching('no-unused-vars'));
70 | // `prefer-const` passes here
71 | expect(message).toEqual(expect.stringMatching('prefer-const'));
72 | expect(message).toEqual(expect.stringMatching('\\(4 errors,'));
73 |
74 | next = thirdPass;
75 |
76 | writeFileSync(
77 | target,
78 | "const x = require('./watch-leaf')\nconst foo = 0\n",
79 | );
80 | }
81 |
82 | function thirdPass(err, stats) {
83 | expect(err).toBeNull();
84 | expect(stats.hasWarnings()).toBe(false);
85 | expect(stats.hasErrors()).toBe(true);
86 | const { errors } = stats.compilation;
87 | expect(errors.length).toBe(1);
88 | const [{ message }] = errors;
89 | expect(message).toEqual(targetExpectedPattern);
90 | expect(message).toEqual(expect.stringMatching('no-unused-vars'));
91 | // `prefer-const` fails here
92 | expect(message).toEqual(expect.stringMatching('prefer-const'));
93 |
94 | next = finish;
95 |
96 | writeFileSync(
97 | target,
98 | '/* eslint-disable no-unused-vars */\nconst foo = false;\n',
99 | );
100 | }
101 |
102 | function finish(err, stats) {
103 | expect(err).toBeNull();
104 | expect(stats.hasWarnings()).toBe(false);
105 | const { errors } = stats.compilation;
106 | const [{ message }] = errors;
107 | expect(stats.hasErrors()).toBe(true);
108 | expect(message).toEqual(expect.stringMatching('prefer-const'));
109 | done();
110 | }
111 | });
112 | });
113 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "moduleResolution": "node",
5 | "allowJs": true,
6 | "checkJs": true,
7 | "strict": true,
8 | "useUnknownInCatchVariables": false,
9 | "types": ["node"],
10 | "resolveJsonModule": true
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/types/ESLintError.d.ts:
--------------------------------------------------------------------------------
1 | export = ESLintError;
2 | declare class ESLintError extends Error {
3 | /**
4 | * @param {string=} messages
5 | */
6 | constructor(messages?: string | undefined);
7 | stack: string;
8 | }
9 |
--------------------------------------------------------------------------------
/types/getESLint.d.ts:
--------------------------------------------------------------------------------
1 | export type ESLint = import('eslint').ESLint;
2 | export type LintResult = import('eslint').ESLint.LintResult;
3 | export type Options = import('./options').Options;
4 | export type AsyncTask = () => Promise;
5 | export type LintTask = (files: string | string[]) => Promise;
6 | export type Linter = {
7 | threads: number;
8 | eslint: ESLint;
9 | lintFiles: LintTask;
10 | cleanup: AsyncTask;
11 | };
12 | export type Worker = JestWorker & {
13 | lintFiles: LintTask;
14 | };
15 | /** @typedef {import('eslint').ESLint} ESLint */
16 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
17 | /** @typedef {import('./options').Options} Options */
18 | /** @typedef {() => Promise} AsyncTask */
19 | /** @typedef {(files: string|string[]) => Promise} LintTask */
20 | /** @typedef {{threads: number, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */
21 | /** @typedef {JestWorker & {lintFiles: LintTask}} Worker */
22 | /**
23 | * @param {Options} options
24 | * @returns {Promise}
25 | */
26 | export function loadESLint(options: Options): Promise;
27 | /**
28 | * @param {string|undefined} key
29 | * @param {number} poolSize
30 | * @param {Options} options
31 | * @returns {Promise}
32 | */
33 | export function loadESLintThreaded(
34 | key: string | undefined,
35 | poolSize: number,
36 | options: Options,
37 | ): Promise;
38 | /**
39 | * @param {string|undefined} key
40 | * @param {Options} options
41 | * @returns {Promise}
42 | */
43 | export function getESLint(
44 | key: string | undefined,
45 | { threads, ...options }: Options,
46 | ): Promise;
47 | import { Worker as JestWorker } from 'jest-worker';
48 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | export = ESLintWebpackPlugin;
2 | declare class ESLintWebpackPlugin {
3 | /**
4 | * @param {Options} options
5 | */
6 | constructor(options?: Options);
7 | key: string;
8 | options: import('./options').PluginOptions;
9 | /**
10 | * @param {Compiler} compiler
11 | * @param {Omit & {resourceQueryExclude: RegExp[]}} options
12 | * @param {string[]} wanted
13 | * @param {string[]} exclude
14 | */
15 | run(
16 | compiler: Compiler,
17 | options: Omit & {
18 | resourceQueryExclude: RegExp[];
19 | },
20 | wanted: string[],
21 | exclude: string[],
22 | ): Promise;
23 | /**
24 | * @param {Compiler} compiler
25 | * @returns {void}
26 | */
27 | apply(compiler: Compiler): void;
28 | /**
29 | *
30 | * @param {Compiler} compiler
31 | * @returns {string}
32 | */
33 | getContext(compiler: Compiler): string;
34 | }
35 | declare namespace ESLintWebpackPlugin {
36 | export { Compiler, Module, NormalModule, Options };
37 | }
38 | type Compiler = import('webpack').Compiler;
39 | type Module = import('webpack').Module;
40 | type NormalModule = import('webpack').NormalModule;
41 | type Options = import('./options').Options;
42 |
--------------------------------------------------------------------------------
/types/linter.d.ts:
--------------------------------------------------------------------------------
1 | export = linter;
2 | /**
3 | * @param {string|undefined} key
4 | * @param {Options} options
5 | * @param {Compilation} compilation
6 | * @returns {Promise<{lint: Linter, report: Reporter, threads: number}>}
7 | */
8 | declare function linter(
9 | key: string | undefined,
10 | options: Options,
11 | compilation: Compilation,
12 | ): Promise<{
13 | lint: Linter;
14 | report: Reporter;
15 | threads: number;
16 | }>;
17 | declare namespace linter {
18 | export {
19 | ESLint,
20 | Formatter,
21 | LintResult,
22 | Compiler,
23 | Compilation,
24 | Options,
25 | FormatterFunction,
26 | GenerateReport,
27 | Report,
28 | Reporter,
29 | Linter,
30 | LintResultMap,
31 | };
32 | }
33 | type ESLint = import('eslint').ESLint;
34 | type Formatter = import('eslint').ESLint.Formatter;
35 | type LintResult = import('eslint').ESLint.LintResult;
36 | type Compiler = import('webpack').Compiler;
37 | type Compilation = import('webpack').Compilation;
38 | type Options = import('./options').Options;
39 | type FormatterFunction = import('./options').FormatterFunction;
40 | type GenerateReport = (compilation: Compilation) => Promise;
41 | type Report = {
42 | errors?: ESLintError;
43 | warnings?: ESLintError;
44 | generateReportAsset?: GenerateReport;
45 | };
46 | type Reporter = () => Promise;
47 | type Linter = (files: string | string[]) => void;
48 | type LintResultMap = {
49 | [files: string]: LintResult;
50 | };
51 | import ESLintError = require('./ESLintError');
52 |
--------------------------------------------------------------------------------
/types/options.d.ts:
--------------------------------------------------------------------------------
1 | export type ESLintOptions = import('eslint').ESLint.Options;
2 | export type LintResult = import('eslint').ESLint.LintResult;
3 | export type FormatterFunction = (results: LintResult[]) => string;
4 | export type OutputReport = {
5 | filePath?: string | undefined;
6 | formatter?: (string | FormatterFunction) | undefined;
7 | };
8 | export type PluginOptions = {
9 | context?: string | undefined;
10 | emitError?: boolean | undefined;
11 | emitWarning?: boolean | undefined;
12 | eslintPath?: string | undefined;
13 | exclude?: (string | string[]) | undefined;
14 | extensions?: (string | string[]) | undefined;
15 | failOnError?: boolean | undefined;
16 | failOnWarning?: boolean | undefined;
17 | files?: (string | string[]) | undefined;
18 | fix?: boolean | undefined;
19 | formatter?: (string | FormatterFunction) | undefined;
20 | lintDirtyModulesOnly?: boolean | undefined;
21 | quiet?: boolean | undefined;
22 | outputReport?: OutputReport | undefined;
23 | threads?: (number | boolean) | undefined;
24 | resourceQueryExclude?: (RegExp | RegExp[]) | undefined;
25 | configType?: string | undefined;
26 | };
27 | export type Options = PluginOptions & ESLintOptions;
28 | /** @typedef {import("eslint").ESLint.Options} ESLintOptions */
29 | /** @typedef {import('eslint').ESLint.LintResult} LintResult */
30 | /**
31 | * @callback FormatterFunction
32 | * @param {LintResult[]} results
33 | * @returns {string}
34 | */
35 | /**
36 | * @typedef {Object} OutputReport
37 | * @property {string=} filePath
38 | * @property {string|FormatterFunction=} formatter
39 | */
40 | /**
41 | * @typedef {Object} PluginOptions
42 | * @property {string=} context
43 | * @property {boolean=} emitError
44 | * @property {boolean=} emitWarning
45 | * @property {string=} eslintPath
46 | * @property {string|string[]=} exclude
47 | * @property {string|string[]=} extensions
48 | * @property {boolean=} failOnError
49 | * @property {boolean=} failOnWarning
50 | * @property {string|string[]=} files
51 | * @property {boolean=} fix
52 | * @property {string|FormatterFunction=} formatter
53 | * @property {boolean=} lintDirtyModulesOnly
54 | * @property {boolean=} quiet
55 | * @property {OutputReport=} outputReport
56 | * @property {number|boolean=} threads
57 | * @property {RegExp|RegExp[]=} resourceQueryExclude
58 | * @property {string=} configType
59 | */
60 | /** @typedef {PluginOptions & ESLintOptions} Options */
61 | /**
62 | * @param {Options} pluginOptions
63 | * @returns {PluginOptions}
64 | */
65 | export function getOptions(pluginOptions: Options): PluginOptions;
66 | /**
67 | * @param {Options} loaderOptions
68 | * @returns {ESLintOptions}
69 | */
70 | export function getESLintOptions(loaderOptions: Options): ESLintOptions;
71 |
--------------------------------------------------------------------------------
/types/utils.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @template T
3 | * @param {T} value
4 | * @return {
5 | T extends (null | undefined)
6 | ? []
7 | : T extends string
8 | ? [string]
9 | : T extends readonly unknown[]
10 | ? T
11 | : T extends Iterable
12 | ? T[]
13 | : [T]
14 | }
15 | */
16 | export function arrify(
17 | value: T,
18 | ): T extends null | undefined
19 | ? []
20 | : T extends string
21 | ? [string]
22 | : T extends readonly unknown[]
23 | ? T
24 | : T extends Iterable
25 | ? T_1[]
26 | : [T];
27 | /**
28 | * @param {string|string[]} files
29 | * @param {string} context
30 | * @returns {string[]}
31 | */
32 | export function parseFiles(files: string | string[], context: string): string[];
33 | /**
34 | * @param {string|string[]} patterns
35 | * @param {string|string[]} extensions
36 | * @returns {string[]}
37 | */
38 | export function parseFoldersToGlobs(
39 | patterns: string | string[],
40 | extensions?: string | string[],
41 | ): string[];
42 | /**
43 | * @param {string} _ key, but unused
44 | * @param {any} value
45 | */
46 | export function jsonStringifyReplacerSortKeys(_: string, value: any): any;
47 |
--------------------------------------------------------------------------------
/types/worker.d.ts:
--------------------------------------------------------------------------------
1 | export type setupOptions = {
2 | /**
3 | * - import path of eslint
4 | */
5 | eslintPath?: string | undefined;
6 | configType?: string | undefined;
7 | /**
8 | * - linter options
9 | */
10 | eslintOptions: ESLintOptions;
11 | };
12 | export type ESLint = import('eslint').ESLint;
13 | export type ESLintOptions = import('eslint').ESLint.Options;
14 | export type LintResult = import('eslint').ESLint.LintResult;
15 | export type ESLintClass = {
16 | new (arg0: ESLintOptions): ESLint;
17 | outputFixes: (arg0: LintResult[]) => any;
18 | };
19 |
--------------------------------------------------------------------------------