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

@doikayt/typescript-build-config

v0.1.32

Published

Shared build configuration for TypeScript projects — ESLint, Prettier, tsconfig, and Playwright presets

Readme

@doikayt/typescript-build-config

Shared build configuration presets for TypeScript-based projects.

Purpose

This package centralises common build tooling configuration, release policy, and content assets across all TypeScript projects maintained under the @doikayt scope. The goal is a single source of truth for such settings that should be held constant across projects, avoiding drift between repos over time. The plugin encapsulates common build policy and release workflow logic via the pipeline files it installs into each consumer repo and the canonical release process in docs/RELEASE-PROCESS.md.

Quick start

These steps set up — in a fresh "new-developer" environment — a local git repo with a skeleton demo app plus all the standard Doikayt build configuration: tooling that auto-documents the code in the repo and lets it be pushed to GitHub, built on GitHub's CI servers, and (for a library) published to npm. Steps are minimal here — each links to the section that explains what and why.

1. One-time machine setup (once per machine)

Clone this base repo and install the shared team shell aliases. This only sets up the aliases (dk-scaffold, mkrepo); it does not configure any project:

git clone https://github.com/doikayt/typescript-build-config.git   # HTTPS: no SSH key needed
cd typescript-build-config
./assets/shell/install.sh   # adds `source assets/shell/aliases.sh` to your rc (idempotent)
exec $SHELL                 # reload

2. Create a new project (repeatable)

The two paths below are alternatives that reach the same result — a project wired to this base package.

dk-scaffold

dk-scaffold is the one-command wrapper that also (optionally) creates the GitHub repo and pushes it. Just run it with a first argument naming your project and a second indicating lib (a published library) or app (a standalone app/utility, the default) — see library vs app and Packaging concepts:

dk-scaffold my-project lib

Run dk-scaffold's steps individually

The steps below mirror the work dk-scaffold does under the hood — either for a brand-new project or to adopt Doikayt standard build config into an existing one. (Installation and Usage explain each in detail.)

npx @doikayt/typescript-build-config new                  # npm init -y + @doikayt scope
npm install --save-dev @doikayt/typescript-build-config   # postinstall seeds configs + pipeline
npx @doikayt/typescript-build-config init                 # scripts, deps, publish config, demo
npm install                                               # fetch declared devDependencies

Trying it out

dk-scaffold does a lot under the hood, so the best way to get familiar with it is to run it at the two levels it supports — each requiring a little more setup than the last, and revealing a little more of the process. Each level scaffolds a throwaway app (nothing publishes) unless noted. Do the one-time machine setup first so dk-scaffold is on your PATH.

Level 0 — local (no external GitHub/npm accounts needed). Scaffold and run the same gate CI runs — no GitHub repo, no push:

dk-scaffold my-demo --local

That single command runs init (demo=yes, app, no Playwright) then npm run ci. You'll see the vitest demo tests pass, npm run build emit dist/ + type declarations, and update-all-format fill the README's TOC + UML markers (mermaid source — rendered at Level 1). npm publish --dry-run refuses — proof the app archetype never publishes.

Level 1 — push to your own GitHub. Bring a GitHub account and the gh CLI. Sign in, point the wrapper at your account, and drop --local:

gh auth login                                # one-time: sign in to GitHub
export REPO_OWNER=<your-github-username>      # target owner for the repo
dk-scaffold my-demo                          # app (private) — no npm creds

Now it creates the repo and pushes — and you see what Level 0 couldn't: the mermaid diagrams rendered (example here) and the CI job go green in Actions (example: this project's runs). Push a follow-up feat: commit and the release job version-bumps + tags — nothing published (app). (The initial chore: scaffold push releases nothing; feat: / fix: triggers it.)

The screen cast below shows the process for Level 1. Note:

  • When we log in to our test account (associated with a dummy GitHub account, not doikayt) we see:
    • no repos initially
    • no doikayt aliases registered in .bashrc
  • After we run dk-scaffold demo, we need to input 'y' to install, then we see:
    • lots of output showing the project being configured and built
    • a push to GitHub
    • a build and release triggered on GitHub
    • since we did not select lib, we got an app build profile, for which there is no publish to npm

After we ran the screen cast steps we were able to screenshot how our push triggered a build o onthe Github CI servers.

Design Goals

The package housed in this repo is designed to be 'upstream-most' -- i.e., all other packages depend on it (possibly transitively) and this package has no other dependencies on doikait packages (except ONE pinned version specific dependency on build_tools which we rely on for documentation generation. -- Cycles are generally bad, but this one works!)

