npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@42wp/dev-env

v1.0.4

Published

42WP — a CLI to spin up disposable WordPress dev environments with Docker (Traefik + MySQL + per-project containers).

Readme

42wp — 42WP dev environments

O Guia do Mochileiro do WordPress — a tiny CLI that spins up disposable WordPress dev environments with Docker, the 42WP way.

42wp runs a shared global layer (Traefik reverse proxy + MySQL + phpMyAdmin + Mailpit) and, on top of it, per-project WordPress containers reachable at http://<project>.localhost. Your current working directory is mounted as wp-content, so you develop your theme/plugin locally and WordPress runs in a container with Xdebug, Memcached and WP-CLI preinstalled.

Requirements

  • Docker with the Compose v2 plugin (docker compose)
  • Node.js >= 18.17

Install

npm install -g @42wp/dev-env

This installs the 42wp command globally.

Usage

42wp init [project]        # scaffold a .42wp-dev-env with this project's options
42wp global start          # start Traefik + MySQL + phpMyAdmin + Mailpit
42wp start <project>       # build & start a project (run from your theme/plugin repo)
42wp update <project>      # update an existing project to a newer WordPress image
42wp wp <project> <args>   # run a WP-CLI command inside the project container
42wp stop <project>        # stop a project's containers
42wp rm <project>          # remove a site (container, image, database) — keeps your repo
42wp seed <project> [n]    # generate demo content in a running project (default 200)
42wp global stop           # tear the global layer down

Run 42wp with no arguments for the full command list.

Project config (.42wp-dev-env)

Drop a .42wp-dev-env file in your project repo to save its 42wp start options, so you (and your team) can just run 42wp start with no flags. Scaffold one with 42wp init:

42wp init mysite --subdomains --vip --locale pt_BR --theme my-theme --demo-content

That writes:

project_slug=mysite
multisite=subdomain      # false | subdir | subdomain
username=admin
password=password
locale=pt_BR
default_theme=my-theme
wpvip=true
initial_content=200

42wp start, run in that directory, reads the file and applies it — and CLI flags override any value for a one-off run. With project_slug set you can run 42wp start with no name. 42wp init won't overwrite an existing file unless you pass --force.

| Key | Effect | | --- | --- | | project_slug | project name (so 42wp start needs no argument) | | multisite | false, subdir or subdomain | | username / password | admin credentials (plaintext — local dev only) | | locale | WordPress site language, e.g. pt_BR (pt-br is normalized) | | default_theme | theme to wp theme activate after install | | wpvip | clone + mount the VIP mu-plugins | | initial_content | true (200 posts) or a number |

WordPress version

By default projects are built on wordpress:php8.4-apache (newest WordPress, PHP 8.4, Apache). Pick a different image tag per project with --wp <tag>:

42wp start my-theme --wp latest             # newest WP, image's default PHP
42wp start legacy   --wp 6.9-php8.5-apache  # pin a specific WP + PHP
42wp update my-theme                        # rebuild on the default tag, run wp core update-db
42wp update my-theme --wp php8.5-apache     # update to a specific image

update rewrites the project's Dockerfile, re-pulls the base image, recreates the container and runs wp core update-db to migrate the schema. Set a different default for every project with the FORTYTWO_WP_TAG env var.

Removing a site

42wp rm <project> tears a site down completely — its containers, the locally built image, and its database — and deletes the generated project dir (~/.42wp/projects/<name>/, including any cloned VIP mu-plugins). Your repository is never touched: wp-content is a bind mount of your working directory, which Docker leaves alone. It asks for confirmation; skip the prompt with --yes (or --force):

42wp rm my-theme          # prompts before removing
42wp rm my-theme --yes    # no prompt (for scripts/CI)

Demo content

Seed a project with a full demo dataset using --demo-content (defaults to 200 posts), or set a count with --demo-content=<n>:

42wp start my-site --demo-content       # ~200 posts
42wp start my-site --demo-content=50    # 50 posts

It runs a PHP seeder (wp eval-file) — no plugin required (FakerPress has no CLI) — that creates:

  • ~10 42wp_author terms (Portuguese names), ~10 tags, ~10 categories
  • a small pool of real images downloaded from picsum.photos, reused as featured + inline images
  • posts with rich HTML content (h1/h2/h3, lists, blockquote, images, links), a plain-text excerpt, varied past dates, and each linked to 1 category, 2–5 tags and 1–3 authors

