From a094186386dc339cd44c1fd5f52656122551b301 Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:37:04 +0530 Subject: [PATCH 1/7] Migrate to Webpack 5 --- .eslintignore | 14 -- .eslintrc.js | 293 ----------------------- docs/changelog.md | 47 +++- docs/guides/getting-started.md | 47 +++- docs/index.md | 3 +- eslint.config.mjs | 233 ++++++++++++++++++ index.js | 32 ++- package.json | 82 +++---- src/__snapshots__/externals.test.js.snap | 4 +- src/config.js | 11 +- src/helpers/clean-on-exit.js | 2 +- src/helpers/is-installed.js | 7 +- src/loaders.js | 38 ++- src/loaders.test.js | 22 +- src/plugins.js | 32 ++- src/presets.js | 61 +++-- src/presets.test.js | 64 ++--- test/src/index.js | 5 +- test/test-config.js | 6 +- 19 files changed, 511 insertions(+), 492 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js create mode 100644 eslint.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 537506f9..00000000 --- a/.eslintignore +++ /dev/null @@ -1,14 +0,0 @@ -# /node_modules/* and /bower_components/* in the project root are ignored by default - -# Ignore generated files -**/__snapshots__/ - -# Ignore documentation site -docs/ - -# Ignore third-party code -src/vendor - -# The test folder uses ES modules -test/src -test/build diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 58377177..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,293 +0,0 @@ -module.exports = { - 'env': { - 'node': true, - 'commonjs': true, - 'jest': true, - 'es6': true - }, - 'extends': 'eslint:recommended', - 'parserOptions': { - 'ecmaVersion': 2018 - }, - 'rules': { - 'accessor-pairs': 'error', - 'array-bracket-newline': 'off', - 'array-bracket-spacing': [ - 'error', - 'always' - ], - 'array-callback-return': 'error', - 'array-element-newline': 'off', - 'arrow-body-style': 'off', - 'arrow-parens': 'off', - 'arrow-spacing': [ - 'error', - { - 'after': true, - 'before': true - } - ], - 'block-scoped-var': 'error', - 'block-spacing': 'error', - 'brace-style': 'error', - 'callback-return': 'error', - 'capitalized-comments': 'off', - 'class-methods-use-this': 'error', - 'comma-dangle': 'off', - 'comma-spacing': 'off', - 'comma-style': [ - 'error', - 'last' - ], - 'complexity': 'error', - 'computed-property-spacing': [ - 'error', - 'always' - ], - // 'consistent-return': 'error', - 'consistent-this': 'error', - 'curly': 'error', - 'default-case': 'error', - 'dot-location': [ 'error', 'property' ], - 'dot-notation': 'error', - 'eol-last': 'error', - 'eqeqeq': 'error', - 'func-call-spacing': 'error', - 'func-name-matching': 'error', - 'func-names': 'error', - 'func-style': [ - 'error', - 'expression' - ], - 'function-paren-newline': 'error', - 'generator-star-spacing': 'error', - 'global-require': 'error', - 'guard-for-in': 'error', - 'handle-callback-err': 'error', - 'id-blacklist': 'error', - 'id-length': 'error', - 'id-match': 'error', - 'implicit-arrow-linebreak': [ - 'error', - 'beside' - ], - 'indent': 'off', - 'indent-legacy': 'off', - 'init-declarations': 'off', - 'jsx-quotes': 'error', - 'key-spacing': 'off', - 'keyword-spacing': 'error', - 'line-comment-position': 'error', - 'linebreak-style': [ - 'error', - 'unix' - ], - 'lines-around-comment': 'off', - 'lines-around-directive': 'error', - 'lines-between-class-members': 'error', - 'max-classes-per-file': 'error', - 'max-depth': 'error', - 'max-len': 'off', - 'max-lines': 'off', - 'max-lines-per-function': 'off', - 'max-nested-callbacks': 'error', - 'max-params': 'error', - 'max-statements-per-line': 'error', - 'multiline-comment-style': [ - 'error', - 'separate-lines' - ], - 'multiline-ternary': [ - 'error', - 'always-multiline' - ], - 'new-cap': 'error', - 'new-parens': 'error', - 'newline-after-var': 'off', - 'newline-before-return': 'off', - 'newline-per-chained-call': 'error', - 'no-alert': 'error', - 'no-array-constructor': 'error', - 'no-async-promise-executor': 'error', - 'no-await-in-loop': 'error', - 'no-bitwise': 'error', - 'no-buffer-constructor': 'error', - 'no-caller': 'error', - 'no-catch-shadow': 'error', - 'no-confusing-arrow': [ 'error', { 'allowParens': true } ], - 'no-continue': 'error', - 'no-div-regex': 'error', - 'no-duplicate-imports': 'error', - 'no-else-return': 'error', - 'no-empty-function': 'error', - 'no-eq-null': 'error', - 'no-eval': 'error', - 'no-extend-native': 'error', - 'no-extra-bind': 'error', - 'no-extra-label': 'error', - 'no-extra-parens': 'off', - 'no-floating-decimal': 'error', - 'no-implicit-coercion': 'error', - 'no-implicit-globals': 'error', - 'no-implied-eval': 'error', - 'no-inline-comments': 'error', - 'no-invalid-this': 'error', - 'no-iterator': 'error', - 'no-label-var': 'error', - 'no-labels': 'error', - 'no-lone-blocks': 'error', - 'no-lonely-if': 'error', - 'no-loop-func': 'error', - 'no-magic-numbers': 'off', - 'no-misleading-character-class': 'error', - 'no-mixed-operators': 'error', - 'no-mixed-requires': 'error', - 'no-multi-assign': 'error', - 'no-multi-spaces': 'error', - 'no-multi-str': 'error', - 'no-multiple-empty-lines': 'error', - 'no-native-reassign': 'error', - 'no-negated-condition': 'error', - 'no-negated-in-lhs': 'error', - 'no-nested-ternary': 'error', - 'no-new': 'error', - 'no-new-func': 'error', - 'no-new-object': 'error', - 'no-new-require': 'error', - 'no-new-wrappers': 'error', - 'no-octal-escape': 'error', - 'no-param-reassign': 'error', - 'no-path-concat': 'error', - 'no-plusplus': 'error', - 'no-process-exit': 'error', - 'no-proto': 'error', - 'no-prototype-builtins': 'error', - 'no-restricted-globals': 'error', - 'no-restricted-imports': 'error', - 'no-restricted-modules': 'error', - 'no-restricted-properties': 'error', - 'no-restricted-syntax': 'error', - 'no-return-assign': 'error', - 'no-return-await': 'error', - 'no-script-url': 'error', - 'no-self-compare': 'error', - 'no-sequences': 'error', - 'no-shadow-restricted-names': 'error', - 'no-spaced-func': 'error', - 'no-sync': 'error', - 'no-tabs': [ - 'error', - { - 'allowIndentationTabs': true - } - ], - 'no-template-curly-in-string': 'error', - 'no-ternary': 'off', - 'no-throw-literal': 'error', - 'no-trailing-spaces': 'error', - 'no-undef-init': 'error', - 'no-undefined': 'off', - 'no-underscore-dangle': 'error', - 'no-unmodified-loop-condition': 'error', - 'no-unneeded-ternary': 'error', - 'no-unused-expressions': 'error', - 'no-use-before-define': 'error', - 'no-useless-call': 'error', - 'no-useless-catch': 'error', - 'no-useless-computed-key': 'error', - 'no-useless-concat': 'error', - 'no-useless-constructor': 'error', - 'no-useless-rename': 'error', - 'no-useless-return': 'error', - 'no-var': 'error', - 'no-void': 'error', - 'no-warning-comments': 'warn', - 'no-whitespace-before-property': 'error', - 'no-with': 'error', - 'nonblock-statement-body-position': 'error', - 'object-curly-newline': 'error', - 'object-curly-spacing': [ - 'error', - 'always' - ], - 'object-property-newline': 'error', - 'object-shorthand': 'error', - 'one-var': 'off', - 'one-var-declaration-per-line': 'error', - 'operator-assignment': 'error', - 'operator-linebreak': [ - 'error', - 'after' - ], - 'padded-blocks': 'off', - 'padding-line-between-statements': 'error', - 'prefer-arrow-callback': 'error', - 'prefer-const': 'off', - 'prefer-destructuring': 'error', - 'prefer-numeric-literals': 'error', - 'prefer-object-spread': 'error', - 'prefer-promise-reject-errors': 'error', - 'prefer-reflect': 'error', - 'prefer-rest-params': 'error', - 'prefer-spread': 'error', - 'prefer-template': 'error', - 'quote-props': 'off', - 'quotes': [ - 'error', - 'single' - ], - 'radix': 'error', - 'require-atomic-updates': 'error', - 'require-await': 'error', - 'require-jsdoc': 'error', - 'require-unicode-regexp': 'off', - 'rest-spread-spacing': [ - 'error', - 'never' - ], - 'semi': 'error', - 'semi-spacing': 'error', - 'semi-style': [ - 'error', - 'last' - ], - 'sort-imports': 'error', - 'sort-keys': 'off', - 'sort-vars': 'error', - 'space-before-blocks': 'error', - 'space-before-function-paren': 'error', - 'space-in-parens': [ - 'error', - 'always' - ], - 'space-infix-ops': 'error', - 'spaced-comment': [ - 'error', - 'always' - ], - 'strict': [ - 'error', - 'never' - ], - 'switch-colon-spacing': 'error', - 'symbol-description': 'error', - 'template-curly-spacing': [ - 'error', - 'always' - ], - 'template-tag-spacing': 'error', - 'unicode-bom': [ - 'error', - 'never' - ], - 'valid-jsdoc': 'off', - 'vars-on-top': 'error', - 'wrap-iife': 'error', - 'wrap-regex': 'error', - 'yield-star-spacing': 'error', - 'yoda': [ - 'error', - 'never' - ] - } -}; diff --git a/docs/changelog.md b/docs/changelog.md index 5b4df9b9..50fb2171 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,9 +6,50 @@ nav_order: 10 # Changelog -## Next - -- Include the `contenthash` in generated CSS filenames. [#204](https://github.com/humanmade/webpack-helpers/pull/204) +## v1.0.0-alpha + +### Added + +- Full Webpack 5 support with modern optimizations and performance improvements +- ESLint 9+ support with flat configuration format (`eslint.config.js`) +- Modern `eslint-webpack-plugin` replacing deprecated `eslint-loader` +- Enhanced TypeScript support for `.ts` and `.tsx` files +- Updated peer dependencies to latest stable versions +- Improved build performance with Webpack 5's enhanced tree shaking +- Module federation capabilities support + +### Changed + +- **BREAKING**: Upgraded from Webpack 4 to Webpack 5 +- **BREAKING**: Updated ESLint support to use ESLint 9+ flat configuration format +- **BREAKING**: Replaced deprecated `eslint-loader` with `eslint-webpack-plugin` +- **BREAKING**: Updated minimum Node.js requirement to align with Webpack 5 +- Updated all bundled dependencies to latest stable versions +- Improved error handling and debugging capabilities +- Enhanced development server performance + +### Removed + +- Support for legacy `.eslintrc.*` configuration files (use `eslint.config.js` instead) +- Webpack 4 compatibility and related legacy code +- `eslint-loader` dependency (replaced with `eslint-webpack-plugin`) +- Outdated peer dependency constraints + +### Migration Guide + +- Update your `package.json` to use `webpack@5`, `webpack-cli@5`, and `webpack-dev-server@5` +- If using ESLint, migrate from `.eslintrc.*` files to `eslint.config.js` using the flat configuration format +- Review and update any custom webpack configurations to ensure Webpack 5 compatibility +- Update Node.js to a supported version if needed + +## v0.12.0 + +* Include contenthash in generated CSS filenames by @kadamwhite in https://github.com/humanmade/webpack-helpers/pull/204 +* Use Just the HM Docs theme by @joeleenk in https://github.com/humanmade/webpack-helpers/pull/210 +* Add workflow to deploy GH pages by @joeleenk in https://github.com/humanmade/webpack-helpers/pull/212 +* Update to Jekyll 4, inherit from theme by @joeleenk in https://github.com/humanmade/webpack-helpers/pull/213 +* Update getting-started.md by @pamprn09 in https://github.com/humanmade/webpack-helpers/pull/216 +* Update externals for WP 6.2 and add snapshot update command by @Sephsekla in https://github.com/humanmade/webpack-helpers/pull/217 ## v0.11.1 diff --git a/docs/guides/getting-started.md b/docs/guides/getting-started.md index d184e874..a273c3b2 100644 --- a/docs/guides/getting-started.md +++ b/docs/guides/getting-started.md @@ -18,17 +18,10 @@ While this package depends in turn on a number of loaders and plugins, it delibe ** *Check notice before proceeding:** ```bash -npm install --save-dev @humanmade/webpack-helpers webpack@4 webpack-cli@3 webpack-dev-server sass +npm install --save-dev @humanmade/webpack-helpers webpack@5 webpack-cli@5 webpack-dev-server@5 sass ``` -Note that we specify Webpack version 4. Support for Webpack 5 is anticipated in the v1.0 release of these helpers, but at present using Webpack 4 provides the most predictable and stable experience across our projects. - -***❗ *Notice*** -This verson is outdated and might leave you with outdated versions of this library and associated Webpack tooling. -There's a [pending release](https://github.com/humanmade/webpack-helpers/pull/205) that will fix this problem. But for now you should install humanmade/webpack-helpers@beta, webpack @5, webpack-cli@4, and webpack-dev-server@4. To do so run the following command: -```bash -npm install --save-dev @humanmade/webpack-helpers@beta webpack@5 webpack-cli@4 webpack-dev-server@4 -``` +**Webpack 5 Support:** This package now fully supports Webpack 5 with all the latest features and optimizations. We recommend using Webpack 5 for all new projects as it provides better performance, improved tree shaking, and enhanced module federation capabilities. ## Configuring Webpack @@ -54,7 +47,41 @@ By the end of this guide Webpack will take our source JavaScript files from thes **ESLint** -If [ESLint](https://eslint.org/) is installed, `eslint-loader` will be used to validate that your code compiles and passes required style rules before the bundle is generated. While ESLint will be used if present, these helpers do not assume any specific configuration or rules. If you aren't using ESLint you may install and configure it with basic syntax and style rules by following the [official getting started guide](https://eslint.org/docs/user-guide/getting-started), or by installing Human Made's [`@humanmade/eslint-config`](https://www.npmjs.com/package/@humanmade/eslint-config) preset. +If [ESLint](https://eslint.org/) is installed, the webpack plugin `eslint-webpack-plugin` will be used to validate that your code compiles and passes required style rules before the bundle is generated. This package now supports ESLint 9+ with the modern flat configuration format. + +To configure ESLint for your project, create an `eslint.config.js` file in your project root: + +```js +// eslint.config.js +import js from '@eslint/js'; + +export default [ + js.configs.recommended, + { + files: ['**/*.js', '**/*.jsx'], + languageOptions: { + ecmaVersion: 2024, + sourceType: 'module', + globals: { + // Browser globals + window: 'readonly', + document: 'readonly', + // WordPress globals + wp: 'readonly', + jQuery: 'readonly', + $: 'readonly' + } + }, + rules: { + // Add your custom rules here + 'no-console': 'warn', + 'no-unused-vars': 'warn' + } + } +]; +``` + +**Note:** ESLint 9 uses the new flat configuration format. The old `.eslintrc.*` files are no longer supported. If you need to migrate from an older ESLint configuration, refer to the [ESLint migration guide](https://eslint.org/docs/latest/use/configure/migration-guide). **Babel** diff --git a/docs/index.md b/docs/index.md index 8e39b97a..af7af5a8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -54,7 +54,8 @@ The included presets provide the following out of the box: - Automatic inlining of small image and font assets as Data URI strings. - PostCSS [Autoprefixer](https://github.com/postcss/autoprefixer#readme) support and Flexbox bug fixes. - [TypeScript](https://www.typescriptlang.org/) compilation for `.ts` and `.tsx` files, if the `typescript` npm package is installed. -- Automatic [ESLint](https://eslint.org/) linting on build, if the `eslint` npm package is installed. +- Automatic [ESLint](https://eslint.org/) linting on build using ESLint 9+ with flat configuration format, if the `eslint` npm package is installed. +- Full Webpack 5 support with modern optimizations and module federation capabilities. ### WordPress Core Externals diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..bafe1d91 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,233 @@ +import globals from 'globals'; +import js from '@eslint/js'; + +export default [ + { + ignores: [ + '**/__snapshots__/**', + 'docs/**', + 'src/vendor/**', + 'test/build/**', + 'node_modules/**', + 'bower_components/**' + ] + }, + js.configs.recommended, + { + languageOptions: { + ecmaVersion: 2024, + sourceType: 'module', + globals: { + ...globals.node, + ...globals.commonjs, + ...globals.jest, + ...globals.es6, + ...globals.browser + } + }, + rules: { + 'accessor-pairs': 'error', + 'array-bracket-spacing': [ 'error', 'always' ], + 'array-callback-return': 'error', + 'arrow-body-style': 'off', + 'arrow-parens': 'off', + 'arrow-spacing': [ + 'error', + { + 'after': true, + 'before': true + } + ], + 'block-scoped-var': 'error', + 'block-spacing': 'error', + 'brace-style': 'error', + 'callback-return': 'error', + 'capitalized-comments': 'off', + 'class-methods-use-this': 'error', + 'comma-dangle': 'off', + 'comma-spacing': 'off', + 'comma-style': [ 'error', 'last' ], + 'complexity': 'error', + 'computed-property-spacing': [ 'error', 'always' ], + 'consistent-this': 'error', + 'curly': 'error', + 'default-case': 'error', + 'dot-location': [ 'error', 'property' ], + 'dot-notation': 'error', + 'eol-last': 'error', + 'eqeqeq': 'error', + 'func-call-spacing': 'error', + 'func-name-matching': 'error', + 'func-names': 'error', + 'func-style': [ 'error', 'expression' ], + 'function-paren-newline': 'error', + 'generator-star-spacing': 'error', + 'global-require': 'error', + 'guard-for-in': 'error', + 'handle-callback-err': 'error', + 'id-length': 'error', + 'id-match': 'error', + 'implicit-arrow-linebreak': [ 'error', 'beside' ], + 'indent': 'off', + 'init-declarations': 'off', + 'jsx-quotes': 'error', + 'key-spacing': 'off', + 'keyword-spacing': 'error', + 'line-comment-position': 'error', + 'linebreak-style': [ 'error', 'unix' ], + 'lines-around-comment': 'off', + 'lines-between-class-members': 'error', + 'max-classes-per-file': 'error', + 'max-depth': 'error', + 'max-len': 'off', + 'max-lines': 'off', + 'max-lines-per-function': 'off', + 'max-nested-callbacks': 'error', + 'max-params': 'error', + 'max-statements-per-line': 'error', + 'multiline-comment-style': [ 'error', 'separate-lines' ], + 'multiline-ternary': [ 'error', 'always-multiline' ], + 'new-cap': 'error', + 'new-parens': 'error', + 'newline-after-var': 'off', + 'newline-before-return': 'off', + 'newline-per-chained-call': 'error', + 'no-alert': 'error', + 'no-array-constructor': 'error', + 'no-async-promise-executor': 'error', + 'no-await-in-loop': 'error', + 'no-bitwise': 'error', + 'no-caller': 'error', + 'no-confusing-arrow': [ 'error', { 'allowParens': true } ], + 'no-continue': 'error', + 'no-div-regex': 'error', + 'no-duplicate-imports': 'error', + 'no-else-return': 'error', + 'no-empty-function': 'error', + 'no-eq-null': 'error', + 'no-eval': 'error', + 'no-extend-native': 'error', + 'no-extra-bind': 'error', + 'no-extra-label': 'error', + 'no-extra-parens': 'off', + 'no-floating-decimal': 'error', + 'no-implicit-coercion': 'error', + 'no-implicit-globals': 'error', + 'no-implied-eval': 'error', + 'no-inline-comments': 'error', + 'no-invalid-this': 'error', + 'no-iterator': 'error', + 'no-label-var': 'error', + 'no-labels': 'error', + 'no-lone-blocks': 'error', + 'no-lonely-if': 'error', + 'no-loop-func': 'error', + 'no-magic-numbers': 'off', + 'no-misleading-character-class': 'error', + 'no-mixed-operators': 'error', + 'no-mixed-requires': 'error', + 'no-multi-assign': 'error', + 'no-multi-spaces': 'error', + 'no-multi-str': 'error', + 'no-multiple-empty-lines': 'error', + 'no-negated-condition': 'error', + 'no-nested-ternary': 'error', + 'no-new': 'error', + 'no-new-func': 'error', + 'no-new-object': 'error', + 'no-new-require': 'error', + 'no-new-wrappers': 'error', + 'no-octal-escape': 'error', + 'no-param-reassign': 'error', + 'no-path-concat': 'error', + 'no-plusplus': 'error', + 'no-process-exit': 'error', + 'no-proto': 'error', + 'no-prototype-builtins': 'error', + 'no-restricted-globals': 'error', + 'no-restricted-imports': 'error', + 'no-restricted-modules': 'error', + 'no-restricted-properties': 'error', + 'no-restricted-syntax': 'error', + 'no-return-assign': 'error', + 'no-return-await': 'error', + 'no-script-url': 'error', + 'no-self-compare': 'error', + 'no-sequences': 'error', + 'no-shadow-restricted-names': 'error', + 'no-tabs': [ 'error', { 'allowIndentationTabs': true } ], + 'no-template-curly-in-string': 'error', + 'no-ternary': 'off', + 'no-throw-literal': 'error', + 'no-trailing-spaces': 'error', + 'no-undef-init': 'error', + 'no-undefined': 'off', + 'no-underscore-dangle': 'error', + 'no-unmodified-loop-condition': 'error', + 'no-unneeded-ternary': 'error', + 'no-unused-expressions': 'error', + 'no-use-before-define': 'error', + 'no-useless-call': 'error', + 'no-useless-catch': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-concat': 'error', + 'no-useless-constructor': 'error', + 'no-useless-rename': 'error', + 'no-useless-return': 'error', + 'no-var': 'error', + 'no-void': 'error', + 'no-warning-comments': 'warn', + 'no-whitespace-before-property': 'error', + 'no-with': 'error', + 'nonblock-statement-body-position': 'error', + 'object-curly-newline': 'error', + 'object-curly-spacing': [ 'error', 'always' ], + 'object-property-newline': 'error', + 'object-shorthand': 'error', + 'one-var': 'off', + 'one-var-declaration-per-line': 'error', + 'operator-assignment': 'error', + 'operator-linebreak': [ 'error', 'after' ], + 'padded-blocks': 'off', + 'padding-line-between-statements': 'error', + 'prefer-arrow-callback': 'error', + 'prefer-const': 'off', + 'prefer-destructuring': 'error', + 'prefer-numeric-literals': 'error', + 'prefer-object-spread': 'error', + 'prefer-promise-reject-errors': 'error', + 'prefer-rest-params': 'error', + 'prefer-spread': 'error', + 'prefer-template': 'error', + 'quote-props': 'off', + 'quotes': [ 'error', 'single' ], + 'radix': 'error', + 'require-atomic-updates': 'error', + 'require-await': 'error', + 'require-unicode-regexp': 'off', + 'rest-spread-spacing': [ 'error', 'never' ], + 'semi': 'error', + 'semi-spacing': 'error', + 'semi-style': [ 'error', 'last' ], + 'sort-imports': 'error', + 'sort-keys': 'off', + 'sort-vars': 'error', + 'space-before-blocks': 'error', + 'space-before-function-paren': 'error', + 'space-in-parens': [ 'error', 'always' ], + 'space-infix-ops': 'error', + 'spaced-comment': [ 'error', 'always' ], + 'strict': [ 'error', 'never' ], + 'switch-colon-spacing': 'error', + 'symbol-description': 'error', + 'template-curly-spacing': [ 'error', 'always' ], + 'template-tag-spacing': 'error', + 'unicode-bom': [ 'error', 'never' ], + 'vars-on-top': 'error', + 'wrap-iife': 'error', + 'wrap-regex': 'error', + 'yield-star-spacing': 'error', + 'yoda': [ 'error', 'never' ] + } + } +]; diff --git a/index.js b/index.js index f8bf15cc..9f5b227e 100644 --- a/index.js +++ b/index.js @@ -1,18 +1,28 @@ /** * Expose public package API. */ +const config = require( './src/config' ); +const externals = require( './src/externals' ); +const choosePort = require( './src/helpers/choose-port' ); +const cleanOnExit = require( './src/helpers/clean-on-exit' ); +const filePath = require( './src/helpers/file-path' ); +const findInObject = require( './src/helpers/find-in-object' ); +const withDynamicPort = require( './src/helpers/with-dynamic-port' ); +const loaders = require( './src/loaders' ); +const plugins = require( './src/plugins' ); +const presets = require( './src/presets' ); + module.exports = { - /* eslint-disable global-require */ - config: require( './src/config' ), - externals: require( './src/externals' ), + config, + externals, helpers: { - choosePort: require( './src/helpers/choose-port' ), - cleanOnExit: require( './src/helpers/clean-on-exit' ), - filePath: require( './src/helpers/file-path' ), - findInObject: require( './src/helpers/find-in-object' ), - withDynamicPort: require( './src/helpers/with-dynamic-port' ), + choosePort, + cleanOnExit, + filePath, + findInObject, + withDynamicPort, }, - loaders: require( './src/loaders' ), - plugins: require( './src/plugins' ), - presets: require( './src/presets' ), + loaders, + plugins, + presets, }; diff --git a/package.json b/package.json index 7b3d5522..8001cde5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@humanmade/webpack-helpers", "public": true, - "version": "0.12.0", + "version": "1.0.0-alpha", "description": "Reusable Webpack configuration components & related helper utilities.", "main": "index.js", "author": "Human Made", @@ -22,7 +22,7 @@ "test": "jest", "update-snapshot": "jest --updateSnapshot", "test-build": "rm -rf test/build && webpack --config=test/test-config.js", - "test-dev-server": "webpack-dev-server --config=test/test-config.js" + "test-dev-server": "webpack serve --config=test/test-config.js" }, "jest": { "setupFilesAfterEnv": [ @@ -30,53 +30,53 @@ ] }, "devDependencies": { - "eslint": "^7.17.0", - "jest": "^26.6.3", - "sass": "^1.32.4", - "typescript": "^4.0.2", - "webpack": "^4.46.0", - "webpack-cli": "^3.3.12", - "webpack-dev-server": "^3.11.2" + "@eslint/js": "^9.34.0", + "eslint": "^9.34.0", + "eslint-webpack-plugin": "^5.0.2", + "globals": "^16.3.0", + "jest": "^30.1.3", + "sass": "^1.92.0", + "typescript": "^5.9.2", + "webpack": "^5.101.3", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.2" }, "dependencies": { - "@babel/core": "^7.10.3", - "@babel/plugin-proposal-class-properties": "^7.10.1", - "@wordpress/babel-preset-default": "^4.16.0", - "babel-loader": "^8.0.6", + "@babel/core": "^7.28.3", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@wordpress/babel-preset-default": "^8.30.0", + "babel-loader": "^10.0.0", "babel-plugin-transform-react-jsx": "^6.24.1", - "bell-on-bundler-error-plugin": "^2.0.0", - "block-editor-hmr": "^0.6.1", - "chalk": "^4.1.0", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^6.0.2", - "css-loader": "^5.0.1", + "bell-on-bundler-error-plugin": "^3.0.0", + "block-editor-hmr": "^0.7.0", + "chalk": "^5.6.0", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^13.0.1", + "css-loader": "^7.1.2", + "css-minimizer-webpack-plugin": "^7.0.2", "detect-port-alt": "^1.1.6", - "eslint-loader": "^4.0.2", - "file-loader": "^6.0.0", - "inquirer": "^7.2.0", - "is-root": "^2.1.0", - "mini-css-extract-plugin": "^1.3.4", - "optimize-css-assets-webpack-plugin": "^5.0.1", - "postcss": "^8.2.4", + "inquirer": "^12.9.4", + "is-root": "^3.0.0", + "mini-css-extract-plugin": "^2.9.4", + "postcss": "^8.5.6", "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^4.1.0", - "postcss-preset-env": "^6.7.0", - "run-parallel": "1.1.9", - "sass-loader": "^10.0.2", - "signal-exit": "^3.0.2", - "style-loader": "^2.0.0", - "terser-webpack-plugin": "^4.2.0", - "ts-loader": "^8.0.1", - "url-loader": "^4.1.0", - "webpack-bundle-analyzer": "^4.3.0", - "webpack-fix-style-only-entries": "^0.6.0", - "webpack-manifest-plugin": "^3.0.0" + "postcss-loader": "^8.2.0", + "postcss-preset-env": "^10.3.1", + "run-parallel": "^1.2.0", + "sass-loader": "^16.0.5", + "signal-exit": "^4.1.0", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.14", + "ts-loader": "^9.5.4", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-fix-style-only-entries": "^0.6.1", + "webpack-manifest-plugin": "^5.0.1" }, "peerDependencies": { "sass": "*", "typescript": "*", - "webpack": "^4.0.0", - "webpack-cli": "^3.0.0", - "webpack-dev-server": "^3.0.0" + "webpack": "^5.0.0", + "webpack-cli": "^5.0.0", + "webpack-dev-server": "^5.0.0" } } diff --git a/src/__snapshots__/externals.test.js.snap b/src/__snapshots__/externals.test.js.snap index 65c37adf..d91e3eb8 100644 --- a/src/__snapshots__/externals.test.js.snap +++ b/src/__snapshots__/externals.test.js.snap @@ -1,7 +1,7 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`externals contains entries for all core WordPress JS global members 1`] = ` -Object { +{ "@wordpress/a11y": "wp.a11y", "@wordpress/annotations": "wp.annotations", "@wordpress/api-fetch": "wp.apiFetch", diff --git a/src/config.js b/src/config.js index 144df737..437c7ec3 100644 --- a/src/config.js +++ b/src/config.js @@ -7,14 +7,15 @@ module.exports = { * @type {Object} */ devServer: { - disableHostCheck: true, + allowedHosts: 'all', headers: { 'Access-Control-Allow-Origin': '*', }, - hotOnly: true, - watchOptions: { - aggregateTimeout: 300, - }, + hot: true, + }, + + watchOptions: { + aggregateTimeout: 300, }, /** diff --git a/src/helpers/clean-on-exit.js b/src/helpers/clean-on-exit.js index 944aaab8..72b51c0e 100644 --- a/src/helpers/clean-on-exit.js +++ b/src/helpers/clean-on-exit.js @@ -18,7 +18,7 @@ const cleanOnExit = ( paths = [] ) => { paths.forEach( path => { try { unlinkSync( path ); - } catch ( err ) { + } catch ( err ) { // eslint-disable-line no-unused-vars // Silently ignore unlinking errors: so long as the file is gone, that is ok. } } ); diff --git a/src/helpers/is-installed.js b/src/helpers/is-installed.js index 228d9ac3..4509af53 100644 --- a/src/helpers/is-installed.js +++ b/src/helpers/is-installed.js @@ -5,12 +5,11 @@ * @returns {Boolean} Whether the package is available via `require()`. */ module.exports = ( packageName ) => { - /* eslint-disable global-require */ + const checkRequire = require; try { - require( packageName ); + checkRequire( packageName ); return true; - } catch ( err ) { + } catch ( err ) { // eslint-disable-line no-unused-vars return false; } - /* eslint-enable */ }; diff --git a/src/loaders.js b/src/loaders.js index 0a231153..0cfe3c41 100644 --- a/src/loaders.js +++ b/src/loaders.js @@ -28,18 +28,10 @@ const createLoaderFactory = loaderKey => { }; // Define all supported loader factories within the loaders object. -[ 'eslint', 'js', 'ts', 'url', 'style', 'css', 'postcss', 'sass', 'file' ].forEach( loaderKey => { +[ 'js', 'ts', 'style', 'css', 'postcss', 'sass', 'asset', 'assetResource', 'assetInline' ].forEach( loaderKey => { loaders[ loaderKey ] = createLoaderFactory( loaderKey ); } ); -loaders.eslint.defaults = { - test: /\.jsx?$/, - exclude: /(node_modules|bower_components)/, - enforce: 'pre', - loader: require.resolve( 'eslint-loader' ), - options: {}, -}; - loaders.js.defaults = { test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, @@ -56,14 +48,27 @@ loaders.ts.defaults = { loader: require.resolve( 'ts-loader' ), }; -loaders.url.defaults = { +// Asset modules replace url-loader and file-loader in Webpack 5 +loaders.asset.defaults = { test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf)$/, - loader: require.resolve( 'url-loader' ), - options: { - limit: 10000, + type: 'asset', + parser: { + dataUrlCondition: { + maxSize: 10000, + }, }, }; +loaders.assetResource.defaults = { + test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf)$/, + type: 'asset/resource', +}; + +loaders.assetInline.defaults = { + test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf)$/, + type: 'asset/inline', +}; + loaders.style.defaults = { loader: require.resolve( 'style-loader' ), options: {}, @@ -102,11 +107,4 @@ loaders.sass.defaults = { }, }; -loaders.file.defaults = { - // Exclude `js`, `html` and `json`, but match anything else. - exclude: /\.(js|html|json)$/, - loader: require.resolve( 'file-loader' ), - options: {}, -}; - module.exports = loaders; diff --git a/src/loaders.test.js b/src/loaders.test.js index deb445d5..1a380fdb 100644 --- a/src/loaders.test.js +++ b/src/loaders.test.js @@ -22,23 +22,16 @@ describe( 'loaders', () => { expect( result.value ).toBe( 42 ); } ); - describe( '.eslint()', () => { - it( 'tests for .js or .jsx files', () => { - expect( 'file.js'.match( loaders.eslint().test ) ).not.toBeNull(); - expect( 'file.jsx'.match( loaders.eslint().test ) ).not.toBeNull(); - expect( 'file.scss'.match( loaders.eslint().test ) ).toBeNull(); - } ); - } ); describe( '.js()', () => { it( 'tests for .js or .jsx files', () => { - expect( 'file.js'.match( loaders.eslint().test ) ).not.toBeNull(); - expect( 'file.jsx'.match( loaders.eslint().test ) ).not.toBeNull(); - expect( 'file.scss'.match( loaders.eslint().test ) ).toBeNull(); + expect( 'file.js'.match( loaders.js().test ) ).not.toBeNull(); + expect( 'file.jsx'.match( loaders.js().test ) ).not.toBeNull(); + expect( 'file.scss'.match( loaders.js().test ) ).toBeNull(); } ); } ); - describe( '.url()', () => { + describe( '.asset()', () => { it( 'tests for static assets', () => { [ 'file.png', @@ -51,16 +44,19 @@ describe( 'loaders', () => { 'file.eot', 'file.ttf', ].forEach( acceptedFileType => { - expect( acceptedFileType.match( loaders.url().test ) ).not.toBeNull(); + expect( acceptedFileType.match( loaders.asset().test ) ).not.toBeNull(); } ); [ 'file.js', 'file.css', 'file.html', ].forEach( unacceptedFileType => { - expect( unacceptedFileType.match( loaders.url().test ) ).toBeNull(); + expect( unacceptedFileType.match( loaders.asset().test ) ).toBeNull(); } ); } ); + it( 'uses asset module type', () => { + expect( loaders.asset().type ).toBe( 'asset' ); + } ); } ); } ); diff --git a/src/plugins.js b/src/plugins.js index 3de1ad3d..1c4b8b89 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -1,16 +1,19 @@ const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); const { CleanWebpackPlugin } = require( 'clean-webpack-plugin' ); const { HotModuleReplacementPlugin } = require( 'webpack' ); +const { WebpackManifestPlugin: ManifestPlugin } = require( 'webpack-manifest-plugin' ); + const BellOnBundleErrorPlugin = require( 'bell-on-bundler-error-plugin' ); const CopyPlugin = require( 'copy-webpack-plugin' ); +const CssMinimizerPlugin = require( 'css-minimizer-webpack-plugin' ); +const ESLintPlugin = require( 'eslint-webpack-plugin' ); const FixStyleOnlyEntriesPlugin = require( 'webpack-fix-style-only-entries' ); -const { WebpackManifestPlugin: ManifestPlugin } = require( 'webpack-manifest-plugin' ); const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' ); const TerserPlugin = require( 'terser-webpack-plugin' ); -const OptimizeCssAssetsPlugin = require( 'optimize-css-assets-webpack-plugin' ); const deepMerge = require( './helpers/deep-merge' ); + module.exports = { /** * Expose plugin constructor functions for use in consuming applications. @@ -22,11 +25,12 @@ module.exports = { BundleAnalyzerPlugin, CleanWebpackPlugin, CopyPlugin, + ESLintPlugin, FixStyleOnlyEntriesPlugin, HotModuleReplacementPlugin, ManifestPlugin, MiniCssExtractPlugin, - OptimizeCssAssetsPlugin, + CssMinimizerPlugin, TerserPlugin, }, @@ -154,12 +158,28 @@ module.exports = { } ), /** - * Create a new OptimizeCssAssetsPlugin instance. + * Create a new CssMinimizerPlugin instance (replaces OptimizeCssAssetsPlugin in Webpack 5). + * + * @param {Object} [options] Optional plugin configuration options. + * @returns {CssMinimizerPlugin} A configured CssMinimizerPlugin instance. + */ + cssMinimizer: ( options = {} ) => new CssMinimizerPlugin( options ), + + + /** + * Create a new ESLintPlugin instance. * * @param {Object} [options] Optional plugin configuration options. - * @returns {OptimizeCssAssetsPlugin} A configured OptimizeCssAssetsPlugin instance. + * @returns {ESLintPlugin} A configured ESLintPlugin instance. */ - optimizeCssAssets: ( options = {} ) => new OptimizeCssAssetsPlugin( options ), + eslint: ( options = {} ) => new ESLintPlugin( { + extensions: [ 'js', 'jsx', 'ts', 'tsx' ], + files: [ 'src/**/*' ], + eslintPath: 'eslint/use-at-your-own-risk', + overrideConfigFile: 'eslint.config.mjs', + failOnError: true, + ...options, + } ), /** * Create a new TerserPlugin instance, defaulting to a set of options diff --git a/src/presets.js b/src/presets.js index 9f3a28a2..95e9e43d 100644 --- a/src/presets.js +++ b/src/presets.js @@ -1,4 +1,4 @@ -const { devServer, stats } = require( './config' ); +const { devServer, stats, watchOptions } = require( './config' ); const deepMerge = require( './helpers/deep-merge' ); const filePath = require( './helpers/file-path' ); const findInObject = require( './helpers/find-in-object' ); @@ -8,6 +8,7 @@ const loaders = require( './loaders' ); const plugins = require( './plugins' ); const { ManifestPlugin, MiniCssExtractPlugin } = plugins.constructors; + /** * Dictionary of shared seed objects by path. * @@ -138,23 +139,15 @@ const development = ( config = {}, options = {} ) => { module: { strictExportPresence: true, rules: [ - // Run all JS files through ESLint, if installed. - ...ifInstalled( 'eslint', getFilteredLoader( 'eslint', { - options: { - emitWarning: true, - }, - } ) ), { // "oneOf" will traverse all following loaders until one will // match the requirements. If no loader matches, it will fall - // back to the "file" loader at the end of the loader list. + // back to the asset modules at the end of the loader list. oneOf: [ // Enable processing TypeScript, if installed. ...ifInstalled( 'typescript', getFilteredLoader( 'ts' ) ), // Process JS with Babel. getFilteredLoader( 'js' ), - // Convert small files to data URIs. - getFilteredLoader( 'url' ), // Parse styles using SASS, then PostCSS. filterLoaders( { test: /\.s?css$/, @@ -177,9 +170,8 @@ const development = ( config = {}, options = {} ) => { } ), ], }, 'stylesheet' ), - // "file" loader makes sure any non-matching assets still get served. - // When you `import` an asset you get its filename. - getFilteredLoader( 'file' ), + // Asset modules handle images, fonts, etc. (replaces url-loader and file-loader) + getFilteredLoader( 'asset' ), ], }, ], @@ -191,9 +183,12 @@ const development = ( config = {}, options = {} ) => { devServer: { ...devServer, - stats, }, + watchOptions, + + stats, + plugins: [ plugins.hotModuleReplacement(), ], @@ -293,19 +288,15 @@ const production = ( config = {}, options = {} ) => { module: { strictExportPresence: true, rules: [ - // Run all JS files through ESLint, if installed. - ...ifInstalled( 'eslint', getFilteredLoader( 'eslint' ) ), { // "oneOf" will traverse all following loaders until one will // match the requirements. If no loader matches, it will fall - // back to the "file" loader at the end of the loader list. + // back to the asset modules at the end of the loader list. oneOf: [ // Enable processing TypeScript, if installed. ...ifInstalled( 'typescript', getFilteredLoader( 'ts' ) ), // Process JS with Babel. getFilteredLoader( 'js' ), - // Convert small files to data URIs. - getFilteredLoader( 'url' ), // Parse styles using SASS, then PostCSS. filterLoaders( { test: /\.s?css$/, @@ -318,9 +309,8 @@ const production = ( config = {}, options = {} ) => { getFilteredLoader( 'sass', cssOptions ), ], }, 'stylesheet' ), - // "file" loader makes sure any non-matching assets still get served. - // When you `import` an asset you get its filename. - getFilteredLoader( 'file' ), + // Asset modules handle images, fonts, etc. (replaces url-loader and file-loader) + getFilteredLoader( 'asset' ), ], }, ], @@ -329,29 +319,36 @@ const production = ( config = {}, options = {} ) => { optimization: { minimizer: [ plugins.terser(), - plugins.optimizeCssAssets( ( + plugins.cssMinimizer( ( // Set option to output source maps if devtool is set. config.devtool && ! ( /inline-/ ).test( config.devtool ) ? { - cssProcessorOptions: { - map: { - inline: false, - }, + minimizerOptions: { + preset: [ + 'default', + { + map: { + inline: false, + }, + }, + ], }, } : undefined ) ), ], nodeEnv: 'production', - noEmitOnErrors: true, + emitOnErrors: false, }, - stats, + stats, - plugins: [], - }; + plugins: [ + // Plugins will be added conditionally below + ], +}; - // If no entry was provided, inject a default entry value. +// If no entry was provided, inject a default entry value. if ( ! config.entry ) { prodDefaults.entry = { index: filePath( 'src/index.js' ), diff --git a/src/presets.test.js b/src/presets.test.js index b623b2a0..d92be7bd 100644 --- a/src/presets.test.js +++ b/src/presets.test.js @@ -258,29 +258,29 @@ describe( 'presets', () => { }, }, { filterLoaders: ( loader, loaderType ) => { - if ( loaderType === 'file' ) { - loader.options.publicPath = '../../'; - } - if ( loaderType === 'url' ) { + if ( loaderType === 'asset' ) { + // Test filtering asset modules (Webpack 5 replacement for file/url loaders) loader.test = /\.(png|jpg|jpeg|gif|svg)$/; + loader.parser = { + dataUrlCondition: { + maxSize: 8000, + }, + }; } return loader; }, } ); - const fileLoader = getLoaderByName( config.module.rules, 'file-loader' ); - const urlLoader = getLoaderByName( config.module.rules, 'url-loader' ); + // Look for asset module rule instead of file-loader/url-loader + const assetRule = getLoaderByTest( config.module.rules, /\.(png|jpg|jpeg|gif|svg)$/ ); const jsLoader = getLoaderByName( config.module.rules, 'babel-loader' ); - expect( fileLoader ).toEqual( expect.objectContaining( { - exclude: /\.(js|html|json)$/, - options: { - publicPath: '../../', - }, - } ) ); - expect( urlLoader ).toEqual( expect.objectContaining( { + expect( assetRule ).toEqual( expect.objectContaining( { test: /\.(png|jpg|jpeg|gif|svg)$/, - options: { - limit: 10000, - }, + type: 'asset', + parser: expect.objectContaining( { + dataUrlCondition: { + maxSize: 8000, + }, + } ), } ) ); expect( jsLoader ).not.toBeNull(); } ); @@ -467,29 +467,29 @@ describe( 'presets', () => { }, }, { filterLoaders: ( loader, loaderType ) => { - if ( loaderType === 'file' ) { - loader.options.publicPath = '../../'; - } - if ( loaderType === 'url' ) { + if ( loaderType === 'asset' ) { + // Test filtering asset modules (Webpack 5 replacement for file/url loaders) loader.test = /\.(png|jpg|jpeg|gif|svg)$/; + loader.parser = { + dataUrlCondition: { + maxSize: 8000, + }, + }; } return loader; }, } ); - const fileLoader = getLoaderByName( config.module.rules, 'file-loader' ); - const urlLoader = getLoaderByName( config.module.rules, 'url-loader' ); + // Look for asset module rule instead of file-loader/url-loader + const assetRule = getLoaderByTest( config.module.rules, /\.(png|jpg|jpeg|gif|svg)$/ ); const jsLoader = getLoaderByName( config.module.rules, 'babel-loader' ); - expect( fileLoader ).toEqual( expect.objectContaining( { - exclude: /\.(js|html|json)$/, - options: { - publicPath: '../../', - }, - } ) ); - expect( urlLoader ).toEqual( expect.objectContaining( { + expect( assetRule ).toEqual( expect.objectContaining( { test: /\.(png|jpg|jpeg|gif|svg)$/, - options: { - limit: 10000, - }, + type: 'asset', + parser: expect.objectContaining( { + dataUrlCondition: { + maxSize: 8000, + }, + } ), } ) ); expect( jsLoader ).not.toBeNull(); } ); diff --git a/test/src/index.js b/test/src/index.js index 032f5a87..928c16bd 100644 --- a/test/src/index.js +++ b/test/src/index.js @@ -1,6 +1,9 @@ +import './style.scss'; + import { getResults } from './helpers'; -import './style.scss'; +// Intentional ESLint errors for testing +var unused_variable = "test"; // no-var, no-unused-vars, quotes ( async () => { const results = await getResults(); diff --git a/test/test-config.js b/test/test-config.js index f196d43c..ca6d38ea 100644 --- a/test/test-config.js +++ b/test/test-config.js @@ -10,7 +10,7 @@ module.exports = [ }, output: { path: filePath( 'test/build/prod' ), - } + }, } ), // This second build targets the same output directory as 'production-test' // to demonstrate whether the asset manifest is correctly shared between @@ -23,7 +23,7 @@ module.exports = [ }, output: { path: filePath( 'test/build/prod' ), - } + }, } ), // This third build targets a different output directory than the others // to make sure that manifests are not shared _between_ directories. @@ -35,7 +35,7 @@ module.exports = [ }, output: { path: filePath( 'test/build/prod-alternate' ), - } + }, } ), // This build uses a development configuration instead of a prod one. presets.development( { From f7845c25e513f4a412db3e2add487a6fcfee5973 Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:44:37 +0530 Subject: [PATCH 2/7] Update Travis CI badge URL in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c504b98b..7e473e40 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Webpack Helpers -A [Human Made](https://humanmade.com) project. [![Build Status](https://travis-ci.com/humanmade/webpack-helpers.svg?branch=main)](https://travis-ci.com/humanmade/webpack-helpers) +A [Human Made](https://humanmade.com) project. [![Build Status](https://app.travis-ci.com/humanmade/webpack-helpers.svg?token=xNUfWUZqcGkpUy3iiQyu&branch=main) ## Background From 9d41ebc9fa4b5ea7148c199100cbb98428a7a25b Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:45:47 +0530 Subject: [PATCH 3/7] Remove intentional ESLint errors from index.js --- test/src/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/src/index.js b/test/src/index.js index 928c16bd..e5adcd27 100644 --- a/test/src/index.js +++ b/test/src/index.js @@ -2,9 +2,6 @@ import './style.scss'; import { getResults } from './helpers'; -// Intentional ESLint errors for testing -var unused_variable = "test"; // no-var, no-unused-vars, quotes - ( async () => { const results = await getResults(); console.log( results ); From 674d42edd719b20fd1be797f16fb77549ac6593e Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:54:51 +0530 Subject: [PATCH 4/7] Remove Node.js 10 and 12 from Travis config --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 88bcb31b..52489092 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ script: "npm run lint && npm run test && npm run test-build" node_js: - node - lts/* - - 12 - - 10 branches: only: - main From e26c208890300f303e18935db188a100536602df Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:58:59 +0530 Subject: [PATCH 5/7] Update webpack-cli version in install instructions --- docs/guides/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/getting-started.md b/docs/guides/getting-started.md index a273c3b2..08ce4906 100644 --- a/docs/guides/getting-started.md +++ b/docs/guides/getting-started.md @@ -18,7 +18,7 @@ While this package depends in turn on a number of loaders and plugins, it delibe ** *Check notice before proceeding:** ```bash -npm install --save-dev @humanmade/webpack-helpers webpack@5 webpack-cli@5 webpack-dev-server@5 sass +npm install --save-dev @humanmade/webpack-helpers webpack@5 webpack-cli@6 webpack-dev-server@5 sass ``` **Webpack 5 Support:** This package now fully supports Webpack 5 with all the latest features and optimizations. We recommend using Webpack 5 for all new projects as it provides better performance, improved tree shaking, and enhanced module federation capabilities. From 424b4393604ae46593278de364bff71f9a37841e Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Thu, 4 Sep 2025 23:59:43 +0530 Subject: [PATCH 6/7] Remove link from build status badge in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e473e40..6e2a50ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Webpack Helpers -A [Human Made](https://humanmade.com) project. [![Build Status](https://app.travis-ci.com/humanmade/webpack-helpers.svg?token=xNUfWUZqcGkpUy3iiQyu&branch=main) +A [Human Made](https://humanmade.com) project. ![Build Status](https://app.travis-ci.com/humanmade/webpack-helpers.svg?token=xNUfWUZqcGkpUy3iiQyu&branch=main) ## Background From ee0908573ce8fcf6c33258531d80b372d3618f39 Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Tue, 9 Sep 2025 21:52:02 +0530 Subject: [PATCH 7/7] Add Node.js 22 requirement and .nvmrc file --- .nvmrc | 1 + README.md | 10 ++++++++++ package.json | 3 +++ 3 files changed, 14 insertions(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/README.md b/README.md index 6e2a50ee..ac50d2bb 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ Visit [humanmade.github.io/webpack-helpers](https://humanmade.github.io/webpack- Visit the [`docs/`](./docs) folder to view or modify the content used to generate this documentation site. +## Requirements + +- Node.js >= 22.0.0 + +This project includes an `.nvmrc` file specifying Node.js 22. If you use nvm, you can run: + +```bash +nvm use +``` + ## What's In The Box ### [Opinionated Configuration Presets](https://humanmade.github.io/webpack-helpers/modules/presets) diff --git a/package.json b/package.json index 8001cde5..24e7d494 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "main": "index.js", "author": "Human Made", "license": "MIT", + "engines": { + "node": ">=22.0.0" + }, "homepage": "https://github.com/humanmade/webpack-helpers#readme", "repository": { "type": "git",