Two goals drive every design decision made for this package, and they pull in different directions:

  1. Consistency enforcement — every @doikayt TypeScript repo should expose the same command surface (ci and update-all-format) and share the same lint / format / release policy, so tooling, CI, and contributors can assume an identical shape in any repo.
  2. Minimal per-project setup — standing up a new repo, or absorbing a policy change into an old one, should cost as close to zero manual wiring as possible.

This package reconciles these two via one architectural choice: it both defines the conventions and distributes them. A plain template repo would copy conventions once and let them drift; a linter would enforce them but set nothing up. The npm package defined in this repo does both — it is the single artifact that decides "every repo has a ci gate" and also installs and re-checks it. That single source of truth is what keeps the ecosystem coherent as it grows.

What each component is for

Each artifact this package ships exists to serve one or both goals. (See Delivery Model for how each is delivered; the table below is why.)

| Component | Value it delivers | Serves | | --- | --- | --- | | Presetssrc/eslint.js, Prettier, base tsconfig, living in node_modules | One lint / format / TS ruleset; change it once here and every repo picks it up on npm update | Consistency | | Stubssrc/top-level/* seeded into the project root | Zero-config entry points that only extends the presets | Minimal setup | | Pipelinesrc/pipeline/* copied on install | A working release + changeset workflow with no hand-wiring; drift is diff-warned | Both | | Convention checks — postinstall warns on missing ci / update-all-format | An install-time nudge toward the shared command surface | Consistency | | ci gaterelease.yml runs npm run ci | Fail-closed enforcement: a repo that ignores the convention cannot release | Consistency (hard teeth) | | Policy docdocs/RELEASE-PROCESS.md, linked never copied | One canonical release policy, impossible to drift | Consistency | | Assetsassets/image/* seeded into docs/assets/ | Shared brand logos with no per-repo copies to maintain | Minimal setup | | Shell aliasesassets/shell/aliases.sh, cloned + sourced (never shipped to npm) | One standard way for the team to create and scaffold repos (mkrepo, dk-*) | Consistency |

The two goals map onto two lifecycle phases — delivery (getting canonical artifacts into a consuming repo) and enforcement (keeping that repo conformant over time). Each has its own top-level section below.

Installation

npm install --save-dev @doikayt/typescript-build-config

The postinstall script copies starter config files into your project root and sets up the release pipeline (see below).

Usage

After installing, run the init scaffolder to write the canonical npm-script set, declare the dev dependencies those scripts need — vitest, @doikayt/autogen-markdown-doc, typescript, and @changesets/cli (the release pipeline needs it) — and seed a commented vitest.config.ts. init is interactive and idempotent — it only adds what is missing and never overwrites a script or config you already have, so it is safe to re-run.

The canonical script set

Every project gets the same named scripts, so any repo responds identically to npm run ci, npm run update-all-format, and so on. The aggregates chain to small single-purpose scripts, and the check side mirrors the write side:

| Script | What it does | | --- | --- | | ci | The release gate: check-all-format then test (plus test:e2e for UI). | | build | Compile TypeScript to dist/ (tsc). | | test | Unit / integration tests (vitest run). | | test:e2e | End-to-end tests (playwright test) — UI projects only. | | prepack | Runs build before packing, so changeset publish ships compiled dist/libraries only. | | update-all-format | Reformat everything: code + markdown docs (write). | | check-all-format | Verify formatting: code + markdown docs (used by ci). | | update-code-formatting / check-code-formatting | Prettier write / check. | | update-markdown-docs / check-markdown-docs | Regenerate / verify generated markdown (TOC, UML, …). |

init also asks whether the project is a publishable library. That answer sets the package.json fields that control publishing — main, types, exports, files, prepack, private — non-destructively (it merges, it doesn't overwrite your other keys):

  • Librarymain / types / exportsdist/index.js, files: ["dist"], and prepack, so it publishes a compiled, typed ESM package on push. init also prompts for the package name (default @doikayt/<name>; a bare name is scoped to @doikayt, an already-scoped name is kept).
  • App / CLI (the default) — marked private: true, so a release versions and tags it but never publishes to npm.

type: "module" and build are set either way. New to these terms (compiled dist/, publish fields, library vs app)? See Packaging concepts for the background.

Optional Playwright config for Web / UI testing

The above section covers what init decides; the two recipes below are how you run it. They differ only in the Playwright? answer — n for a console/CLI or plain library, y for a UI / web project that needs end-to-end tests. Library-vs-app is an orthogonal choice, so either shape can still be published or kept private.

Console / CLI or plain library — answer n:

npx @doikayt/typescript-build-config new              # npm init -y + @doikayt scope
npm install --save-dev @doikayt/typescript-build-config
npx @doikayt/typescript-build-config init             # answer "n" to Playwright; "n" (app) or "y" (library)
npm install                                            # fetch the declared devDependencies

UI / web — answer y: injects the end-to-end setup — adds a test:e2e script and folds it into ci (so the release gate runs e2e), declares @playwright/test, and seeds a playwright.config.ts template.

npx @doikayt/typescript-build-config new              # npm init -y + @doikayt scope
npm install --save-dev @doikayt/typescript-build-config
npx @doikayt/typescript-build-config init             # answer "y" to the Playwright prompt
npm install
# then fill in the TODOs in the generated playwright.config.ts (webServer, baseURL)

Browsers are installed on demand by the bundled doikayt-playwright-install wrapper the test:e2e script calls — no manual playwright install step, locally or in CI.

Existing project

Skip init if you prefer. On install, postinstall warns about any missing required targets (ci, update-all-format) until you add them — either by running init to adopt the full canonical set, or by defining the two scripts by hand (see Conventions).

Dependency Strategy

eslint and prettier are listed as dependencies and are pulled in automatically. The @typescript-eslint/* plugins are listed in both dependencies and peerDependencies — the peer declaration pins the minimum version to ^8.57.1 to avoid a ts-api-utils incompatibility with TS 5.4+.

This package targets new projects. Use in existing projects that pin older versions of these tools may produce peer dependency conflicts.

Current Contents

  • new command (npx @doikayt/typescript-build-config new) — runs npm init -y then scopes the package name to @doikayt (an existing scope is kept)

  • init scaffolder CLI (npx @doikayt/typescript-build-config init) — writes the canonical npm-script set, declares dev dependencies, and seeds config templates (see Usage)

  • doikayt-playwright-install bin — env-gated Playwright browser installer used by a UI project's test:e2e script (adds --with-deps only on CI+Linux)

  • ESLint config

  • Prettier config

  • TypeScript config (tsconfig.json, tsconfig.test.json, tsconfig.eslint.json)

  • Postinstall script that copies starter top-level config files into your project root, extending the configs installed under node_modules

  • GitHub Actions release workflow (.github/workflows/release.yml)

  • Changeset config (.changeset/config.json)

  • Auto-changeset script (scripts/auto-changeset.sh)

  • NPM token diagnostic workflow (.github/workflows/verify-npm-token.yml) — see Troubleshooting Publish Auth

  • Playwright config helper (@doikayt/typescript-build-config/playwright) — nixChromiumLaunchOptions() discovers the system Chromium on quirky NixOS; definePlaywrightConfig() wraps defineConfig and merges the result in. Transparent no-op on platforms where Playwright's bundled browser works. @playwright/test is an optional peer dependency — it is not installed automatically. Any project that imports from @doikayt/typescript-build-config/playwright must declare it explicitly in its own devDependencies:

    npm install --save-dev @playwright/test
    import { definePlaywrightConfig } from "@doikayt/typescript-build-config/playwright";
    export default definePlaywrightConfig({
        testDir: "code/tests/e2e",
        testMatch: "**/*.spec.ts",
        use: { baseURL: "http://localhost:8080" },
    });
  • Brand assets (assets/image/doikayt-logo.png, assets/image/doikayt-logo.svg) — shared logos for use across all @doikayt project READMEs and documentation