Works on any project (not just --vip). It always creates the requested number of posts (it doesn't skip when posts already exist), and a failure never aborts start. The author terms use the 42wp_author taxonomy, which the project registers itself (e.g. the 42-framework mu-plugin) — the seeder does not register any taxonomy. If 42wp_author isn't available, the author links are simply skipped.

When the project ships the 42-framework (detected by client-mu-plugins/42-framework or a composer.json that requires 42wp/wp-mu-plugins), the seeder additionally configures — once, idempotently — a fuller site:

  • the /%category%/%postname%/ permalink structure
  • a 42wp_homepage post set as the static front page (falls back to a regular page if the type isn't registered)
  • 4 category-driven menus — Header Primário / Secundário and Footer Primário / Social (with a nested footer and YouTube/X/Instagram links) — assigned to the theme's registered menu locations

Re-running (or 42wp seed) reuses the existing homepage and menus instead of duplicating them; only posts keep appending. On a plain WordPress site these extra steps are skipped and only the content above is seeded.

To add demo content to an already-running project without recreating it, use seed:

42wp seed my-site         # 200 posts
42wp seed my-site 500     # 500 posts
42wp seed my-site --demo-content=50

Admin credentials

The silent install creates an admin / password user by default. Override per project on start:

42wp start my-theme --user joao --pass s3cr3t

Site language

New sites install in English (en_US) by default. Pick a WordPress locale with --locale; the language pack is downloaded and activated automatically:

42wp start my-site --locale pt_BR      # Brazilian Portuguese
42wp start my-site --locale es_ES      # Spanish (Spain)

Set a default for every project with the FORTYTWO_WP_LOCALE env var. This is the site language — distinct from --lang, which controls the 42wp CLI's own messages (en/pt). The pack is downloaded into wp-content/languages; if the download fails (e.g. offline), the site stays in English and start continues.

Multisite

Start a site as a WordPress network with --multisite (subdirectory) or add --subdomains for a subdomain network:

42wp start my-network --multisite               # sites at my-network.localhost/site2
42wp start my-network --multisite --subdomains  # sites at site2.my-network.localhost

This installs WordPress, converts it to a network, writes the MULTISITE constants into wp-config.php, and mounts a .htaccess with the matching network rewrite rules (WP-CLI refuses to generate those for a multisite, which otherwise causes a redirect loop on sub-sites). For subdomain networks the Traefik router is widened to a wildcard (*.my-network.localhost) so sub-sites route to the container; *.localhost already resolves to 127.0.0.1, so no /etc/hosts edits are needed. Network admin lives at …/wp-admin/network/.

WordPress VIP (mu-plugins)

WPVIP sites expect the VIP Go mu-plugins under wp-content/mu-plugins. Pass --vip to start and the tool clones vip-go-mu-plugins-built (shallow) and mounts it into the container at wp-content/mu-plugins:

42wp start my-vip-site --vip

The clone lives in the project's data dir (~/.42wp/projects/<name>/mu-plugins), so it never touches your repo, and re-running start fast-forwards it. Regular (non-VIP) projects are unaffected.

--vip also mounts a small dev-helper mu-plugin (00-42wp-dev.php) that loads the WordPress admin media includes in admin/REST/CLI contexts. This fixes "Call to undefined function media_handle_sideload()"-style errors from plugins like FakerPress, which the VIP environment triggers by loading file.php early. It's mounted from the data dir, so your repo and the cloned VIP suite stay clean.

Example

cd ~/code/my-theme
42wp global start
42wp start my-theme
# → http://my-theme.localhost  (admin / password at /wp-admin)
42wp wp my-theme plugin list
42wp stop my-theme

Shared services once the global layer is up:

| Service | URL | | ----------- | ---------------------------- | | Traefik | http://localhost:8080 | | phpMyAdmin | http://db.42wp.localhost | | Mailpit | http://mail.42wp.localhost |

How it works

State lives under ~/.42wp (override with FORTYTWO_HOME):

~/.42wp/
  docker-compose.global.yml   # the global layer (created on first run)
  mysql-data/                 # persisted MySQL data
  projects/<project>/         # generated wp-config.php, Dockerfile, docker-compose.yml, meta.json

42wp start generates an ephemeral wp-config.php (fetching real salts from api.wordpress.org, with a local fallback when offline), a Dockerfile, and a docker-compose.yml, then builds the container and runs a silent wp core install. The DB name is the project name with hyphens turned into underscores; a trailing .suffix (e.g. .localhost) is stripped.

Where a project is bound. 42wp start mounts the directory you run it from as wp-content, so it first checks that directory looks like one — it must contain plugins/ and themes/, otherwise start refuses (rather than building a broken site). Pass --force to override (e.g. bootstrapping a brand-new, empty project). The chosen directory is recorded in the project's meta.json, and later starts reuse it — so re-running from a different folder can't silently re-point the container. Run 42wp start <name> --rebind from a new directory to move the binding there on purpose.

The generated wp-config.php always defines WP_ENVIRONMENT_TYPE as local, so wp_get_environment_type() reports local (not the WordPress default of production) — code and plugins that branch on the environment behave as they should in dev. The constant is set before mu-plugins load, so it holds even under --vip. The config is regenerated every start, so to apply this to an existing project, just re-run 42wp start <project>.

Configuration

| Env var | Default | Description | | --------------- | -------------- | --------------------------------------------- | | FORTYTWO_HOME | ~/.42wp | Where the global compose, DB data and projects live | | FORTYTWO_WP_TAG | php8.4-apache | Default WordPress image tag for new projects (overridden by --wp) | | FORTYTWO_WP_LOCALE | unset (en_US) | Default WordPress site language for new projects (overridden by --locale) | | FORTYTWO_LANG | auto (LANG) | CLI UI language: en or pt | | NO_COLOR | unset | Disable colored output |

You can also pass --lang en / --lang pt before the command.

License

MIT