A comprehensive template for AI-driven JavaScript/TypeScript development with full CI/CD pipeline support.
This repository publishes the real test package
@link-foundation/example-package-name so the template release pipeline is
validated end to end with npm trusted publishing.
- Multi-runtime support: Works with Bun, Node.js, and Deno
- Universal testing: Uses test-anywhere for cross-runtime tests
- Automated releases: Changesets-based versioning with GitHub Actions
- Optional Docker Hub publishing: Docker images can be published after the matching npm version is visible
- Universal app example: React UI for the package API with GitHub Pages, Electron, and Capacitor build paths
- Code quality: ESLint + Prettier with pre-commit hooks via Husky
- Package manager agnostic: Works with bun, npm, yarn, pnpm, and deno
- Broken link checks: Automated link validation with lychee and Web Archive fallback suggestions
- Click "Use this template" on GitHub to create a new repository
- Clone your new repository
- Update
package.jsonwith your package name and description - Install dependencies:
bun install - Start developing!
# Install dependencies
bun install
# Run tests
bun test --timeout 30000
# Or with other runtimes:
npm test
deno test --allow-read
# Lint code
bun run lint
# Format code
bun run format
# Check all (lint + format + file size)
bun run check
# Build the universal React example app
npm install --prefix examples/universal-app
npm run example:web:build
npm run example:desktop:package
# Try the CLI locally
node bin/example-package-name.js add 2 3.
├── .changeset/ # Changeset configuration
├── .github/workflows/ # GitHub Actions CI/CD
├── .husky/ # Git hooks (pre-commit)
├── examples/ # Usage examples
│ └── universal-app/ # React + GitHub Pages + Electron + Capacitor app
├── scripts/ # Build and release scripts
├── src/ # Source code
│ ├── index.js # Main entry point
│ └── index.d.ts # TypeScript definitions
├── tests/ # Test files
├── .eslintrc.js # ESLint configuration
├── .prettierrc # Prettier configuration
├── bunfig.toml # Bun configuration
├── deno.json # Deno configuration
└── package.json # Node.js package manifest
This template is designed to work seamlessly with all major JavaScript runtimes:
- Bun: Primary runtime with highest performance, uses native test support (
bun test) - Node.js: Alternative runtime, uses built-in test runner (
node --test) - Deno: Secure runtime with built-in TypeScript support (
deno test)
The test-anywhere framework provides a unified testing API that works identically across all runtimes.
While package.json is the source of truth for dependencies, the template supports:
- bun: Primary choice, uses
bun.lockb - npm: Uses
package-lock.json - yarn: Uses
yarn.lock - pnpm: Uses
pnpm-lock.yaml - deno: Uses
deno.jsonfor configuration
Note: package-lock.json is not committed by default to allow any package manager.
The template includes examples/universal-app, a Vite React app that imports
add and multiply from src/index.js and renders a visual calculator UI.
The same static build is used by:
- GitHub Pages (
npm run example:web:build) - Electron desktop packaging (
npm run example:desktop:package) - Capacitor Android/iOS sync (
npm run example:mobile:sync)
The example app has its own package.json and lockfile so template users can
opt into the frontend stack without adding React, Electron, or Capacitor to the
library package itself.
See examples/universal-app/README.md for local web, desktop, Android, and iOS testing instructions.
- ESLint: Configured with recommended rules + Prettier integration
- Prettier: Consistent code formatting
- Husky + lint-staged: Pre-commit hooks ensure code quality
- File size limit: Files must stay under 1500 lines for maintainability (enforced via ESLint and CI)
The release workflow uses Changesets for version management:
- Creating a changeset: Run
bun run changesetto document changes - PR validation: CI checks for valid changeset in each PR
- Automated versioning: Merging to
maintriggers version bump - npm publishing: Automated via OIDC trusted publishing (no tokens needed)
- Optional Docker Hub publishing: When configured, waits for the exact npm version and tags the Docker image with that version
- GitHub releases: Auto-created with formatted release notes
First release of a brand-new package: OIDC trusted publishing cannot create a package that does not exist yet (the first publish fails with
E404, because a trusted publisher can only be configured for an existing package). To bootstrap, add a repository secret namedNPM_TOKEN(a granular/automation token with publish access). The release workflow passes it asNODE_AUTH_TOKENautomatically. Once the package exists and OIDC trusted publishing is configured on npmjs.com, the token becomes optional and can be removed.
Two manual release modes are available via GitHub Actions:
- Instant release: Immediately bump version and publish
- Changeset PR: Create a PR with changeset for review
The GitHub Actions workflow (.github/workflows/release.yml) implements a fast-fail pipeline:
Fast checks (~7-30s each, run first for fastest feedback):
- Test compilation: Syntax-checks all
.mjsfiles withnode --check - Lint, format & secrets scan: ESLint, Prettier, jscpd, and secretlint for credential leak detection
- File line limits: Enforces the 1500-line limit on JavaScript (
.js,.mjs,.cjs) and Markdown (.md) files plusrelease.yml - Changeset check: Validates PR has exactly one changeset (added by that PR)
- Version check: Blocks manual version changes in
package.json - Documentation validation: Checks required doc files (doc line limits are enforced by the file line limits check)
Slow checks (only run after all fast checks pass):
- Test matrix: 3 runtimes × 3 OS = 9 test combinations
- Broken link checks: Validates all links in Markdown/HTML files (separate workflow)
Release (on merge to main):
- Changeset merge: Combines multiple pending changesets at release time
- Release: Automated versioning and npm publishing
- Optional Docker publish: Publishes Docker Hub
latestand npm-version tags after the npm package is visible
Every CI job declares an explicit timeout-minutes so hung steps fail
in minutes instead of reaching the GitHub Actions default of six hours.
Fast checks use 5-10 minute caps, release jobs use 30 minutes, and the
link checker uses 10 minutes for external network variance.
Individual tests are also capped inside supported runners:
npm test runs node --test --test-timeout=30000, and the CI Bun
runner uses bun test --timeout 30000. Deno does not provide a single
global per-test timeout flag, so Deno tests are protected by the
10-minute matrix job cap.
See BEST-PRACTICES.md for detailed explanations of each practice.
The CI/CD pipeline is designed to handle concurrent PRs gracefully:
-
PR Validation: Only validates changesets added by the current PR, not pre-existing ones from other merged PRs. This prevents false failures when multiple PRs merge before a release cycle completes.
-
Release-time Merging: If multiple changesets exist when releasing, they are automatically merged into a single changeset with:
- The highest version bump type (major > minor > patch)
- All descriptions preserved in chronological order
This design decouples PR validation from the need to pull changes from the default branch, reducing conflicts and ensuring that even if CI/CD fails, all unpublished changesets will still get published when the error is resolved.
The example-app.yml workflow deploys the universal example app to GitHub
Pages on every push to main. Before the first run on main in a new
repository created from this template, open Settings → Pages and set
Source = GitHub Actions. This is a one-time manual step and cannot be
configured from a workflow because the Pages source defaults to
Deploy from a branch. Without it, the pages-deploy job fails on
actions/deploy-pages with Get Pages site failed /
Failed to create deployment. After flipping the source, the workflow
provisions the Pages site on its first run.
The same example-app.yml workflow contains a preview-regen job that boots
the built example app in a headless Chromium via
browser-commander +
Playwright and writes fresh screenshots to
docs/screenshots/example-app/example-app-{locale}-{theme}.png on every
push to main (and on workflow_dispatch). Any drift is committed back to
main with [skip ci] so README/site images never go stale between
releases. The job runs in the official Playwright container with the browser
already installed, avoiding CI stalls from live Chromium downloads.
The same script is available locally:
npm install --prefix examples/universal-app
npm run example:web:preview-images
# Verbose probe of <html data-theme>, <html lang>, and PNG signatures:
PREVIEW_VERBOSE=1 npm run example:web:preview-imagesThe matrix defaults to {en, ru} × {light, dark}. The shipped example app
has no localization or theme toggle yet, so every cell currently renders
the same UI — when a fork adds either, the matrix produces real per-cell
variants without script edits.
The link checker workflow (.github/workflows/links.yml) validates all links in Markdown and HTML files:
- Detection: Uses lychee to scan all
*.mdand*.htmlfiles - Web Archive fallback: For any broken links found, automatically checks the Wayback Machine for archived versions
- Actionable suggestions: Reports one of three outcomes for each broken link:
- Archived: Suggests the Web Archive URL as a replacement
- Not archived: Clearly reports the link is unrecoverable
- Scheduled checks: Runs weekly to catch links that break over time (even if no files changed)
- Issue creation: On scheduled runs, creates a GitHub Issue with the full broken links report
Add regex patterns to .lycheeignore to exclude URLs from checks (e.g., local dev URLs, example.com, known rate-limited sites).
After creating a repository from this template, update the package name in:
package.json: replace"@link-foundation/example-package-name"with your package name.changeset/config.json: Package references
Release scripts derive the package name from package.json at runtime, so no
script-level package-name constants need to be edited during template adoption.
Docker publishing is disabled by default. To enable it for a project that ships
a Docker image, add a Dockerfile and configure these GitHub Actions settings:
| Setting | Type | Description |
|---|---|---|
DOCKERHUB_IMAGE |
Variable | Docker Hub image name, for example namespace/image. This enables Docker publishing. |
DOCKERHUB_USERNAME |
Variable | Docker Hub username used by docker/login-action. |
DOCKERHUB_TOKEN |
Secret | Docker Hub access token used for registry authentication. |
DOCKER_CONTEXT |
Variable, optional | Docker build context. Defaults to .. |
DOCKERFILE |
Variable, optional | Dockerfile path. Defaults to ./Dockerfile. |
When enabled, the release workflow waits until the exact published npm version
is visible in the npm registry, then publishes Docker Hub tags for latest and
that same version. The Docker build also receives NPM_PACKAGE_VERSION as a
build argument so Dockerfiles can install the matching published package.
Customize ESLint in eslint.config.js. Current configuration:
- ES Modules support
- Prettier integration
- No console restrictions (common in CLI tools)
- Strict equality enforcement
- Async/await best practices
- Strict unused variables rule: No exceptions - all unused variables, arguments, and caught errors must be removed (no
_prefix exceptions)
Configured in .prettierrc:
- Single quotes
- Semicolons
- 2-space indentation
- 80-character line width
- ES5 trailing commas
- LF line endings
| Script | Description |
|---|---|
bun test --timeout 30000 |
Run tests with Bun and a 30s per-test cap |
npm test |
Run tests with Node.js and a 30s per-test cap |
bun run lint |
Check code with ESLint |
bun run lint:fix |
Fix ESLint issues automatically |
bun run format |
Format code with Prettier |
bun run format:check |
Check formatting without changing files |
bun run check |
Run all checks (lint + format) |
npm run example:web:dev |
Start the universal app Vite dev server |
npm run example:web:build |
Build the universal app static web bundle |
npm run example:web:preview-images |
Regenerate preview screenshots via browser-commander |
npm run example:desktop:package |
Package the Electron desktop app locally |
npm run example:mobile:sync |
Build and sync the app bundle into Capacitor projects |
bun run changeset |
Create a new changeset |
See CONTRIBUTING.md for detailed contribution guidelines.
Quick steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Create a changeset:
bun run changeset - Commit your changes (pre-commit hooks will run automatically)
- Push and create a Pull Request
This template implements CI/CD best practices for AI-driven development. See BEST-PRACTICES.md for details on:
- File size limits for AI readability
- Automated formatting and linting
- Multi-runtime and cross-platform testing
- Changeset-based versioning
- Concurrency control for CI/CD pipelines
Unlicense - Public Domain