Package that provides development dependencies for other web projects (both browser- and Node.js-based) to consume.
TL;DR version: make web dev dependencies DRY, like a cocktail in a desert πΈ ποΈ
- Avoid having to repeatedly install the same development dependencies in web projects that all use the same foundational tooling. With a small number of exceptions, these are generally CLI-based
devDependenciesthat aren't typicallyimported in thesrc/files of an application. - Provide standardized configuration files that work with both CLI programs and IDE extensions while also preventing the repetitive cluttering up of repository roots with the same boilerplate
*.config.jsfiles over-and-over again. - Provide standardized scripts that automatically add useful arguments and determine the path to their configuration files.
- Provide
make*Configfunctions that allow for the standardized configurations to be extended and customized to suit the specific requirements of the consuming project.
web-devdeps provides devDependencies and configuration files for:
- Automated dependency updates using
renovate. - Building and typechecking using
typescript. - Formatting using
prettierand its plugins. - Linting using
eslintand its plugins (includingtypescript-eslint) for JavaScript and TypeScript. - Linting using
stylelintfor CSS/SCSS/JSX/TSX (the latter two file types being relevant for "CSS-in-JS" solutions likestyled-components). - Releasing using
semantic-release. - Testing using
jestfor unit and integration tests. - Validating commit messages using
commitlint. - Validating the codebase prior to any changes being committed using
lint-stagedandhuskyGit hooks.
Additional details:
- The idea for this package was inspired by Kent C. Dodds'
kcd-scriptsCLI toolbox, with a key difference being the preference for a fully-tested TypeScript codebase using modern ES Module syntax. - This project bootstraps and dogfoods its own configuration files by 1) using
tscto compile thesrc/directory's*.tsfiles to thelib/directory's*.jsfiles, and 2) pointing all configuration paths to the compiledlib/config/*.jsfiles.
Prerequisite: The following instructions assume that Node.js and pnpm are installed and that VS Code is being used as the IDE.
Using the web-devdeps package requires consuming projects to:
-
Use pnpm as a package manager. (Note that this is not a hard requirement; npm should still work despite its more limited feature set.)
-
Have a flattened/hoisted
node_modulesdirectory. pnpm can use this installation method via the nodeLinker setting. (Note that this is npm's default behavior.) -
Adhere to the following commonly-used folder/file structure:
// Created using the `Shinotatwu-DS.file-tree-generator` extension for Visual Studio Code.
π the-project
β£ π node_modules
β β π web-devdeps
β β β π lib/config
β β β β π *.config.js
β β β π tsconfig.json
β π package.json
β π tsconfig.json
Details
π‘ Important: Replace the repo-name placeholder in the commands below with the actual name of the repository.
# 1. Create and initialize a new Git repository:
mkdir repo-name && cd repo-name && git init
# 2. Use `pnpx` to execute this package's `init-repo` script to write the initial files
# needed for web-based projects when creating a new Git repository.
#
# **Tip:** Pass the `--help` flag to print the documentation for the command's flags.
pnpx web-devdeps init-repo repo-name
# 3. Configure the repo to use the Git hooks files in the written `.githooks/` directory
# and modify the permissions to make all files executable:
git config core.hooksPath ./.githooks/ && chmod u+x ./.githooks/*
# 4. Install the `web-devdeps` version listed in the written `package.json` file:
pnpm install
# 5. (optional) Automatically fix the formatting for all of the written files:
pnpm fix.format
# 6. Note how the key files (`package.json`, `README.md`, `tsconfig.json`, etc.)
# and folders (`.githooks/`, `.vscode/`) have all been initialized. Open each
# written file and make updates as needed, then add and commit everything:
git add --all && git commit -m "feat: initial commit"
# 7. Verify that the Git hooks ran automatically and the relevant checks
# (formatting, linting, testing, typechecking, etc.) were successful.Details
- Create the
.githooks/directory and populate it with the following three files:
A. .githooks/commit-msg
#!/usr/bin/env sh
./node_modules/web-devdeps/.githooks/_/commit-msgB. .githooks/pre-commit
#!/usr/bin/env sh
./node_modules/web-devdeps/.githooks/_/pre-commitC. .githooks/pre-push
#!/usr/bin/env sh
./node_modules/web-devdeps/.githooks/_/pre-push- Create the
.vscode/directory and populate it with thesettings.jsonfile:
{
"eslint.options": {
"overrideConfigFile": "node_modules/web-devdeps/lib/config/eslint.config.js"
},
"prettier.configPath": "node_modules/web-devdeps/lib/config/prettier.config.js",
"prettier.ignorePath": ".gitignore",
// (optional) Modify or remove the Stylelint-related lines below as per the tooling needs of the project.
"stylelint.configFile": "node_modules/web-devdeps/lib/config/stylelint.config.js",
"stylelint.validate": ["css", "javascriptreact", "scss", "typescriptreact"]
}- (optional) If it's a frontend project that uses TypeScript, create the
config/directory and populate it with thejest.setupFilesAfterEnv.tsfile:
import "@testing-library/jest-dom";- Create the
.node-versionfile:
22
- Install the package as a development dependency:
pnpm add --save-dev --save-exact web-devdeps- Go through the
package.jsonfile and add the followingscripts, making modifications as needed (i.e. a non-TypeScript project has no use for thecheck.typesscript, a non-frontend project has no use for thelint.stylesscripts, a React project that doesn't use a CSS-in-JS runtime library likestyled-componentsdoesn't need to check.jsxor.tsxfiles for linting issues with the styling, etc.):
{
"scripts": {
"build": "...",
"check": "pnpm run /check./",
"check.format": "pnpm format --check ./",
"check.lint.js-ts": "pnpm lint.js-ts ./",
"check.lint.styles": "pnpm lint.styles '**/*.{css,scss,jsx,tsx}'",
"check.types": "tsc --noEmit",
"clean": "pnpm run /clean./",
"clean.caches": "pnpx jest --clear-cache && git clean --force -X ./.caches/",
"clean.deps": "git clean --force -X ./node_modules/",
"format": "web-devdeps format",
"fix": "pnpm run /fix./",
"fix.format": "pnpm format --write ./",
"fix.lint.js-ts": "pnpm lint.js-ts --fix ./",
"fix.lint.styles": "pnpm lint.styles --fix '**/*.{css,scss,jsx,tsx}'",
"githooks.commit-msg": "web-devdeps githooks.commit-msg",
"githooks.pre-commit": "web-devdeps githooks.pre-commit",
"githooks.pre-push": "pnpm validate",
"init": "git config core.hooksPath ./.githooks/ && pnpm install && pnpm validate",
"lint.js-ts": "web-devdeps lint.js-ts",
"lint.styles": "web-devdeps lint.styles",
"test.unit": "web-devdeps test.unit",
"test.unit.coverage": "pnpm test.unit --coverage",
"test.unit.coverage-watch-all": "pnpm test.unit.coverage --watch-all",
"test.unit.watch": "pnpm test.unit.coverage --watch",
"validate": "pnpm build && pnpm check && pnpm test.unit.coverage"
}
}- Create the
pnpm-workspace.yamlfile:
nodeLinker: hoisted- Create the
renovate.jsonfile:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>dustin-ruetz/web-devdeps:renovate.json"]
}- (optional) If it's a TypeScript project, create the
tsconfig.jsonfile and make modifications as needed:
{
"extends": "./node_modules/web-devdeps/tsconfig.json",
"include": ["./config/", "./src/"],
"exclude": ["..."],
"compilerOptions": {
"outDir": "..."
}
}- Try running the validation script:
pnpm validate- Remove any previous development dependencies and configuration files that are no longer needed now that they're being provided by the
web-devdepspackage:
pnpm remove --save-dev eslint jest prettier stylelint # (etc.)Similar to kcd-scripts, this web-devdeps package defaults to supplying standardized configuration files for all of the development dependency tooling that it offers. Both packages also pass along any additional flags and arguments to their respective CLI scripts.
Where it diverges from the kcd-scripts approach is that it doesn't do any automatic detection of configuration files or config overrides based on the files that are present in the repo. This difference is intentional in order to both 1) reduce code complexity, and 2) avoid any perceived attempts at "magic" (i.e. hiding key details instead of surfacing them to make it clearer as to what's happening).
So, in the scenario where a repo wants to both 1) use the web-devdeps CLI scripts, and 2) extend its built-in configurations and customize them in order to suit the project's specific requirements, this can be achieved in a very straightforward and transparent way.
By way of example, here's how this package's ESLint configuration could be extended and customized:
Details
- Create the
config/eslint.config.jsfile (note that the directory, filename and extension are all arbitrary; it can be located anywhere, it can be named anything, it can be a ".mjs" file, etc.) and customize it:
// Refer to the `https://github.com/dustin-ruetz/web-devdeps/blob/main/src/exports.ts` file
// for the full list of `make*Config` functions that this package offers.
import {makeESLintConfig} from "web-devdeps";
export default [
...(await makeESLintConfig()),
{
rules: {
// Note: This rule is configured to `"warn"` by default.
"no-console": "error",
},
},
];- Modify the following two files so that they point to the custom
config/eslint.config.jsfile:
A. .vscode/settings.json
{
"eslint.options": {
"overrideConfigFile": "config/eslint.config.js"
}
}B. package.json
{
"scripts": {
"lint.js-ts": "web-devdeps lint.js-ts --config ./config/eslint.config.js"
}
}-
In VS Code, restart the ESLint server or reload the window.
-
Open a JS or TS file, add a
console.log()statement to it, then verify that both the ESLint IDE extension and thelint.js-tsscript report the file as having theno-consoleerror.
As a final related note on providing transparency and avoiding "magic", this is also why the package's CLI scripts include their paths and flags (both the built-in ones that are automatically added, as well as any additional passed flags) in the terminal output when they're run. In this example, executing the lint.js-ts script will produce the following output:
pnpm lint.js-ts ./src/no-console.ts
> [email protected] lint.js-ts
> web-devdeps lint.js-ts --config ./config/eslint.config.js ./src/no-console.ts
π eslint command being run (as generated by ./node_modules/web-devdeps/lib/scripts/runCLI.js):
> eslint --config ./config/eslint.config.js --cache --cache-location ./.caches/.eslintcache ./src/no-console.ts
/Users/username/repos/the-project/src/no-console.ts
1:1 error Unexpected console statement no-console
β 1 problem (1 error, 0 warnings)
π eslint terminated with non-zero exit code 1.Note that fnm (Fast Node Manager) can be installed and configured to automatically switch to the Node.js version number specified in the .node-version file.
Start by initializing the repo for local development:
- Clone the repository and
cdinto it. - Execute the
pnpm run initcommand in order to:- Configure Git hooks;
- Install dependencies; and
- Validate (π οΈ build, π§ check, π§ͺ test) the codebase.
- (optional) If the repo is already opened in VS Code, reload the window.
Below is a list of the most useful scripts in alphabetical order (execute the pnpm run command to print the full list):
# Compile the codebase (from src/*.ts to lib/*.js).
pnpm build
# Check the codebase for problems (formatting, linting and typechecking).
pnpm check
# Compile the codebase and recompile the output whenever the source changes.
pnpm dev
# Check the codebase for problems and automatically fix them where possible (formatting and linting).
pnpm fix
# Run the unit tests in various modes.
pnpm test.unit
pnpm test.unit.coverage
pnpm test.unit.coverage-watch-all
pnpm test.unit.watch
# Run the full validation suite (π οΈ build, π§ check, π§ͺ test).
pnpm validateNote that act can be used to locally test the GitHub Actions workflow files located in .github/workflows/ as well:
# Simulate a dry-run release in the CI/CD environment.
pnpm github.release
# Simulate the full validation suite running in the CI/CD environment.
pnpm github.validateTo manually publish a release candidate version to the npm registry for testing purposes:
- Run
git checkout -b rc-FEATURE, make/add/commit changes, thengit push -u origin HEADthe branch up to GitHub. - Create a PR from the
rc-FEATUREbranch and confirm that the automated checks all pass. - Go to the /actions/workflows/release.yaml GitHub page.
- Click the "Run workflow" menu button.
- Click the "Use workflow from Branch: main" button, then select the
rc-FEATUREbranch from Step 1. - Click the green "Run workflow" button.
- After the
releaserun is complete, confirm that thercversion was successfully published:- To the npm registry by running the
pnpm view web-devdeps versionscommand. - On GitHub by going to the GitHub Releases page.
- To the npm registry by running the
- Run
pnpm install --save-dev --save-exact web-devdeps@VERSIONto install it in a consuming repo for local testing.
After testing is complete:
- Run
npm loginand authenticate via the browser. - Run
npm unpublish web-devdeps@VERSIONto unpublish thercversion. - Confirm that the
rcversion was successfully unpublished from the npm registry by running thepnpm view web-devdeps versionscommand. - Delete the
rcversion entry from the GitHub Releases page.