Delivery Model

Phase 1 of 2: getting canonical artifacts into a repo. Keeping a repo conformant over time is the Enforcement Model.

This package standardizes downstream repos by delivering five components through three channels — so as you read the diagram, note that the component count and the channel count deliberately differ; several components share a channel. The components are the presets, stubs, pipeline, and assets that implement the common policy, plus a canonical document that states it. Each reaches the consumer via one of three channels:

  1. Referenced in place — presets (stay in node_modules)
  2. Copied on install — stubs, pipeline, assets
  3. Linked, never copied — policy

The diagram below numbers those three channels; the terms after it define the five components.

        ┌─────────────────────────────────────────────────┐
        │        @doikayt/typescript-build-config        │
        │                                                 │
        │  presets  eslint  prettier  tsconfig  playwright│
        │  stubs       src/top-level/*                    │
        │  pipeline    src/pipeline/*                     │
        │  assets      assets/image/*                     │
        │  policy      docs/RELEASE-PROCESS.md            │
        └─────────┬───────────────┬───────────────┬───────┘
                  │               │               │
   1 REFERENCED   │  2 COPIED     │  3 LINKED,    │
     IN PLACE     │    ON INSTALL │  NEVER COPIED │
                  │               │               │
 presets stay in  │ postinstall   │ cited by URL  │
 node_modules —   │ seeds stubs,  │ only; one     │
 updates flow     │ pipeline, and │ canonical     │
 with npm update  │ assets once;  │ copy — drift  │
                  │ consumer owns │ is impossible │
                  │ them; drift ⇒ │               │
                  │ diff warning  │               │
                  ▼               ▼               ▼
        ┌─────────────────────────────────────────────────┐
        │                  consumer repo                  │
        │                                                 │
        │  seeded stubs:  eslint.config.js  tsconfig.json │
        │       │         prettier.config.js  …           │
        │       └────extends────► presets living in       │
        │                         node_modules  (chan. 1) │
        │  owned pipeline:  .github/workflows/release.yml │
        │                   scripts/auto-changeset.sh     │
        │  owned assets:    docs/assets/doikayt-logo.*    │
        │  CONTRIBUTING.md ─cites URL─► RELEASE-PROCESS.md│
        └─────────────────────────────────────────────────┘

Terms:

  • Presets — the real configuration content: the ESLint rules (src/eslint.js), Prettier options, and base tsconfig this package exports. They stay inside node_modules and are never copied — which is what makes them ecosystem-wide levers: change a lint rule once here, publish, and every downstream repo receives the new rule on its next npm update, with no per-repo edits.
  • Stubs — thin files seeded once into the consumer's project root by the postinstall script (eslint.config.js, tsconfig.json, …). Their only job is to extends/import the presets: channel 2 seeds these static, copied-once files, and they point back at the evolving content that channel 1 keeps current in node_modules.
  • Owned pipeline — the release workflow and scripts, copied on install. "Owned" by the consumer repo: it may edit its copies, upstream never overwrites them, and postinstall prints a diff warning when a copy drifts from the canonical template.
  • Assets — brand logos seeded once into docs/assets/ by the postinstall script. Same channel-2 treatment as the pipeline: copied if absent, skipped if identical, warning (without a text diff) if diverged. Consumer owns the copy; it will not be silently overwritten.
  • Policy — the release process itself, stated once in docs/RELEASE-PROCESS.md. Consumers reference it by URL from their own contributor docs (as the consumer box shows) — never copied, so it cannot drift.

Beyond consumer repos — the shell aliases. The five components above all reach a consuming repo through npm (channels 1–3). The team shell aliases are the exception: they target the developer's machine, not a repo, through a fourth channel — cloned and sourced (git clone this repo + source assets/shell/aliases.sh, via assets/shell/install.sh). npm never touches them and they never land in a consumer's package.json or tree. Same goal (a standard way to create repos), different destination — which is why they sit outside the five-components / three-channels model rather than as a sixth row in it.

Enforcement Model

Phase 2 of 2: keeping a repo conformant over time, once delivery has seeded it. Getting artifacts in is the Delivery Model above.

Delivery gets canonical artifacts into a consuming repo; enforcement keeps that repo aligned as both it and the upstream evolve. The two phases share machinery: postinstall is the workhorse of both — it delivers on first install (copying absent files) and enforces on every subsequent run that rebuilds this package into node_modules — a fresh npm install, an npm ci (which always rebuilds the whole tree), or an npm update that bumps this package — warning on drift and on missing conventions. A no-op npm install with an unchanged tree does not re-trigger it.

Enforcement runs on a spectrum from soft to hard:

  • Soft — install-time warnings (name-level). On every npm install / npm update, postinstall warns (non-fatal) when a required target (ci, update-all-format) is missing, and prints a diff when a copied pipeline or asset file has drifted from its canonical template. See Release Pipeline for the per-file drift behavior and Conventions Every Project Must Adhere To for the required targets.
  • Hard — the fail-closed ci gate. The release workflow runs npm run ci; a repo without a working ci gate fails the build and cannot publish. This is the only enforcement with real teeth — see The ci script.

One deliberate limitation: the soft checks are name-level only. They verify a ci script exists, not that it runs anything meaningful — "ci": "echo ok" satisfies the warning. Guaranteeing that ci actually does its job is left to the fail-closed pipeline, where a broken gate surfaces as a red build. Put differently: delivery is "deep" (full config seeded) while continuous enforcement is "shallow" (presence, not behavior) — a gap worth knowing when you rely on it.

Release Pipeline

On install, the postinstall script copies the release pipeline files into your project automatically. auto-changeset.sh reads the package name from your package.json at runtime, so renaming the package needs no pipeline edits.

On npm update, the behavior per file is:

  • File absent — copied into place
  • File identical to canonical — skipped silently
  • File differs from canonical — warning printed with a diff; local copy is left untouched for manual review

This means local customisations are never silently overwritten, but you are notified when your copy has drifted from the upstream version.

The release pipeline requires an NPM secret stored at the GitHub organisation level. All repos under the org inherit it automatically — no per-repo secret configuration is needed.

Conventions Every Project Must Adhere To

Every project that installs this package must implement two named entry points. postinstall warns whenever it re-runs (a fresh npm install, an npm ci, or an npm update that bumps this package) until each is in place.

The ci script

Every project must expose a ci script in package.json. The release workflow calls npm run ci as its CI gate. A project without a ci script will fail the CI job — the correct signal that the convention has not been met.

ci is the single entry point for "everything that must pass before a release": formatting checks, tests, linting — whatever the project requires. What it calls internally is up to the project:

{
  "scripts": { "ci": "prettier --check src/ && vitest run && playwright test" }
}

npm run ci doubles as your local "simulate CI" command.

Invariant: if npm run ci passes locally, and you commit and push, the remote CI job will pass.

NX projects

package.json is the source of truth — it is always present, and the release workflow calls npm run ci directly. So an NX project keeps the real ci command in package.json, exactly like any other project — it does not delegate package.json to NX. If you want NX orchestration, add NX targets that delegate to npm, never the reverse:

// package.json — the real command lives here
{ "scripts": { "ci": "npm run check-all-format && npm run test" } }
// project.json (optional) — an NX target delegates to the npm script
{
  "targets": {
    "ci": {
      "executor": "nx:run-commands",
      "options": { "command": "npm run ci" }
    }
  }
}

The release workflow is NX-agnostic — it calls npm run ci, never NX directly.

The update-all-format target

Every project must expose an update-all-format entry point — either a package.json script or an NX project.json target. This is the single command for "reformat everything before reviewing a diff": run it before committing to keep diffs clean and reviewable.

The plugin enforces the name only, not the content:

{
  "scripts": {
    "update-all-format": "prettier --write src/ && npm run update-markdown-docs"
  }
}

Or as an NX target:

{
  "targets": {
    "update-all-format": {
      "executor": "nx:run-commands",
      "options": { "command": "prettier --write src/" }
    }
  }
}

If neither is found, postinstall prints a warning (non-fatal) each time it re-runs (a fresh npm install, an npm ci, or an npm update that bumps this package) until the target is added.

NX projects may define the target in project.json instead of package.json scripts — the postinstall check recognises both. There is no shim requirement for update-all-format (unlike ci), because no external tooling calls it directly.

Publishing

Releases are automated via Changesets and GitHub Actions. The full policy — commit-prefix → bump mapping, forcing or suppressing a release, resolving changeset status errors, verifying a release, troubleshooting publish auth — is documented in docs/RELEASE-PROCESS.md. That document is the canonical release policy for every repo that installs this package.

Quick reference:

For a patch release — push a fix: commit to main. The workflow handles the rest.

For a minor or major release — run npx changeset locally, choose the bump level at the prompt, commit the generated .changeset/*.md file, then push. The auto-generation step is skipped when a handwritten changeset is present. For majors this is mandatory: a breaking-change commit (feat!: or BREAKING CHANGE in the body) with no handwritten changeset fails the release job until one is committed.

Manual publish (emergency): trigger the workflow manually via GitHub Actions → CI / Release → Run workflow on the main branch.

Team shell aliases

Installed by Quick start step 1 (or manually: add source <clone-path>/assets/shell/aliases.sh to your shell rc). They require gh (authenticated) and node/npm.

| Alias | What it does | | --- | --- | | mkrepo <name> | Create a public repo in the doikayt org (guards against duplicates). | | dk-new | npm init -y + @doikayt scope. | | dk-init | Scaffold the build config into the current project. | | dk-scaffold <name>\|. [lib\|app] | Create the repo, scaffold, and push — one command (defaults to app). Pass . to scaffold in the current directory, using its leaf name as @doikayt/<package-name>. |

For Maintainers

Clone and install

git clone [email protected]:doikayt/typescript-build-config.git
cd typescript-build-config
npm install

The postinstall script detects that it is running inside the plugin repo itself and exits immediately — no config stubs or pipeline files are copied.

Run the full CI suite

npm run ci

This is the same entry point mandated for every consumer repo. In this project it runs a Prettier format check followed by the full test suite:

npm run ci  →  prettier --check src/  →  node --test

A passing npm run ci locally means the remote CI job will pass.

To auto-fix formatting before verifying:

npm run update-all-format && npm run ci

To verify end-to-end that init scaffolds and ships both a library and an app — by hand, with what to look for at each step — follow docs/verification-runbook.md.

License

MIT