Skip to content

Package that provides development dependencies for other web projects (both browser- and Node.js-based) to consume.

License

Notifications You must be signed in to change notification settings

dustin-ruetz/web-devdeps

Repository files navigation

web-devdeps

Package that provides development dependencies for other web projects (both browser- and Node.js-based) to consume.

Goals

TL;DR version: make web dev dependencies DRY, like a cocktail in a desert 🍸 🏜️

  1. 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 devDependencies that aren't typically imported in the src/ files of an application.
  2. 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.js files over-and-over again.
  3. Provide standardized scripts that automatically add useful arguments and determine the path to their configuration files.
  4. Provide make*Config functions that allow for the standardized configurations to be extended and customized to suit the specific requirements of the consuming project.

Features and Purpose

web-devdeps provides devDependencies and configuration files for:

  • Automated dependency updates using renovate.
  • Building and typechecking using typescript.
  • Formatting using prettier and its plugins.
  • Linting using eslint and its plugins (including typescript-eslint) for JavaScript and TypeScript.
  • Linting using stylelint for CSS/SCSS/JSX/TSX (the latter two file types being relevant for "CSS-in-JS" solutions like styled-components).
  • Releasing using semantic-release.
  • Testing using jest for unit and integration tests.
  • Validating commit messages using commitlint.
  • Validating the codebase prior to any changes being committed using lint-staged and husky Git hooks.

Additional details:

  • The idea for this package was inspired by Kent C. Dodds' kcd-scripts CLI 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 tsc to compile the src/ directory's *.ts files to the lib/ directory's *.js files, and 2) pointing all configuration paths to the compiled lib/config/*.js files.

Usage and Development

Prerequisite: The following instructions assume that Node.js and pnpm are installed and that VS Code is being used as the IDE.

Usage in Projects

Using the web-devdeps package requires consuming projects to:

  1. Use pnpm as a package manager. (Note that this is not a hard requirement; npm should still work despite its more limited feature set.)

  2. Have a flattened/hoisted node_modules directory. pnpm can use this installation method via the nodeLinker setting. (Note that this is npm's default behavior.)

  3. 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

Starting a New Project

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.

Adding to an Existing Project

Details
  1. 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-msg

B. .githooks/pre-commit

#!/usr/bin/env sh
./node_modules/web-devdeps/.githooks/_/pre-commit

C. .githooks/pre-push

#!/usr/bin/env sh
./node_modules/web-devdeps/.githooks/_/pre-push
  1. Create the .vscode/ directory and populate it with the settings.json file:
{
	"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"]
}
  1. (optional) If it's a frontend project that uses TypeScript, create the config/ directory and populate it with the jest.setupFilesAfterEnv.ts file:
import "@testing-library/jest-dom";
  1. Create the .node-version file:
22
  1. Install the package as a development dependency:
pnpm add --save-dev --save-exact web-devdeps
  1. Go through the package.json file and add the following scripts, making modifications as needed (i.e. a non-TypeScript project has no use for the check.types script, a non-frontend project has no use for the lint.styles scripts, a React project that doesn't use a CSS-in-JS runtime library like styled-components doesn't need to check .jsx or .tsx files 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"
	}
}
  1. Create the pnpm-workspace.yaml file:
nodeLinker: hoisted
  1. Create the renovate.json file:
{
	"$schema": "https://docs.renovatebot.com/renovate-schema.json",
	"extends": ["github>dustin-ruetz/web-devdeps:renovate.json"]
}
  1. (optional) If it's a TypeScript project, create the tsconfig.json file and make modifications as needed:
{
	"extends": "./node_modules/web-devdeps/tsconfig.json",
	"include": ["./config/", "./src/"],
	"exclude": ["..."],
	"compilerOptions": {
		"outDir": "..."
	}
}
  1. Try running the validation script:
pnpm validate
  1. Remove any previous development dependencies and configuration files that are no longer needed now that they're being provided by the web-devdeps package:
pnpm remove --save-dev eslint jest prettier stylelint # (etc.)

Customizing Configurations

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
  1. Create the config/eslint.config.js file (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",
		},
	},
];
  1. Modify the following two files so that they point to the custom config/eslint.config.js file:

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"
	}
}
  1. In VS Code, restart the ESLint server or reload the window.

  2. Open a JS or TS file, add a console.log() statement to it, then verify that both the ESLint IDE extension and the lint.js-ts script report the file as having the no-console error.

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.

Local Development

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:

  1. Clone the repository and cd into it.
  2. Execute the pnpm run init command in order to:
    1. Configure Git hooks;
    2. Install dependencies; and
    3. Validate (πŸ› οΈ build, 🧐 check, πŸ§ͺ test) the codebase.
  3. (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 validate

Note 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.validate

Publishing an rc- Version

To manually publish a release candidate version to the npm registry for testing purposes:

  1. Run git checkout -b rc-FEATURE, make/add/commit changes, then git push -u origin HEAD the branch up to GitHub.
  2. Create a PR from the rc-FEATURE branch and confirm that the automated checks all pass.
  3. Go to the /actions/workflows/release.yaml GitHub page.
  4. Click the "Run workflow" menu button.
  5. Click the "Use workflow from Branch: main" button, then select the rc-FEATURE branch from Step 1.
  6. Click the green "Run workflow" button.
  7. After the release run is complete, confirm that the rc version was successfully published:
    1. To the npm registry by running the pnpm view web-devdeps versions command.
    2. On GitHub by going to the GitHub Releases page.
  8. Run pnpm install --save-dev --save-exact web-devdeps@VERSION to install it in a consuming repo for local testing.

After testing is complete:

  1. Run npm login and authenticate via the browser.
  2. Run npm unpublish web-devdeps@VERSION to unpublish the rc version.
  3. Confirm that the rc version was successfully unpublished from the npm registry by running the pnpm view web-devdeps versions command.
  4. Delete the rc version entry from the GitHub Releases page.

About

Package that provides development dependencies for other web projects (both browser- and Node.js-based) to consume.

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •