Skip to content
Merged
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
15 changes: 13 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_style = tab
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 80

[*.{yml,yaml,json}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

[*.snap]
trim_trailing_whitespace = false
4 changes: 0 additions & 4 deletions .eslintrc.js

This file was deleted.

32 changes: 32 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Test

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [20.x, 22.x, 24.x]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
architecture: ${{ steps.calculate_architecture.outputs.result }}
cache: "npm"
- run: npm ci
- run: npm run lint
- uses: codecov/codecov-action@v5
with:
flags: integration
token: ${{ secrets.CODECOV_TOKEN }}
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ pids
*.pid
*.seed

# IDE
.idea

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

Expand All @@ -28,10 +31,10 @@ build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
# Cache directories
.npm
.eslintcache

# Optional REPL history
.node_repl_history
1 change: 1 addition & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./prettier-config.js";
63 changes: 19 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[![npm][npm]][npm-url]
[![test][test]][test-url]
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]
[![discussions](https://img.shields.io/github/discussions/webpack/webpack)](https://github.com/webpack/webpack/discussions)

<div align="center">
<!-- replace with accurate logo e.g from https://worldvectorlogo.com/ -->
Expand All @@ -14,56 +13,32 @@
<p>Provides Webpacks's .eslintrc as an extensible shared config.<p>
</div>

<h2 align="center">Install</h2>
# eslint-config-webpack

## Install

```bash
npm i -D eslint-config-webpack
```

<h2 align="center">Usage</h2>

Webpack's eslint config contains all of our ESLint rules, including ECMAScript 6+ and is similar to [Airbnb's ESLint base rules](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb-base). It requires `eslint` and `eslint-plugin-import`.
## Usage

<h2 align="center">eslint setup</h2>
Webpack's eslint config contains all of our ESLint rules.

_In your .eslintrc.js || .yml || .json add ..._
_In your eslint.config.js add ..._

```js
// Add to your .eslintrc

"extends": "webpack"
import { defineConfig } from "eslint/config";
import config from "eslint-config-webpack";

export default defineConfig([
{
extends: [config]
}
]);
```

<h2 align="center">Maintainers</h2>

<table>
<tbody>
<tr>
<td align="center">
<img width="150" height="150"
src="https://avatars2.githubusercontent.com/u/8420490?v=3&s=150">
</br>
<a href="https://github.com/d3viant0ne">Joshua Wiens</a>
</td>
<td align="center">
<img width="150" height="150"
src="https://avatars3.githubusercontent.com/u/166921?v=3&s=150">
</br>
<a href="https://github.com/bebraw">Juho Vepsäläinen</a>
</td>
</tr>
<tbody>
</table>


[npm]: https://img.shields.io/npm/v/@webpack-contrib/eslint-config-webpack.svg
[npm-url]: https://npmjs.com/package/@webpack-contrib/eslint-config-webpack

[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg
[chat-url]: https://gitter.im/webpack/webpack

[test]: http://img.shields.io/travis/webpack-contrib/eslint-config-webpack.svg
[test-url]: https://travis-ci.org/webpack-contrib/eslint-config-webpack

[cover]: https://coveralls.io/repos/github/webpack-contrib/eslint-config-webpack/badge.svg?branch=master
[cover-url]: https://coveralls.io/github/webpack-contrib/eslint-config-webpack?branch=master
[npm]: https://img.shields.io/npm/v/eslint-config-webpack.svg
[npm-url]: https://npmjs.com/package/eslint-config-webpack
[test]: https://github.com/webpack-contrib/eslint-config-webpack/actions/workflows/test.yml/badge.svg
[test-url]: https://github.com/webpack-contrib/eslint-config-webpack/actions/workflows/test.yml
208 changes: 208 additions & 0 deletions configs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import fs from "node:fs";
import path from "node:path";
// eslint-disable-next-line import/no-unresolved
import { globalIgnores } from "eslint/config";
import semver from "semver";
import configs from "./configs/index.js";
import ignorePaths from "./ignore-paths.js";

const SKIP_TIME = 5000;

class Cache {
/**
* Initialize this cache instance.
*/
constructor() {
this.map = new Map();
}

/**
* Get the cached value of the given key.
* @param {string} key The key to get.
* @returns {import('type-fest').JsonObject} The cached value or null.
*/
get(key) {
const entry = this.map.get(key);
const now = Date.now();

if (entry) {
if (entry.expire > now) {
entry.expire = now + SKIP_TIME;
return entry.value;
}
this.map.delete(key);
}
return null;
}

/**
* Set the value of the given key.
* @param {string} key The key to set.
* @param {import('type-fest').JsonObject} value The value to set.
* @returns {void}
*/
set(key, value) {
const entry = this.map.get(key);
const expire = Date.now() + SKIP_TIME;

if (entry) {
entry.value = value;
entry.expire = expire;
} else {
this.map.set(key, { value, expire });
}
}
}

const cache = new Cache();

/**
* Reads the `package.json` data in a given path.
*
* Don't cache the data.
* @param {string} dir The path to a directory to read.
* @returns {import('type-fest').JsonObject|null} The read `package.json` data, or null.
*/
function readPackageJson(dir) {
const filePath = path.join(dir, "package.json");
try {
const text = fs.readFileSync(filePath, "utf8");
const data = JSON.parse(text);

if (
data !== null &&
typeof data === "object" &&
Array.isArray(data) === false
) {
data.filePath = filePath;
return data;
}
// eslint-disable-next-line unicorn/prefer-optional-catch-binding
} catch (_err) {
// do nothing.
}

return null;
}

/**
* Gets a `package.json` data.
* The data is cached if found, then it's used after.
* @param {string=} startPath A file path to lookup.
* @returns {import('type-fest').JsonObject | null} A found `package.json` data or `null`.
* This object have additional property `filePath`.
*/
function getPackageJson(startPath = "a.js") {
const startDir = path.dirname(path.resolve(startPath));
let dir = startDir;
let prevDir = "";
let data = null;

do {
data = cache.get(dir);
if (data) {
if (dir !== startDir) {
cache.set(startDir, data);
}
return data;
}

data = readPackageJson(dir);
if (data) {
cache.set(dir, data);
cache.set(startDir, data);
return data;
}

// Go to next.
prevDir = dir;
dir = path.resolve(dir, "..");
} while (dir !== prevDir);

cache.set(startDir, null);
return null;
}

const packageJson = getPackageJson();
const isModule =
packageJson !== null &&
typeof packageJson === "object" &&
"type" in packageJson &&
packageJson.type === "module";

/**
* @returns {Record<string, string>} javascript configuration
*/
function getJavascriptConfig() {
if (packageJson.engines && packageJson.engines.node) {
const minVersion = semver.minVersion(packageJson.engines.node).major;

// https://node.green/
switch (minVersion) {
case 8:
case 9:
return configs["javascript/es2017"];
case 10:
case 11:
return configs["javascript/es2018"];
case 12:
case 13: {
const languageOptions = {
...configs["javascript/es2019"].languageOptions,
};

languageOptions.globals.Promise = false;
languageOptions.globals.BigInt = false;

return { ...configs["javascript/es2019"], languageOptions };
}
case 14:
return configs["javascript/es2020"];

case 15:
return configs["javascript/es2021"];
case 16:
case 17:
case 18:
case 19:
return configs["javascript/es2022"];
case 20:
case 21:
return configs["javascript/es2023"];
case 22:
case 23:
return configs["javascript/es2024"];
case 24:
case 25:
return configs["javascript/es2025"];
default:
return configs["javascript/recommended"];
}
}

return configs["javascript/recommended"];
}

configs.recommended = [
globalIgnores(ignorePaths),
isModule
? configs["node/mixed-module-and-commonjs"]
: configs["node/mixed-commonjs-and-module"],
getJavascriptConfig(),
configs["typescript/jsdoc"],
configs["jest/recommended"],
configs["markdown/recommended"],
configs["stylistic/recommended"],
];

configs["recommended-dirty"] = [
globalIgnores(ignorePaths),
configs["node/mixed-dirty"],
getJavascriptConfig(),
configs["typescript/jsdoc"],
configs["jest/recommended"],
configs["markdown/recommended"],
configs["stylistic/recommended"],
];

export { default } from "./configs/index.js";
13 changes: 13 additions & 0 deletions configs/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import globals from "globals";

const recommendedBrowserConfig = {
languageOptions: {
globals: {
...globals.browser,
}
},
};

export default {
"browser/recommended": recommendedBrowserConfig,
};
Loading