diff --git a/.cspell.json b/.cspell.json index 0e1a2be1f25..02d12f393c6 100644 --- a/.cspell.json +++ b/.cspell.json @@ -64,6 +64,7 @@ "nwjs", "Oikawa", "pathinfo", + "plopfile", "pnpm", "postcss", "prebuild", @@ -100,7 +101,9 @@ "Yukii", "Yuuji", "Zangetsu", - "Zenitsu" + "Zenitsu", + "quickstart", + "pinia" ], "dictionaries": ["npm", "software-terms"], "ignorePaths": [ diff --git a/.eslintignore b/.eslintignore index 91718ffa7a2..844c3971e3f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,4 +10,5 @@ test/**/index.js test/build/config/error-commonjs/syntax-error.js test/build/config/error-mjs/syntax-error.mjs test/build/config/error-array/webpack.config.js +test/build/config-format/esm-require-await/webpack.config.js test/configtest/with-config-path/syntax-error.config.js diff --git a/.eslintrc.js b/.eslintrc.js index a17e0c5be7c..3a7b389a670 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,14 +1,9 @@ module.exports = { root: true, reportUnusedDisableDirectives: true, - extends: ["eslint:recommended", "plugin:node/recommended", "prettier"], + extends: ["eslint:recommended", "plugin:n/recommended", "prettier"], parserOptions: { ecmaVersion: 2018, sourceType: "script" }, - plugins: ["node"], - settings: { - node: { - allowModules: ["@webpack-cli/generators"], - }, - }, + plugins: ["n"], env: { node: true, es6: true, @@ -16,6 +11,7 @@ module.exports = { }, rules: { "no-process-exit": "off", + "n/no-process-exit": "off", "no-template-curly-in-string": "error", "no-caller": "error", "no-extra-bind": "error", @@ -26,7 +22,7 @@ module.exports = { overrides: [ { settings: { - node: { + n: { tryExtensions: [".ts", ".tsx", ".js", ".jsx", ".json"], }, }, @@ -39,7 +35,27 @@ module.exports = { parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint"], rules: { - "node/no-unsupported-features/es-syntax": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + args: "all", + argsIgnorePattern: "^_", + caughtErrors: "all", + caughtErrorsIgnorePattern: "^_", + destructuredArrayIgnorePattern: "^_", + varsIgnorePattern: "^_", + ignoreRestSiblings: true, + }, + ], + "n/no-unsupported-features/es-syntax": "off", + "n/no-process-exit": "off", + "@typescript-eslint/no-require-imports": "off", + }, + }, + { + files: ["**/packages/create-webpack-app/**/*.js"], + parserOptions: { + sourceType: "module", }, }, ], diff --git a/.gitattributes b/.gitattributes index 07764a78d98..dd5eef67af6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -* text eol=lf \ No newline at end of file +* text eol=lf +*png.tpl -text \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f8761f90d48..6a74a6b9254 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -69,10 +69,9 @@ In case you are suggesting a new feature, we will match your idea with our curre yarn install ``` -- Bootstrap all the submodules before building for the first time +- Build all the submodules before building for the first time ```bash - yarn lerna bootstrap yarn build ``` @@ -131,6 +130,8 @@ This is a multi-package repository and dependencies are managed using [lerna](ht > If you are adding or updating any dependency, please commit the updated `yarn.lock` file. +To update dependencies, import each dependency and make sure the command line build passes. The dependency should support our minimal supported node version for webpack, found in `package.json`. + ## Branching Model We base our branching model on [git flow](http://nvie.com/posts/a-successful-git-branching-model/). Instead of working with a `develop` base branch, we use the `master` branch. We do it to ease the workflow a bit. However, we find that adding prefixes to the branches is useful. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8505d9a5fdf..2db9dc995b2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,7 @@ updates: timezone: Europe/Berlin open-pull-requests-limit: 10 versioning-strategy: lockfile-only + groups: + dependencies: + patterns: + - "*" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 24a35a06add..8caf799d12e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -9,6 +9,6 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Repository" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Dependency Review" - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 07b5c3bc21c..8cf1eb0445d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -4,15 +4,10 @@ on: push: branches: - master - - next pull_request: branches: - master - next - workflow_dispatch: - inputs: - tags: - description: "Test description" permissions: contents: read @@ -34,10 +29,10 @@ jobs: webpack-version: [latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Using Node v${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "yarn" @@ -45,9 +40,6 @@ jobs: - name: Install dependencies run: yarn --frozen-lockfile - - name: Bootstrap - run: yarn lerna bootstrap - - name: Build run: yarn build @@ -66,24 +58,28 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - node-version: [14.x, 16.x, 18.x, 20.x] + node-version: [18.x, 20.x, 22.x, 23.x] shard: ["1/4", "2/4", "3/4", "4/4"] webpack-version: [latest] dev-server-version: [latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Using Node v${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "yarn" + - uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Install dependencies - run: yarn --frozen-lockfile --ignore-engines + run: yarn --frozen-lockfile --ignore-engines --ignore-scripts - name: Prepare environment for tests run: yarn build:ci @@ -92,7 +88,7 @@ jobs: run: yarn test:coverage --ci --shard=${{ matrix.shard }} - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 smoketests: name: Smoketests - ${{ matrix.os }} - Node v${{ matrix.node-version }} @@ -105,12 +101,12 @@ jobs: os: [ubuntu-latest] node-version: [lts/*] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Using Node v${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "yarn" @@ -131,17 +127,19 @@ jobs: group: commitlint-${{ github.ref }} cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: "lts/*" cache: "yarn" - run: yarn --frozen-lockfile - - uses: wagoid/commitlint-github-action@v5 + - name: Validate PR commits with commitlint + if: github.event_name == 'pull_request' + run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose env: NODE_PATH: ${{ github.workspace }}/node_modules diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 14e537949af..d762f5f3d78 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -22,13 +22,13 @@ jobs: steps: - name: Checkout Codebase - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ env.GITHUB_ACCESS_TOKEN }} - name: Using Node v${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "yarn" @@ -39,9 +39,6 @@ jobs: - name: Install latest webpack and webpack-dev-server version run: yarn add -W webpack-dev-server@latest webpack@latest - - name: Bootstrap - run: yarn lerna bootstrap - - name: Build run: yarn build diff --git a/.gitignore b/.gitignore index d1c154e5dd0..9514a890c09 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ packages/**/*.map # cache .eslintcache +.cspellcache # build files packages/**/lib diff --git a/.husky/commit-msg b/.husky/commit-msg index e8511eaeaf6..dbce4f4cf4b 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npx --no-install commitlint --edit $1 +commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit index 36af219892f..c27d8893a99 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npx lint-staged +lint-staged diff --git a/CHANGELOG.md b/CHANGELOG.md index 0065fb459c1..67a59a7832a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [5.1.4](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.3...webpack-cli@5.1.4) (2023-06-07) + +### Bug Fixes + +- multi compiler progress output ([f659624](https://github.com/webpack/webpack-cli/commit/f6596242c74100bfd6fa391ed2071402a3bd4785)) + ## [5.1.3](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.2...webpack-cli@5.1.3) (2023-06-04) ### Bug Fixes diff --git a/OPTIONS.md b/OPTIONS.md index a638429def9..22db8518f5b 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -47,11 +47,14 @@ Options: --cache-name Name for the cache. Different names will lead to different coexisting caches. --cache-profile Track and log detailed timing information for individual cache items. --no-cache-profile Negative 'cache-profile' option. + --cache-readonly Enable/disable readonly mode. + --no-cache-readonly Negative 'cache-readonly' option. --cache-store When to store data to the filesystem. (pack: Store data when compiler is idle in a single file). --cache-version Version of the cache data. Different versions won't allow to reuse the cache and override existing content. Update the version when config changed in a way which doesn't allow to reuse cache. This will invalidate the cache. --context The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory. --dependencies References to another configuration to depend on. --dependencies-reset Clear all items provided in 'dependencies' configuration. References to other configurations to depend on. + --no-dev-server Negative 'dev-server' option. -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -74,8 +77,6 @@ Options: --no-experiments-cache-unaffected Negative 'experiments-cache-unaffected' option. --experiments-css Enable css support. --no-experiments-css Negative 'experiments-css' option. - --experiments-css-exports-only Avoid generating and loading a stylesheet and only embed exports from css into output javascript files. - --no-experiments-css-exports-only Negative 'experiments-css-exports-only' option. --experiments-future-defaults Apply defaults of next major version. --no-experiments-future-defaults Negative 'experiments-future-defaults' option. --experiments-layers Enable module layers. @@ -140,6 +141,8 @@ Options: --module-expr-context-reg-exp [value] Sets the default regular expression for full dynamic dependencies. Deprecated: This option has moved to 'module.parser.javascript.exprContextRegExp'. --no-module-expr-context-reg-exp Negative 'module-expr-context-reg-exp' option. --module-expr-context-request Set the default request for full dynamic dependencies. Deprecated: This option has moved to 'module.parser.javascript.exprContextRequest'. + --module-generator-asset-binary Whether or not this asset module should be considered binary. This can be set to 'false' to treat this asset module as text. + --no-module-generator-asset-binary Negative 'module-generator-asset-binary' option. --module-generator-asset-data-url-encoding Asset encoding (defaults to base64). --no-module-generator-asset-data-url-encoding Negative 'module-generator-asset-data-url-encoding' option. --module-generator-asset-data-url-mimetype Asset mimetype (getting from file extension by default). @@ -148,17 +151,51 @@ Options: --module-generator-asset-filename Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk. --module-generator-asset-output-path Emit the asset in the specified folder relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there. --module-generator-asset-public-path The 'publicPath' specifies the public URL address of the output files when referenced in a browser. + --module-generator-asset-inline-binary Whether or not this asset module should be considered binary. This can be set to 'false' to treat this asset module as text. + --no-module-generator-asset-inline-binary Negative 'module-generator-asset-inline-binary' option. --module-generator-asset-inline-data-url-encoding Asset encoding (defaults to base64). --no-module-generator-asset-inline-data-url-encoding Negative 'module-generator-asset-inline-data-url-encoding' option. --module-generator-asset-inline-data-url-mimetype Asset mimetype (getting from file extension by default). + --module-generator-asset-resource-binary Whether or not this asset module should be considered binary. This can be set to 'false' to treat this asset module as text. + --no-module-generator-asset-resource-binary Negative 'module-generator-asset-resource-binary' option. --module-generator-asset-resource-emit Emit an output asset from this asset module. This can be set to 'false' to omit emitting e. g. for SSR. --no-module-generator-asset-resource-emit Negative 'module-generator-asset-resource-emit' option. --module-generator-asset-resource-filename Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk. --module-generator-asset-resource-output-path Emit the asset in the specified folder relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there. --module-generator-asset-resource-public-path The 'publicPath' specifies the public URL address of the output files when referenced in a browser. + --module-generator-css-es-module Configure the generated JS modules that use the ES modules syntax. + --no-module-generator-css-es-module Negative 'module-generator-css-es-module' option. + --module-generator-css-exports-only Avoid generating and loading a stylesheet and only embed exports from css into output javascript files. + --no-module-generator-css-exports-only Negative 'module-generator-css-exports-only' option. + --module-generator-css-auto-es-module Configure the generated JS modules that use the ES modules syntax. + --no-module-generator-css-auto-es-module Negative 'module-generator-css-auto-es-module' option. + --module-generator-css-auto-exports-convention Specifies the convention of exported names. + --module-generator-css-auto-exports-only Avoid generating and loading a stylesheet and only embed exports from css into output javascript files. + --no-module-generator-css-auto-exports-only Negative 'module-generator-css-auto-exports-only' option. + --module-generator-css-auto-local-ident-name Configure the generated local ident name. + --module-generator-css-global-es-module Configure the generated JS modules that use the ES modules syntax. + --no-module-generator-css-global-es-module Negative 'module-generator-css-global-es-module' option. + --module-generator-css-global-exports-convention Specifies the convention of exported names. + --module-generator-css-global-exports-only Avoid generating and loading a stylesheet and only embed exports from css into output javascript files. + --no-module-generator-css-global-exports-only Negative 'module-generator-css-global-exports-only' option. + --module-generator-css-global-local-ident-name Configure the generated local ident name. + --module-generator-css-module-es-module Configure the generated JS modules that use the ES modules syntax. + --no-module-generator-css-module-es-module Negative 'module-generator-css-module-es-module' option. + --module-generator-css-module-exports-convention Specifies the convention of exported names. + --module-generator-css-module-exports-only Avoid generating and loading a stylesheet and only embed exports from css into output javascript files. + --no-module-generator-css-module-exports-only Negative 'module-generator-css-module-exports-only' option. + --module-generator-css-module-local-ident-name Configure the generated local ident name. --module-no-parse A regular expression, when matched the module is not parsed. An absolute path, when the module starts with this path it is not parsed. --module-no-parse-reset Clear all items provided in 'module.noParse' configuration. Don't parse files matching. It's matched against the full resolved request. --module-parser-asset-data-url-condition-max-size Maximum size of asset that should be inline as modules. Default: 8kb. + --module-parser-css-named-exports Use ES modules named export for css exports. + --no-module-parser-css-named-exports Negative 'module-parser-css-named-exports' option. + --module-parser-css-auto-named-exports Use ES modules named export for css exports. + --no-module-parser-css-auto-named-exports Negative 'module-parser-css-auto-named-exports' option. + --module-parser-css-global-named-exports Use ES modules named export for css exports. + --no-module-parser-css-global-named-exports Negative 'module-parser-css-global-named-exports' option. + --module-parser-css-module-named-exports Use ES modules named export for css exports. + --no-module-parser-css-module-named-exports Negative 'module-parser-css-module-named-exports' option. --no-module-parser-javascript-amd Negative 'module-parser-javascript-amd' option. --module-parser-javascript-browserify Enable/disable special handling for browserify bundles. --no-module-parser-javascript-browserify Negative 'module-parser-javascript-browserify' option. @@ -168,6 +205,8 @@ Options: --no-module-parser-javascript-commonjs-magic-comments Negative 'module-parser-javascript-commonjs-magic-comments' option. --module-parser-javascript-create-require [value] Enable/disable parsing "import { createRequire } from "module"" and evaluating createRequire(). --no-module-parser-javascript-create-require Negative 'module-parser-javascript-create-require' option. + --module-parser-javascript-dynamic-import-fetch-priority Specifies global fetchPriority for dynamic import. + --no-module-parser-javascript-dynamic-import-fetch-priority Negative 'module-parser-javascript-dynamic-import-fetch-priority' option. --module-parser-javascript-dynamic-import-mode Specifies global mode for dynamic import. --module-parser-javascript-dynamic-import-prefetch [value] Specifies global prefetch for dynamic import. --no-module-parser-javascript-dynamic-import-prefetch Negative 'module-parser-javascript-dynamic-import-prefetch' option. @@ -199,6 +238,7 @@ Options: --no-module-parser-javascript-node-filename Negative 'module-parser-javascript-node-filename' option. --module-parser-javascript-node-global [value] Include a polyfill for the 'global' variable. --no-module-parser-javascript-node-global Negative 'module-parser-javascript-node-global' option. + --module-parser-javascript-override-strict Override the module to strict or non-strict. This may affect the behavior of the module (some behaviors differ between strict and non-strict), so please configure this option carefully. --module-parser-javascript-reexport-exports-presence Specifies the behavior of invalid export names in "export ... from ...". This might be useful to disable during the migration from "export ... from ..." to "export type ... from ..." when reexporting types in TypeScript. --no-module-parser-javascript-reexport-exports-presence Negative 'module-parser-javascript-reexport-exports-presence' option. --module-parser-javascript-require-context Enable/disable parsing of require.context syntax. @@ -241,6 +281,8 @@ Options: --no-module-parser-javascript-auto-commonjs-magic-comments Negative 'module-parser-javascript-auto-commonjs-magic-comments' option. --module-parser-javascript-auto-create-require [value] Enable/disable parsing "import { createRequire } from "module"" and evaluating createRequire(). --no-module-parser-javascript-auto-create-require Negative 'module-parser-javascript-auto-create-require' option. + --module-parser-javascript-auto-dynamic-import-fetch-priority Specifies global fetchPriority for dynamic import. + --no-module-parser-javascript-auto-dynamic-import-fetch-priority Negative 'module-parser-javascript-auto-dynamic-import-fetch-priority' option. --module-parser-javascript-auto-dynamic-import-mode Specifies global mode for dynamic import. --module-parser-javascript-auto-dynamic-import-prefetch [value] Specifies global prefetch for dynamic import. --no-module-parser-javascript-auto-dynamic-import-prefetch Negative 'module-parser-javascript-auto-dynamic-import-prefetch' option. @@ -272,6 +314,7 @@ Options: --no-module-parser-javascript-auto-node-filename Negative 'module-parser-javascript-auto-node-filename' option. --module-parser-javascript-auto-node-global [value] Include a polyfill for the 'global' variable. --no-module-parser-javascript-auto-node-global Negative 'module-parser-javascript-auto-node-global' option. + --module-parser-javascript-auto-override-strict Override the module to strict or non-strict. This may affect the behavior of the module (some behaviors differ between strict and non-strict), so please configure this option carefully. --module-parser-javascript-auto-reexport-exports-presence Specifies the behavior of invalid export names in "export ... from ...". This might be useful to disable during the migration from "export ... from ..." to "export type ... from ..." when reexporting types in TypeScript. --no-module-parser-javascript-auto-reexport-exports-presence Negative 'module-parser-javascript-auto-reexport-exports-presence' option. --module-parser-javascript-auto-require-context Enable/disable parsing of require.context syntax. @@ -314,6 +357,8 @@ Options: --no-module-parser-javascript-dynamic-commonjs-magic-comments Negative 'module-parser-javascript-dynamic-commonjs-magic-comments' option. --module-parser-javascript-dynamic-create-require [value] Enable/disable parsing "import { createRequire } from "module"" and evaluating createRequire(). --no-module-parser-javascript-dynamic-create-require Negative 'module-parser-javascript-dynamic-create-require' option. + --module-parser-javascript-dynamic-dynamic-import-fetch-priority Specifies global fetchPriority for dynamic import. + --no-module-parser-javascript-dynamic-dynamic-import-fetch-priority Negative 'module-parser-javascript-dynamic-dynamic-import-fetch-priority' option. --module-parser-javascript-dynamic-dynamic-import-mode Specifies global mode for dynamic import. --module-parser-javascript-dynamic-dynamic-import-prefetch [value] Specifies global prefetch for dynamic import. --no-module-parser-javascript-dynamic-dynamic-import-prefetch Negative 'module-parser-javascript-dynamic-dynamic-import-prefetch' option. @@ -345,6 +390,7 @@ Options: --no-module-parser-javascript-dynamic-node-filename Negative 'module-parser-javascript-dynamic-node-filename' option. --module-parser-javascript-dynamic-node-global [value] Include a polyfill for the 'global' variable. --no-module-parser-javascript-dynamic-node-global Negative 'module-parser-javascript-dynamic-node-global' option. + --module-parser-javascript-dynamic-override-strict Override the module to strict or non-strict. This may affect the behavior of the module (some behaviors differ between strict and non-strict), so please configure this option carefully. --module-parser-javascript-dynamic-reexport-exports-presence Specifies the behavior of invalid export names in "export ... from ...". This might be useful to disable during the migration from "export ... from ..." to "export type ... from ..." when reexporting types in TypeScript. --no-module-parser-javascript-dynamic-reexport-exports-presence Negative 'module-parser-javascript-dynamic-reexport-exports-presence' option. --module-parser-javascript-dynamic-require-context Enable/disable parsing of require.context syntax. @@ -387,6 +433,8 @@ Options: --no-module-parser-javascript-esm-commonjs-magic-comments Negative 'module-parser-javascript-esm-commonjs-magic-comments' option. --module-parser-javascript-esm-create-require [value] Enable/disable parsing "import { createRequire } from "module"" and evaluating createRequire(). --no-module-parser-javascript-esm-create-require Negative 'module-parser-javascript-esm-create-require' option. + --module-parser-javascript-esm-dynamic-import-fetch-priority Specifies global fetchPriority for dynamic import. + --no-module-parser-javascript-esm-dynamic-import-fetch-priority Negative 'module-parser-javascript-esm-dynamic-import-fetch-priority' option. --module-parser-javascript-esm-dynamic-import-mode Specifies global mode for dynamic import. --module-parser-javascript-esm-dynamic-import-prefetch [value] Specifies global prefetch for dynamic import. --no-module-parser-javascript-esm-dynamic-import-prefetch Negative 'module-parser-javascript-esm-dynamic-import-prefetch' option. @@ -418,6 +466,7 @@ Options: --no-module-parser-javascript-esm-node-filename Negative 'module-parser-javascript-esm-node-filename' option. --module-parser-javascript-esm-node-global [value] Include a polyfill for the 'global' variable. --no-module-parser-javascript-esm-node-global Negative 'module-parser-javascript-esm-node-global' option. + --module-parser-javascript-esm-override-strict Override the module to strict or non-strict. This may affect the behavior of the module (some behaviors differ between strict and non-strict), so please configure this option carefully. --module-parser-javascript-esm-reexport-exports-presence Specifies the behavior of invalid export names in "export ... from ...". This might be useful to disable during the migration from "export ... from ..." to "export type ... from ..." when reexporting types in TypeScript. --no-module-parser-javascript-esm-reexport-exports-presence Negative 'module-parser-javascript-esm-reexport-exports-presence' option. --module-parser-javascript-esm-require-context Enable/disable parsing of require.context syntax. @@ -607,6 +656,8 @@ Options: --no-output-cross-origin-loading Negative 'output-cross-origin-loading' option. --output-css-chunk-filename Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk. --output-css-filename Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk. + --output-css-head-data-compression Compress the data in the head tag of CSS files. + --no-output-css-head-data-compression Negative 'output-css-head-data-compression' option. --output-devtool-fallback-module-filename-template Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers. --output-devtool-module-filename-template Filename template string of function for the sources array in a generated SourceMap. --output-devtool-namespace Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries. @@ -618,18 +669,28 @@ Options: --output-enabled-wasm-loading-types-reset Clear all items provided in 'output.enabledWasmLoadingTypes' configuration. List of wasm loading types enabled for use by entry points. --output-environment-arrow-function The environment supports arrow functions ('() => { ... }'). --no-output-environment-arrow-function Negative 'output-environment-arrow-function' option. + --output-environment-async-function The environment supports async function and await ('async function () { await ... }'). + --no-output-environment-async-function Negative 'output-environment-async-function' option. --output-environment-big-int-literal The environment supports BigInt as literal (123n). --no-output-environment-big-int-literal Negative 'output-environment-big-int-literal' option. --output-environment-const The environment supports const and let for variable declarations. --no-output-environment-const Negative 'output-environment-const' option. --output-environment-destructuring The environment supports destructuring ('{ a, b } = obj'). --no-output-environment-destructuring Negative 'output-environment-destructuring' option. + --output-environment-document The environment supports 'document'. + --no-output-environment-document Negative 'output-environment-document' option. --output-environment-dynamic-import The environment supports an async import() function to import EcmaScript modules. --no-output-environment-dynamic-import Negative 'output-environment-dynamic-import' option. + --output-environment-dynamic-import-in-worker The environment supports an async import() is available when creating a worker. + --no-output-environment-dynamic-import-in-worker Negative 'output-environment-dynamic-import-in-worker' option. --output-environment-for-of The environment supports 'for of' iteration ('for (const x of array) { ... }'). --no-output-environment-for-of Negative 'output-environment-for-of' option. + --output-environment-global-this The environment supports 'globalThis'. + --no-output-environment-global-this Negative 'output-environment-global-this' option. --output-environment-module The environment supports EcmaScript Module syntax to import EcmaScript modules (import ... from '...'). --no-output-environment-module Negative 'output-environment-module' option. + --output-environment-node-prefix-for-core-modules The environment supports `node:` prefix for Node.js core modules. + --no-output-environment-node-prefix-for-core-modules Negative 'output-environment-node-prefix-for-core-modules' option. --output-environment-optional-chaining The environment supports optional chaining ('obj?.a' or 'obj?.()'). --no-output-environment-optional-chaining Negative 'output-environment-optional-chaining' option. --output-environment-template-literal The environment supports template literals. @@ -836,6 +897,8 @@ Options: --no-snapshot-resolve-build-dependencies-hash Negative 'snapshot-resolve-build-dependencies-hash' option. --snapshot-resolve-build-dependencies-timestamp Use timestamps of the files/directories to determine invalidation. --no-snapshot-resolve-build-dependencies-timestamp Negative 'snapshot-resolve-build-dependencies-timestamp' option. + --snapshot-unmanaged-paths A RegExp matching an unmanaged directory. A path to an unmanaged directory. + --snapshot-unmanaged-paths-reset Clear all items provided in 'snapshot.unmanagedPaths' configuration. List of paths that are not managed by a package manager and the contents are subject to change. --stats [value] Stats options object or preset name. --no-stats Negative 'stats' option. --stats-all Fallback value for stats options when an option is not defined (has precedence over local webpack defaults). diff --git a/README.md b/README.md index 247b78469e6..5dbabeda940 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ [![codecov][codecov-badge]][codecov-url] [![Install Size][size]][size-url] [![GitHub Discussions][discussion]][discussion-url] +[![Discord][discord-invite]][discord-url] ## Table of Contents @@ -59,10 +60,7 @@ Thus, webpack CLI provides different commands for many common tasks. - [`build|bundle|b [entries...] [options]`](https://webpack.js.org/api/cli/#build) - Run webpack (default command, can be omitted). - [`configtest|t [config-path]`](https://webpack.js.org/api/cli/#configtest) - Validate a webpack configuration. - [`help|h [command] [option]`](https://webpack.js.org/api/cli/#help) - Display help for commands and options. -- [`init|create|new|c|n [generation-path] [options]`](https://webpack.js.org/api/cli/#init) - Create a new webpack project. - [`info|i [options]`](https://webpack.js.org/api/cli/#info) - Returns information related to the local environment. -- [`plugin|p [output-path] [options]`](https://webpack.js.org/api/cli/#plugin) - Initiate new plugin project. -- [`loader|l [output-path] [options]`](https://webpack.js.org/api/cli/#loader) - Initiate new loader project. - [`serve|server|s [entries...] [options]`](https://webpack.js.org/api/cli/#serve) - Use webpack with a development server that provides live reloading. - [`version|v [commands...]`](https://webpack.js.org/api/cli/#version) - Output the version number of `webpack`, `webpack-cli`, `webpack-dev-server`, and commands. - [`watch|w [entries...] [options]`](https://webpack.js.org/api/cli/#watch) - Run webpack and watch for files changes. @@ -73,14 +71,10 @@ If you have followed the [Getting Started](https://webpack.js.org/guides/getting Otherwise, you would need to install webpack CLI and the packages you want to use. -If you want to create a fresh webpack project, the `init` command will guide you through setting up a project. Run the command as stated below. +If you want to create a fresh webpack project run the command as stated below: ```sh -npm i webpack-cli @webpack-cli/init -``` - -```sh -npx webpack-cli init +npx create-webpack-app init ``` You will then be prompted for some questions about which features you want to use, such as `scss`, `typescript`, `PWA` support or other features. @@ -99,7 +93,7 @@ The webpack family welcomes any contributor, small or big. We are happy to elabo ## Funding -If you like **webpack**, please consider donating through [Open Collective](https://opencollective.com/webpack) to help us maintain it. +If you like **webpack**, please consider donating through [Open Collective](https://opencollective.com/webpack) to help us keep the project relevant. [npm]: https://img.shields.io/npm/v/webpack-cli.svg [npm-url]: https://www.npmjs.com/package/webpack-cli @@ -111,6 +105,8 @@ If you like **webpack**, please consider donating through [Open Collective](http [size-url]: https://packagephobia.com/result?p=webpack-cli [discussion]: https://img.shields.io/github/discussions/webpack/webpack [discussion-url]: https://github.com/webpack/webpack/discussions +[discord-invite]: https://img.shields.io/discord/1180618526436888586?style=flat&logo=discord&logoColor=white&label=discord +[discord-url]: https://discord.gg/ARKBCXBu ## Code of Conduct diff --git a/SERVE-OPTIONS-v4.md b/SERVE-OPTIONS-v4.md index 100364c7451..7c1f16514f7 100644 --- a/SERVE-OPTIONS-v4.md +++ b/SERVE-OPTIONS-v4.md @@ -18,7 +18,6 @@ Options: -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. - --extends Path to the configuration to be extended (only works when using webpack-cli). --mode Enable production optimizations or development hints. --name Name of the configuration. Used when loading multiple configurations. -o, --output-path The output directory as **absolute path** (required). diff --git a/SERVE-OPTIONS-v5.md b/SERVE-OPTIONS-v5.md new file mode 100644 index 00000000000..0dd6c384980 --- /dev/null +++ b/SERVE-OPTIONS-v5.md @@ -0,0 +1,117 @@ +``` +Usage: webpack serve|server|s [entries...] [options] + +Run the webpack dev server and watch for source file changes while serving. + +Options: + -c, --config Provide path to one or more webpack configuration files to process, e.g. "./webpack.config.js". + --config-name Name(s) of particular configuration(s) to use if configuration file exports an array of multiple configurations. + -m, --merge Merge two or more configurations using 'webpack-merge'. + --disable-interpret Disable interpret for loading the config file. + --env Environment variables passed to the configuration when it is a function, e.g. "myvar" or "myvar=myval". + --node-env Sets process.env.NODE_ENV to the specified value. + --analyze It invokes webpack-bundle-analyzer plugin to get bundle information. + --progress [value] Print compilation progress during build. + -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. + --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. + -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). + --no-devtool Negative 'devtool' option. + --entry A module that is loaded upon startup. Only the last one is exported. + --mode Enable production optimizations or development hints. + --name Name of the configuration. Used when loading multiple configurations. + -o, --output-path The output directory as **absolute path** (required). + --stats [value] Stats options object or preset name. + --no-stats Negative 'stats' option. + -t, --target Environment to build for. Environment to build for. An array of environments to build for all of them when possible. + --no-target Negative 'target' option. + -w, --watch Enter watch mode, which rebuilds on file change. + --no-watch Negative 'watch' option. + --watch-options-stdin Stop watching when stdin stream has ended. + --no-watch-options-stdin Negative 'watch-options-stdin' option. + --allowed-hosts Allows to enumerate the hosts from which access to the dev server are allowed (useful when you are proxying dev server, by default is 'auto'). + --allowed-hosts-reset Clear all items provided in 'allowedHosts' configuration. Allows to enumerate the hosts from which access to the dev server are allowed (useful when you are proxying dev server, by default is 'auto'). + --bonjour Allows to broadcasts dev server via ZeroConf networking on start. + --no-bonjour Disallows to broadcasts dev server via ZeroConf networking on start. + --no-client Disables client script. + --client-logging Allows to set log level in the browser. + --client-overlay Enables a full-screen overlay in the browser when there are compiler errors or warnings. + --no-client-overlay Disables the full-screen overlay in the browser when there are compiler errors or warnings. + --client-overlay-errors Enables a full-screen overlay in the browser when there are compiler errors. + --no-client-overlay-errors Disables the full-screen overlay in the browser when there are compiler errors. + --client-overlay-warnings Enables a full-screen overlay in the browser when there are compiler warnings. + --no-client-overlay-warnings Disables the full-screen overlay in the browser when there are compiler warnings. + --client-overlay-runtime-errors Enables a full-screen overlay in the browser when there are uncaught runtime errors. + --no-client-overlay-runtime-errors Disables the full-screen overlay in the browser when there are uncaught runtime errors. + --client-overlay-trusted-types-policy-name The name of a Trusted Types policy for the overlay. Defaults to 'webpack-dev-server#overlay'. + --client-progress Prints compilation progress in percentage in the browser. + --no-client-progress Does not print compilation progress in percentage in the browser. + --client-reconnect [value] Tells dev-server the number of times it should try to reconnect the client. + --no-client-reconnect Tells dev-server to not to try to reconnect the client. + --client-web-socket-transport Allows to set custom web socket transport to communicate with dev server. + --client-web-socket-url Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to). + --client-web-socket-url-hostname Tells clients connected to devServer to use the provided hostname. + --client-web-socket-url-pathname Tells clients connected to devServer to use the provided path to connect. + --client-web-socket-url-password Tells clients connected to devServer to use the provided password to authenticate. + --client-web-socket-url-port Tells clients connected to devServer to use the provided port. + --client-web-socket-url-protocol Tells clients connected to devServer to use the provided protocol. + --client-web-socket-url-username Tells clients connected to devServer to use the provided username to authenticate. + --compress Enables gzip compression for everything served. + --no-compress Disables gzip compression for everything served. + --history-api-fallback Allows to proxy requests through a specified index page (by default 'index.html'), useful for Single Page Applications that utilise the HTML5 History API. + --no-history-api-fallback Disallows to proxy requests through a specified index page. + --host Allows to specify a hostname to use. + --hot [value] Enables Hot Module Replacement. + --no-hot Disables Hot Module Replacement. + --ipc [value] Listen to a unix socket. + --live-reload Enables reload/refresh the page(s) when file changes are detected (enabled by default). + --no-live-reload Disables reload/refresh the page(s) when file changes are detected (enabled by default). + --open [value...] Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser). + --no-open Does not open the default browser. + --open-target Opens specified page in browser. + --open-app-name Open specified browser. + --open-reset Clear all items provided in 'open' configuration. Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser). + --open-target-reset Clear all items provided in 'open.target' configuration. Opens specified page in browser. + --open-app-name-reset Clear all items provided in 'open.app.name' configuration. Open specified browser. + --port Allows to specify a port to use. + --server-type Allows to set server and options (by default 'http'). + --server-options-passphrase Passphrase for a pfx file. + --server-options-request-cert Request for an SSL certificate. + --no-server-options-request-cert Does not request for an SSL certificate. + --server-options-ca Path to an SSL CA certificate or content of an SSL CA certificate. + --server-options-ca-reset Clear all items provided in 'server.options.ca' configuration. Path to an SSL CA certificate or content of an SSL CA certificate. + --server-options-cert Path to an SSL certificate or content of an SSL certificate. + --server-options-cert-reset Clear all items provided in 'server.options.cert' configuration. Path to an SSL certificate or content of an SSL certificate. + --server-options-crl Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists). + --server-options-crl-reset Clear all items provided in 'server.options.crl' configuration. Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists). + --server-options-key Path to an SSL key or content of an SSL key. + --server-options-key-reset Clear all items provided in 'server.options.key' configuration. Path to an SSL key or content of an SSL key. + --server-options-pfx Path to an SSL pfx file or content of an SSL pfx file. + --server-options-pfx-reset Clear all items provided in 'server.options.pfx' configuration. Path to an SSL pfx file or content of an SSL pfx file. + --static [value...] Allows to configure options for serving static files from directory (by default 'public' directory). + --no-static Disallows to configure options for serving static files from directory. + --static-directory Directory for static contents. + --static-public-path The static files will be available in the browser under this public path. + --static-serve-index Tells dev server to use serveIndex middleware when enabled. + --no-static-serve-index Does not tell dev server to use serveIndex middleware. + --static-watch Watches for files in static content directory. + --no-static-watch Does not watch for files in static content directory. + --static-reset Clear all items provided in 'static' configuration. Allows to configure options for serving static files from directory (by default 'public' directory). + --static-public-path-reset Clear all items provided in 'static.publicPath' configuration. The static files will be available in the browser under this public path. + --watch-files Allows to configure list of globs/directories/files to watch for file changes. + --watch-files-reset Clear all items provided in 'watchFiles' configuration. Allows to configure list of globs/directories/files to watch for file changes. + --no-web-socket-server Disallows to set web socket server and options. + --web-socket-server-type Allows to set web socket server and options (by default 'ws'). + +Global options: + --color Enable colors on console. + --no-color Disable colors on console. + -v, --version Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands. + -h, --help [verbose] Display help for commands and options. + +To see list of all supported commands and options run 'webpack --help=verbose'. + +Webpack documentation: https://webpack.js.org/. +CLI documentation: https://webpack.js.org/api/cli/. +Made with ♥ by the webpack team. +``` diff --git a/jest.config.js b/jest.config.js index ca5dd813a05..c855f14a234 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,13 +1,4 @@ -const { cli } = require("webpack"); - -// Ignore core-flags test for webpack@4 -const ignorePattern = - typeof cli !== "undefined" - ? ["/node_modules/"] - : ["/node_modules/", "/test/build/core-flags"]; - module.exports = { - testPathIgnorePatterns: ignorePattern, testEnvironment: "node", collectCoverage: true, coverageDirectory: ".nyc_output", diff --git a/lerna.json b/lerna.json index 11f95cf10fc..54f2954de52 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,6 @@ "packages": ["packages/*"], "version": "independent", "npmClient": "yarn", - "useWorkspaces": true, "command": { "version": { "message": "chore(release): publish new version", diff --git a/lint-staged.config.js b/lint-staged.config.js index 61861fac2b3..9b5efffdb9e 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,4 +1,4 @@ module.exports = { - "*": ["prettier --write --ignore-unknown", "cspell --no-must-find-files"], + "*": ["prettier --cache --write --ignore-unknown", "cspell --cache --no-must-find-files"], "*.{js,ts}": ["eslint --cache --fix"], }; diff --git a/package.json b/package.json index 7152304780e..19fe0dab938 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://opencollective.com/webpack" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "keywords": [ "webpack", @@ -32,9 +32,9 @@ "build": "tsc --build", "build:ci": "tsc --build", "watch": "tsc --build --watch", - "lint:prettier": "prettier --list-different .", + "lint:prettier": "prettier --cache --list-different .", "lint:eslint": "eslint --cache --ext .js --ext .ts .", - "lint:spelling": "cspell \"**/*.*\"", + "lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"", "lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:spelling", "fix": "yarn lint:eslint --fix && yarn lint:prettier --write", "pretest": "yarn build && yarn lint", @@ -47,54 +47,52 @@ "test:watch": "jest test/ packages/ --watch", "publish:monorepo": "yarn build && lerna version && lerna publish from-git", "update:docs": "node ./scripts/update-docs", - "prepare": "husky install" + "prepare": "husky" }, "peerDependencies": { "webpack": "5.x.x" }, "devDependencies": { - "@babel/core": "^7.22.1", - "@babel/preset-env": "^7.22.4", + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.2", "@babel/register": "^7.15.8", - "@commitlint/cli": "^17.1.2", - "@commitlint/config-conventional": "^17.1.0", - "@types/jest": "^29.4.0", - "@types/node": "^18.11.9", + "@commitlint/cli": "^19.4.0", + "@commitlint/config-conventional": "^19.2.2", + "@types/jest": "^29.5.13", + "@types/node": "^22.5.5", "@types/rechoir": "^0.6.1", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", "coffeescript": "^2.7.0", "colorette": "^2.0.16", "concat-stream": "^2.0.0", - "cspell": "^6.12.0", - "css-loader": "^6.7.1", - "del-cli": "^5.0.0", - "eslint": "^8.33.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-node": "^11.1.0", + "cspell": "^8.3.2", + "css-loader": "^7.1.2", + "del-cli": "^6.0.0", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-n": "^17.10.2", "execa": "^5.0.0", "get-port": "^5.1.1", - "husky": "^8.0.1", - "internal-ip": "^6.2.0", + "husky": "^9.1.4", "jest": "^29.4.1", "jest-watch-typeahead": "^2.2.2", - "lerna": "^6.0.1", - "lint-staged": "^13.0.3", + "lerna": "^8.1.8", + "lint-staged": "^15.2.9", "mini-css-extract-plugin": "^2.6.1", - "nyc": "^15.1.0", - "prettier": "^2.7.0", - "readable-stream": "^3.6.0", - "rimraf": "^3.0.2", + "nyc": "^17.1.0", + "prettier": "^3.3.3", + "readable-stream": "^4.5.2", "sass": "^1.54.9", - "sass-loader": "^13.0.2", + "sass-loader": "^16.0.2", "strip-ansi": "^6.0.1", - "style-loader": "^3.3.1", + "style-loader": "^4.0.0", "ts-jest": "^29.0.1", "ts-loader": "^9.3.1", "ts-node": "^10.9.1", "typescript": "^5.0.4", - "webpack": "^5.86.0", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.5.0", - "webpack-dev-server": "^4.8.1" + "webpack-dev-server": "^5.1.0" } } diff --git a/packages/README.md b/packages/README.md index 185874c38f4..f09c39d2665 100644 --- a/packages/README.md +++ b/packages/README.md @@ -14,20 +14,14 @@ This folder is the collection of those packages. ## Packages 1. [configtest](https://github.com/webpack/webpack-cli/tree/master/packages/configtest) -2. [generators](https://github.com/webpack/webpack-cli/tree/master/packages/generators) +2. [create-webpack-app](https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app) 3. [info](https://github.com/webpack/webpack-cli/tree/master/packages/info) 4. [serve](https://github.com/webpack/webpack-cli/tree/master/packages/serve) 5. [webpack-cli](https://github.com/webpack/webpack-cli/tree/master/packages/webpack-cli) ## Generic Installation -1. Standalone installation of packages - -```shell -npm install @webpack-cli/ -``` - -2. Installation of respective `package` with `webpack-cli` [Recommended] +Standalone installation of packages (except `create-webpack-app`, it is a self-sufficient package) ```shell npm install webpack-cli @webpack-cli/ diff --git a/packages/configtest/CHANGELOG.md b/packages/configtest/CHANGELOG.md index 66532000907..994ccfb93bf 100644 --- a/packages/configtest/CHANGELOG.md +++ b/packages/configtest/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.0.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/configtest@2.1.1...@webpack-cli/configtest@3.0.0) (2024-12-19) + +**Note:** Version bump only for package @webpack-cli/configtest + ## [2.1.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/configtest@2.1.0...@webpack-cli/configtest@2.1.1) (2023-06-04) **Note:** Version bump only for package @webpack-cli/configtest diff --git a/packages/configtest/package.json b/packages/configtest/package.json index e65cfe1d735..d787f7f0304 100644 --- a/packages/configtest/package.json +++ b/packages/configtest/package.json @@ -1,12 +1,12 @@ { "name": "@webpack-cli/configtest", - "version": "2.1.1", + "version": "3.0.0", "description": "Validate a webpack configuration.", "main": "lib/index.js", "types": "lib/index.d.ts", "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "publishConfig": { "access": "public" diff --git a/packages/create-webpack-app/CHANGELOG.md b/packages/create-webpack-app/CHANGELOG.md new file mode 100644 index 00000000000..723a0382377 --- /dev/null +++ b/packages/create-webpack-app/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 1.0.0 (2024-12-19) + +### Features + +- init `create-webpack-app` package ([2739cea](https://github.com/webpack-cli/create-webpack-app/commit/2739cea843334e44e3ed8822d39005645a82a280)) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md new file mode 100644 index 00000000000..6654d257426 --- /dev/null +++ b/packages/create-webpack-app/README.md @@ -0,0 +1,70 @@ + + +# create-webpack-app CLI + +## About + +- `create-webpack-app` is a cli tool that enables developers to scaffold a new webpack project quickly. It provides developers with a flexible set of commands to increase speed when setting up a custom webpack project. webpack CLI addresses these needs by providing tools to improve the setup of custom webpack configuration. +- It also supports several front-end frameworks and libraries like React, Vue, Svelte and pure project. +- Webpack Loader and Plugin scaffolding is also supported. + +## Supported arguments and commands + +### Usage + +```bash +npx create-webpack-app [command] [options] +``` + +### Commands + +- `init` (also used by default when nothing specified) - project generator +- `loader` - loader generator +- `plugin` - plugin generator + +### CLI options + +**To generate default template** + +```bash +npx create-webpack-app +``` + +**To generate with default answers** + +```bash +npx create-webpack-app -f +``` + +or + +```bash +npx create-webpack-app --force +``` + +**To generate in a specified path** + +```bash +npx create-webpack-app [generation-path] +``` + +**To generate a project according to a template** + +```bash +npx create-webpack-app --template +``` + +Available templates: + +- `default` (used by default when nothing specified) - generate a basic template for JS(TS)/CSS/HTML without any frameworks +- [`react`](https://react.dev/) +- [`vue`](https://vuejs.org/) +- [`svelte`](https://svelte.dev/) + +Available templates for `loader` and `plugin` generators: + +- `default` (used by default when nothing specified) - generate bootstrap code diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js new file mode 100755 index 00000000000..aaefc07b7d0 --- /dev/null +++ b/packages/create-webpack-app/bin/cli.js @@ -0,0 +1,4 @@ +#!/usr/bin/env node + +//eslint-disable-next-line +import * as cli from "../lib/index.js"; diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json new file mode 100644 index 00000000000..1e6d8e514d0 --- /dev/null +++ b/packages/create-webpack-app/package.json @@ -0,0 +1,54 @@ +{ + "name": "create-webpack-app", + "version": "1.0.0", + "description": "CLI for scaffolding webpack projects using default config, framework templates, loader or plugins templates", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/webpack-cli/create-webpack-app.git" + }, + "homepage": "https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app", + "bugs": "https://github.com/webpack/webpack-cli/issues", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "bin": { + "create-webpack-app": "./bin/cli.js" + }, + "type": "module", + "main": "./lib/index.js", + "scripts": { + "build": "tsc --build", + "watch": "tsc --watch" + }, + "engines": { + "node": ">=18.12.0" + }, + "keywords": [ + "webpack", + "cli", + "scaffolding", + "module", + "bundler", + "web", + "frameworks" + ], + "files": [ + "bin", + "lib", + "!**/*__tests__" + ], + "dependencies": { + "@inquirer/prompts": "^7.2.0", + "colorette": "^2.0.20", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "ejs": "^3.1.10", + "node-plop": "^0.32.0" + }, + "devDependencies": { + "@types/cross-spawn": "^6.0.6", + "@types/ejs": "^3.1.5" + } +} diff --git a/packages/create-webpack-app/src/generators/init/default.ts b/packages/create-webpack-app/src/generators/init/default.ts new file mode 100644 index 00000000000..08c8491a8bb --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/default.ts @@ -0,0 +1,205 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = ["webpack", "webpack-cli"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + // Define a custom action for installing packages + + // Define a base generator for the project structure + plop.setGenerator("init-default", { + description: "Create a basic webpack project", + prompts: [ + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["none", "ES6", "Typescript"], + default: "none", + }, + { + type: "confirm", + name: "devServer", + message: "Would you like to use Webpack Dev server?", + default: true, + }, + { + type: "confirm", + name: "htmlWebpackPlugin", + message: "Do you want to simplify the creation of HTML files for your bundle?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "none", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = []; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader"); + break; + } + + if (answers.devServer) { + devDependencies.push("webpack-dev-server"); + } + + if (answers.htmlWebpackPlugin) { + devDependencies.push("html-webpack-plugin", "html-loader"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + if (answers.cssType !== "none") { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files: Array = [ + { filePath: "./index.html", fileType: "text" }, + { filePath: "webpack.config.js", fileType: "text" }, + { filePath: "package.json", fileType: "text" }, + { filePath: "README.md", fileType: "text" }, + ]; + + switch (answers.langType) { + case "Typescript": + answers.entryPoint = "./src/index.ts"; + files.push( + { filePath: "tsconfig.json", fileType: "text" }, + { filePath: answers.entryPoint as string, fileType: "text" }, + ); + break; + case "ES6": + answers.entryPoint = "./src/index.js"; + files.push( + { filePath: "babel.config.json", fileType: "text" }, + { filePath: answers.entryPoint as string, fileType: "text" }, + ); + break; + default: + answers.entryPoint = "./src/index.js"; + files.push({ filePath: answers.entryPoint as string, fileType: "text" }); + break; + } + + if (answers.isPostCSS) { + files.push({ filePath: "postcss.config.js", fileType: "text" }); + } + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/init/default", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/react.ts b/packages/create-webpack-app/src/generators/init/react.ts new file mode 100644 index 00000000000..9984de2d9d0 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/react.ts @@ -0,0 +1,224 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, resolve, join } from "path"; +import { fileURLToPath } from "url"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "react@18", + "react-dom@18", + "webpack-dev-server", + "html-webpack-plugin", + "react-router-dom", + "@types/react-router-dom", + ]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + // Define a custom action for installing packages + + // Define a base generator for the project structure + plop.setGenerator("init-react", { + description: "Create a basic React-webpack project", + prompts: [ + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "useReactState", + message: "Do you want to use React State in your project?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "CSS only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + switch (answers.langType) { + case "ES6": + devDependencies.push( + "babel-loader", + "@babel/core", + "@babel/preset-env", + "@babel/preset-react", + ); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader", "@types/react", "@types/react-dom"); + break; + } + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + const files: Array = [ + { filePath: "./index.html", fileType: "text" }, + { filePath: "webpack.config.js", fileType: "text" }, + { filePath: "package.json", fileType: "text" }, + { filePath: "README.md", fileType: "text" }, + { filePath: "./src/assets/webpack.png", fileType: "binary" }, + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/index.tsx"; + files.push( + { filePath: "tsconfig.json", fileType: "text" }, + { filePath: "index.d.ts", fileType: "text" }, + { filePath: "./src/App.tsx", fileType: "text" }, + { filePath: "./src/components/About.tsx", fileType: "text" }, + { filePath: "./src/components/Home.tsx", fileType: "text" }, + { filePath: "./src/components/Navbar.tsx", fileType: "text" }, + { filePath: "./src/router/index.tsx", fileType: "text" }, + { filePath: answers.entry as string, fileType: "text" }, + ); + break; + case "ES6": + answers.entry = "./src/index.jsx"; + files.push( + { filePath: "./src/App.jsx", fileType: "text" }, + { filePath: "./src/components/About.jsx", fileType: "text" }, + { filePath: "./src/components/Home.jsx", fileType: "text" }, + { filePath: "./src/components/Navbar.jsx", fileType: "text" }, + { filePath: "./src/router/index.jsx", fileType: "text" }, + { filePath: answers.entry as string, fileType: "text" }, + ); + break; + } + + switch (answers.cssType) { + case "CSS only": + files.push({ filePath: "./src/styles/global.css", fileType: "text" }); + break; + case "SASS": + files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); + break; + case "LESS": + files.push({ filePath: "./src/styles/global.less", fileType: "text" }); + break; + case "Stylus": + files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); + break; + } + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/init/react", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/svelte.ts b/packages/create-webpack-app/src/generators/init/svelte.ts new file mode 100644 index 00000000000..1c836156f3d --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/svelte.ts @@ -0,0 +1,212 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "svelte", + "svelte-loader", + "webpack-dev-server", + "html-webpack-plugin", + ]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the Svelte project structure + plop.setGenerator("init-svelte", { + description: "Create a basic Svelte-webpack project", + prompts: [ + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "CSS only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader", "@tsconfig/svelte"); + break; + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files: Array = [ + { filePath: "./index.html", fileType: "text" }, + { filePath: "./src/assets/webpack.png", fileType: "binary" }, + { filePath: "webpack.config.js", fileType: "text" }, + { filePath: "package.json", fileType: "text" }, + { filePath: "README.md", fileType: "text" }, + { filePath: "./src/components/HelloWorld.svelte", fileType: "text" }, + { filePath: "./src/App.svelte", fileType: "text" }, + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/main.ts"; + files.push( + { filePath: "tsconfig.json", fileType: "text" }, + { filePath: "./src/index.d.ts", fileType: "text" }, + { filePath: answers.entry as string, fileType: "text" }, + ); + break; + case "ES6": + answers.entry = "./src/main.js"; + files.push({ filePath: answers.entry as string, fileType: "text" }); + break; + } + + if (answers.langType === "Typescript") { + files.push({ filePath: "./src/store/index.ts", fileType: "text" }); + } else { + files.push({ filePath: "./src/store/index.js", fileType: "text" }); + } + + switch (answers.cssType) { + case "CSS only": + files.push({ filePath: "./src/styles/global.css", fileType: "text" }); + break; + case "SASS": + files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); + break; + case "LESS": + files.push({ filePath: "./src/styles/global.less", fileType: "text" }); + break; + case "Stylus": + files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); + break; + } + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/init/svelte", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/vue.ts b/packages/create-webpack-app/src/generators/init/vue.ts new file mode 100644 index 00000000000..fb90403e9c3 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/vue.ts @@ -0,0 +1,234 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "vue@3", + "webpack-dev-server", + "html-webpack-plugin", + "vue-loader@next", + "@vue/compiler-sfc", + "vue-router@4", + ]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the Vue 3 project structure + plop.setGenerator("init-vue", { + description: "Create a basic Vue-webpack project", + prompts: [ + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "useVueStore", + message: "Do you want to use Pinia for state management?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "CSS only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader"); + break; + } + + if (answers.useVueStore) { + devDependencies.push("pinia"); + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("vue-style-loader", "style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files: Array = [ + { filePath: "./index.html", fileType: "text" }, + { filePath: "./src/assets/webpack.png", fileType: "binary" }, + { filePath: "webpack.config.js", fileType: "text" }, + { filePath: "package.json", fileType: "text" }, + { filePath: "README.md", fileType: "text" }, + { filePath: "./src/App.vue", fileType: "text" }, + { filePath: "./src/components/Home.vue", fileType: "text" }, + { filePath: "./src/components/About.vue", fileType: "text" }, + { filePath: "./src/components/Layout.vue", fileType: "text" }, + { filePath: "./src/components/Navbar.vue", fileType: "text" }, + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/main.ts"; + files.push( + { filePath: "tsconfig.json", fileType: "text" }, + { filePath: answers.entry as string, fileType: "text" }, + ); + break; + case "ES6": + answers.entry = "./src/main.js"; + files.push({ filePath: answers.entry as string, fileType: "text" }); + break; + } + + if (answers.langType === "Typescript") { + files.push({ filePath: "./src/router/index.ts", fileType: "text" }); + } else { + files.push({ filePath: "./src/router/index.js", fileType: "text" }); + } + + if (answers.useVueStore) { + if (answers.langType === "Typescript") { + files.push({ filePath: "./src/store/index.ts", fileType: "text" }); + } else { + files.push({ filePath: "./src/store/index.js", fileType: "text" }); + } + } + + switch (answers.cssType) { + case "CSS only": + files.push({ filePath: "./src/styles/global.css", fileType: "text" }); + break; + case "SASS": + files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); + break; + case "LESS": + files.push({ filePath: "./src/styles/global.less", fileType: "text" }); + break; + case "Stylus": + files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); + break; + } + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/init/vue", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/loader/default.ts b/packages/create-webpack-app/src/generators/loader/default.ts new file mode 100644 index 00000000000..83f2f1dc5fc --- /dev/null +++ b/packages/create-webpack-app/src/generators/loader/default.ts @@ -0,0 +1,99 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; +import { logger } from "../../utils/logger.js"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = ["webpack-defaults"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + // custom helper function + plop.setHelper("makeLoaderName", (name: string) => { + name = plop.getHelper("kebabCase")(name); + + if (!/loader$/.test(name)) { + name += "-loader"; + } + return name; + }); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the project structure + plop.setGenerator("loader-default", { + description: "Create a basic webpack loader.", + prompts: [ + { + type: "input", + name: "name", + message: "Loader name?", + default: "my-loader", + validate: (str: string): boolean => str.length > 0, + }, + { + type: "list", + name: "packageManager", + message: "Pick a package manager:", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = []; + answers.projectPath = join(answers.projectPath, answers.name); + + logger.error(` + Your project must be inside a folder named ${answers.name} + I will create this folder for you. + `); + + const files: Array = [ + { filePath: "./package.json", fileType: "text" }, + { filePath: "./examples/simple/src/index.js", fileType: "text" }, + { filePath: "./examples/simple/src/lazy-module.js", fileType: "text" }, + { filePath: "./examples/simple/src/static-esm-module.js", fileType: "text" }, + { filePath: "./examples/simple/webpack.config.js", fileType: "text" }, + { filePath: "./src/cjs.js", fileType: "text" }, + { filePath: "./test/fixtures/simple-file.js", fileType: "text" }, + { filePath: "./test/functional.test.js", fileType: "text" }, + { filePath: "./test/test-utils.js", fileType: "text" }, + { filePath: "./test/unit.test.js", fileType: "text" }, + { filePath: "./src/index.js", fileType: "text" }, + ]; + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/loader/default", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/plugin/default.ts b/packages/create-webpack-app/src/generators/plugin/default.ts new file mode 100644 index 00000000000..e03f7a9b2a7 --- /dev/null +++ b/packages/create-webpack-app/src/generators/plugin/default.ts @@ -0,0 +1,91 @@ +import { type Answers, type ActionType, type FileRecord } from "../../types"; +import { type NodePlopAPI, type DynamicActionsFunction } from "node-plop"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; +import { logger } from "../../utils/logger.js"; + +export default async function (plop: NodePlopAPI) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // dependencies to be installed + const devDependencies: Array = ["webpack-defaults"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + await plop.load("../../utils/fileGenerator.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the project structure + plop.setGenerator("plugin-default", { + description: "Create a basic webpack plugin.", + prompts: [ + { + type: "input", + name: "name", + message: "Plugin name?", + default: "my-webpack-plugin", + filter: (input) => plop.getHelper("kebabCase")(input), + validate: (str: string): boolean => str.length > 0, + }, + { + type: "list", + name: "packageManager", + message: "Pick a package manager:", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = []; + answers.projectPath = join(answers.projectPath, answers.name); + + logger.error(` + Your project must be inside a folder named ${answers.name} + I will create this folder for you. + `); + + answers.pluginIdentifier = plop.getHelper("pascalCase")(answers.name); + + const files: Array = [ + { filePath: "./package.json", fileType: "text" }, + { filePath: "./examples/simple/src/index.js", fileType: "text" }, + { filePath: "./examples/simple/src/lazy-module.js", fileType: "text" }, + { filePath: "./examples/simple/src/static-esm-module.js", fileType: "text" }, + { filePath: "./examples/simple/webpack.config.js", fileType: "text" }, + { filePath: "./src/cjs.js", fileType: "text" }, + { filePath: "./test/fixtures/simple-file.js", fileType: "text" }, + { filePath: "./test/functional.test.js", fileType: "text" }, + { filePath: "./test/test-utils.js", fileType: "text" }, + { filePath: "./src/index.js", fileType: "text" }, + ]; + + for (const file of files) { + actions.push({ + type: "fileGenerator", + path: join(answers.projectPath, file.filePath), + templateFile: join( + plop.getPlopfilePath(), + "../templates/plugin/default", + `${file.filePath}.tpl`, + ), + fileType: file.fileType, + data: answers, + }); + } + + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts new file mode 100644 index 00000000000..a9a752c9720 --- /dev/null +++ b/packages/create-webpack-app/src/index.ts @@ -0,0 +1,212 @@ +import { Command } from "commander"; +import { resolve, dirname } from "path"; +import { select } from "@inquirer/prompts"; +import nodePlop, { type PlopGenerator } from "node-plop"; +import { fileURLToPath } from "url"; + +import { onSuccessHandler, onFailureHandler, logger } from "./utils/logger.js"; +import { type Answers, type InitOptions, type LoaderOptions, type PluginOptions } from "./types"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const program = new Command(); + +const plop = await nodePlop(resolve(__dirname, "./plopfile.js")); + +const baseAnswers: Answers = { + projectPath: process.cwd(), + langType: "none", + devServer: true, + htmlWebpackPlugin: true, + workboxWebpackPlugin: true, + cssType: "none", + isCSS: false, + isPostCSS: false, + extractPlugin: "No", + packageManager: "npm", +}; +const initValues: Record = { + default: { + ...baseAnswers, + }, + react: { + ...baseAnswers, + langType: "ES6", + useReactState: true, + cssType: "CSS only", + }, + vue: { + ...baseAnswers, + langType: "ES6", + useVueStore: true, + cssType: "CSS only", + }, + svelte: { + ...baseAnswers, + langType: "ES6", + cssType: "CSS only", + }, +}; + +const initGenerators: Record = { + default: plop.getGenerator("init-default"), + react: plop.getGenerator("init-react"), + vue: plop.getGenerator("init-vue"), + svelte: plop.getGenerator("init-svelte"), +}; +const loaderGenerators: Record = { + default: plop.getGenerator("loader-default"), +}; + +const pluginGenerators: Record = { + default: plop.getGenerator("plugin-default"), +}; + +program + .usage("[command] [options]") + .helpOption("-h, --help", "Display help for command") + .description("A CLI tool to generate a Webpack project"); + +program + .command("init", { isDefault: true }) + .aliases(["i", "n", "c", "create", "new"]) + .description("Initialize a new Webpack project") + .argument("[projectPath]", "Path to create the project") + .option("-f, --force", "Skip the prompt and use the default values", false) + .option("-t --template