Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lint-on-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"diagnostics-webpack-plugin": major
---

`lintDirtyModulesOnly` is `lintOnStart`, inverted and defaulting to `true`. It says whether the first compilation lints every file it covers, and a build — which is nothing but a first compilation — now lints whatever it is set to, where `lintDirtyModulesOnly: true` used to leave a build silently unlinted.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,23 @@ type context = string;

Base directory for linting. Every relative `files` and `exclude` pattern is resolved against it.

#### `lintDirtyModulesOnly`
#### `lintOnStart`

- Type:

```ts
type lintDirtyModulesOnly = boolean;
type lintOnStart = boolean;
```

- Default: `false`
- Default: `true`

Whether the first compilation lints every file it covers. Leave it alone and a
build lints everything while a watch run lints everything once and then only
what webpack rebuilds.

Lint only changed files, skipping the initial lint on build start.
Set it to `false` to start a watch run quiet: nothing is linted until you touch
a file, and only the modules webpack rebuilds are reported. A build is nothing
but a first compilation, so it lints either way — the option cannot silence one.

### Shared options

Expand Down Expand Up @@ -310,7 +316,7 @@ Run with `{ use: "eslint" }`. It lints the files webpack builds, so only the mod

Alongside the shared options you can pass any [ESLint Node.js API option](https://eslint.org/docs/latest/integrate/nodejs-api#-new-eslintoptions) — they are handed to the `ESLint` class as they are. `concurrency` is worth knowing about: it spreads a lint across worker threads, and ESLint warns on the runs where doing so costs more than it saves, so measure your own project rather than turning it on by default.

A rebuild lints only the files webpack rebuilt and reports the rest from the previous run, so `lintDirtyModulesOnly` is only worth setting to skip the first lint entirely.
A rebuild lints only the files webpack rebuilt and reports the rest from the previous run, so [`lintOnStart`](#lintonstart) is only worth setting to start a watch run quiet.

### `configType`

Expand Down Expand Up @@ -439,7 +445,7 @@ module.exports = {

Both plugins become one, and every option they had is still here. What changed is where an option is written and how the four that decided severity are spelled.

**Where an option goes.** `context`, `lintDirtyModulesOnly` and `checks` are the plugin's own and stay at the top level. Everything else is shared: write it at the top level to cover every check, or inside a `checks` entry to cover that one. `configType`, `eslintPath`, `stylelintPath` and `threads` belong to a single check and go in its entry.
**Where an option goes.** `context`, `lintOnStart` and `checks` are the plugin's own and stay at the top level. Everything else is shared: write it at the top level to cover every check, or inside a `checks` entry to cover that one. `configType`, `eslintPath`, `stylelintPath` and `threads` belong to a single check and go in its entry.

**Severity is one option.** `emitError`, `emitWarning`, `failOnError`, `failOnWarning` and `quiet` are [`reportAs`](#reportas), because reporting a result as a webpack error is what fails the build:

Expand Down Expand Up @@ -490,7 +496,7 @@ Every option `eslint-webpack-plugin` accepted, and where it is now:
| `files` | Unchanged, shared. |
| `fix` | Unchanged, shared. |
| `formatter` | Unchanged, shared. |
| `lintDirtyModulesOnly` | Unchanged, top level. It covers every check and cannot be set per check. |
| `lintDirtyModulesOnly` | [`lintOnStart`](#lintonstart), inverted: `lintDirtyModulesOnly: true` is `lintOnStart: false`. Top level. |
| `outputReport` | Unchanged, shared. It is still written even when `reportAs` is `false`. |
| `quiet` | `reportAs: { warnings: false }`. |
| `resourceQueryExclude` | Unchanged, shared. |
Expand Down Expand Up @@ -528,7 +534,7 @@ Every option `stylelint-webpack-plugin` accepted, and where it is now:
| `failOnWarning` | [`reportAs`](#reportas), see the table above. |
| `files` | Unchanged, shared. |
| `formatter` | Unchanged, shared. |
| `lintDirtyModulesOnly` | Unchanged, top level. It covers every check and cannot be set per check. |
| `lintDirtyModulesOnly` | [`lintOnStart`](#lintonstart), inverted: `lintDirtyModulesOnly: true` is `lintOnStart: false`. Top level. |
| `outputReport` | Unchanged, shared. It is still written even when `reportAs` is `false`. |
| `quiet` | `reportAs: { warnings: false }`. |
| `stylelintPath` | Unchanged, in the `stylelint` entry. |
Expand Down
26 changes: 12 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,22 @@ class DiagnosticsWebpackPlugin {
return checks;
};

// If `lintDirtyModulesOnly` is disabled,
// execute the checks on the build
if (!this.options.lintDirtyModulesOnly) {
compiler.hooks.run.tapPromise(this.key, (compiler) =>
this.run(compiler, getChecks()),
);
}
// A build is nothing but a first compilation, so `lintOnStart` cannot
// silence one without silencing the plugin.
compiler.hooks.run.tapPromise(this.key, (compiler) =>
this.run(compiler, getChecks()),
);

let hasCompilerRunByDirtyModule = this.options.lintDirtyModulesOnly;
let skipping = !this.options.lintOnStart;

compiler.hooks.watchRun.tapPromise(this.key, (compiler) => {
if (!hasCompilerRunByDirtyModule) {
return this.run(compiler, getChecks());
}
if (skipping) {
skipping = false;

hasCompilerRunByDirtyModule = false;
return Promise.resolve();
}

return Promise.resolve();
return this.run(compiler, getChecks());
});
}

Expand Down Expand Up @@ -270,7 +268,7 @@ class DiagnosticsWebpackPlugin {
);

// A module webpack did not rebuild is reported from the last run.
if (!this.options.lintDirtyModulesOnly) {
if (this.options.lintOnStart) {
compilation.hooks.stillValidModule.tap(this.key, (module) =>
addFile(module, false),
);
Expand Down
8 changes: 4 additions & 4 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const PLUGIN_NAME = "Diagnostics Webpack Plugin";
/**
* @typedef {object} PluginOptions
* @property {string=} context a string indicating the root of your files
* @property {boolean=} lintDirtyModulesOnly lint only changed files, skip linting on start
* @property {boolean=} lintOnStart whether the first compilation lints everything
* @property {CheckEntry[]} checks the checks to run
*/

Expand All @@ -69,7 +69,7 @@ const PLUGIN_NAME = "Diagnostics Webpack Plugin";
/**
* @typedef {object} NormalizedOptions
* @property {string=} context a string indicating the root of your files
* @property {boolean=} lintDirtyModulesOnly lint only changed files, skip linting on start
* @property {boolean} lintOnStart whether the first compilation lints everything
* @property {EnabledCheck[]} checks the checks to run
*/

Expand Down Expand Up @@ -180,7 +180,7 @@ function toAdapter(use) {
function getOptions(pluginOptions) {
const {
context,
lintDirtyModulesOnly,
lintOnStart = true,
checks: entries = [],
...shared
} = pluginOptions;
Expand All @@ -195,7 +195,7 @@ function getOptions(pluginOptions) {
return { name: adapter.name, adapter, options };
});

return { context, lintDirtyModulesOnly, checks: enabled };
return { context, lintOnStart, checks: enabled };
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"description": "A string indicating the root of your files.",
"type": "string"
},
"lintDirtyModulesOnly": {
"description": "Lint only changed files, skip lint on start.",
"lintOnStart": {
"description": "Whether the first compilation lints every file it covers. `false` leaves a watch run reporting only the modules webpack rebuilds; a build has nothing but a first compilation, so it always lints.",
"type": "boolean"
},
"checks": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@ import { afterEach, describe, it } from "node:test";

import pack from "./utils/pack.js";

const target = join(
import.meta.dirname,
"fixtures",
"lint-dirty-modules-only-entry.js",
);
const target = join(import.meta.dirname, "fixtures", "lint-on-start-entry.js");

describe("lint dirty modules only", () => {
describe("lint on start", () => {
let watch;

afterEach(() => {
Expand All @@ -26,8 +22,8 @@ describe("lint dirty modules only", () => {

// eslint-disable-next-line no-use-before-define
let next = firstPass;
const compiler = pack("lint-dirty-modules-only", {
lintDirtyModulesOnly: true,
const compiler = pack("lint-on-start", {
lintOnStart: false,
});
watch = compiler.watch({}, (err, stats) => next(err, stats));

Expand All @@ -51,4 +47,15 @@ describe("lint dirty modules only", () => {
writeFileSync(target, "const bar = false;\n");
}
});

it("still lints a build, which is nothing but a start", async () => {
writeFileSync(target, "const foo = false\n");

const stats = await pack("lint-on-start", {
lintOnStart: false,
}).runAsync();

assert.strictEqual(stats.hasErrors(), true);
assert.match(stats.compilation.errors[0].message, /no-unused-vars/u);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ import { afterEach, describe, it } from "node:test";

import pack from "./utils/pack.js";

const target = join(
import.meta.dirname,
"fixtures/lint-dirty-modules-only/test.scss",
);
const target = join(import.meta.dirname, "fixtures/lint-on-start/test.scss");

describe("lint dirty modules only", () => {
describe("lint on start", () => {
let watch;

afterEach(() => {
Expand All @@ -25,8 +22,8 @@ describe("lint dirty modules only", () => {

// eslint-disable-next-line no-use-before-define
let next = firstPass;
const compiler = pack("lint-dirty-modules-only", {
lintDirtyModulesOnly: true,
const compiler = pack("lint-on-start", {
lintOnStart: false,
});
watch = compiler.watch({}, (err, stats) => next(err, stats));

Expand All @@ -51,4 +48,15 @@ describe("lint dirty modules only", () => {
writeFileSync(target, "#stuff { background: black; }\n");
}
});

it("still lints a build, which is nothing but a start", async () => {
writeFileSync(target, "#stuff { background: black; }\n");

const stats = await pack("lint-on-start", {
lintOnStart: false,
}).runAsync();

assert.strictEqual(stats.hasErrors(), true);
assert.match(stats.compilation.errors[0].message, /color-named/u);
});
});
2 changes: 1 addition & 1 deletion test/stylelint/utils/conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { join } from "node:path";
import DiagnosticsPlugin from "../../../src/index.js";

// Options the plugin only accepts next to the check entries, not inside one.
const PLUGIN_OPTIONS = ["context", "lintDirtyModulesOnly"];
const PLUGIN_OPTIONS = ["context", "lintOnStart"];

export default (context, pluginConf = {}, webpackConf = {}) => {
const testDir = join(import.meta.dirname, "..");
Expand Down
2 changes: 1 addition & 1 deletion test/utils/conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { join } from "node:path";
import DiagnosticsPlugin from "../../src/index.js";

// Options the plugin only accepts next to the check entries, not inside one.
const PLUGIN_OPTIONS = ["context", "lintDirtyModulesOnly"];
const PLUGIN_OPTIONS = ["context", "lintOnStart"];

export default (entry, pluginConf = {}, webpackConf = {}) => {
const testDir = join(import.meta.dirname, "..");
Expand Down
8 changes: 4 additions & 4 deletions types/options.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ export type PluginOptions = {
*/
context?: string | undefined;
/**
* lint only changed files, skip linting on start
* whether the first compilation lints everything
*/
lintDirtyModulesOnly?: boolean | undefined;
lintOnStart?: boolean | undefined;
/**
* the checks to run
*/
Expand All @@ -109,9 +109,9 @@ export type NormalizedOptions = {
*/
context?: string | undefined;
/**
* lint only changed files, skip linting on start
* whether the first compilation lints everything
*/
lintDirtyModulesOnly?: boolean | undefined;
lintOnStart: boolean;
/**
* the checks to run
*/
Expand Down
Loading