Project template for TypeScript libraries built with Effect, optimized for tree-shaking and agentic engineering.
- ESM-only with proper
exportsconfiguration - Tree-shaking optimized (see Tree Shaking)
- Effect as peer dependency
- Types: tsgo (Go-based TypeScript compiler)
- Tests: bun test with 95% coverage gating
- Linting: oxlint (type-aware) + actionlint
- Formatting: oxfmt
- Package validation: publint + attw
- Publishing: Dripip
- CI: GitHub Actions
- AI: Effect MCP for docs, hookify rule blocking
tsc
gh repo create mylib --template jasonkuhrt/template-typescript-lib --clone --public && \
cd mylib && \
bun install && \
bun run bootstrapThen setup a repo secret called NPM_TOKEN for CI publishing.
If you use git-hooks, run this one-time local setup after bootstrap:
git-hooks installThis template tracks its pre-commit behavior in .git-hooks/pre-commit.d/, so installation is explicit and local while hook intent stays in the repo.
This template uses Effect as a peer dependency. Consumers of your library must install Effect themselves.
Building an app instead of a library? Move
effectfrompeerDependenciestodependenciesinpackage.json.
The project includes an .mcp.json configuring the Effect MCP server by tim-smart for Claude Code, giving AI agents access to Effect documentation.
This template is configured for aggressive tree-shaking. Bundlers (webpack, esbuild, rollup, vite) can eliminate unused code when consumers import from your library.
| Field | Value | Purpose |
|---|---|---|
type |
"module" |
ESM output (required for tree-shaking) |
sideEffects |
false |
Tells bundlers all modules are pure |
exports |
Explicit entry points | Black-boxes package internals |
| Option | Purpose |
|---|---|
verbatimModuleSyntax |
Preserves ES module syntax for bundlers |
isolatedModules |
Ensures code is compatible with single-file transpilers |
Two tools validate your package works correctly for consumers:
- publint - Checks packaging for compatibility across environments
- attw - Checks TypeScript types resolve correctly across module resolution modes
Run both with bun run check.
Named exports tree-shake more reliably than default exports. Default exports can cause issues with CommonJS interop.
// Preferred
export const foo = () => {}
export const bar = () => {}
// Avoid
export default { foo, bar }For module-level function calls (HOCs, factories), the /*#__PURE__*/ annotation tells bundlers the call is side-effect free. See Terser and UglifyJS docs.
Bundlers cannot always statically determine if code has side effects. The sideEffects field hints to bundlers that your modules are "pure" and safe to prune if unused.
Important: If you add modules with side effects (e.g., CSS imports, polyfills, or code that runs on import), update sideEffects to an array:
{
"sideEffects": ["./src/polyfill.js", "**/*.css"]
}Barrel files (index.ts files that re-export from other modules) can inhibit tree-shaking in some bundlers. This template uses a single entry point which is acceptable for small libraries.
For larger libraries, consider:
- Multiple entry points via
exportsfield - Avoiding deep re-export chains
- Testing your bundle size with bundlephobia or pkg-size
- Webpack Tree Shaking Guide
- Tree-Shaking: A Reference Guide (Smashing Magazine)
- package.json exports field (Node.js)
- Building TypeScript Libraries (Arrange Act Assert)
- Are The Types Wrong?
- Strict settings via
@tsconfig/strictest - Node 24 target via
@tsconfig/node24 - Build cache in
node_modules/.cache - Output includes
declaration,declarationMap,sourceMapfor optimal consumer DX - Source published for go-to-definition support
.tsimport specifiers withrewriteRelativeImportExtensionsat build time- Subpath imports (
#lib/*) for clean internal paths
- oxlint: Rust-based, type-aware with
--deny-warnings - oxlint-tsgolint: Enables type-aware rules via tsgo
- actionlint: GitHub Actions workflow validation
--fix-dangerouslyenabled for auto-fix (safe with strict types + high coverage)- Staged-file pre-commit checks are tracked in
.git-hooks/pre-commit.d/10-lint-staged.shand runlint-staged, which appliesoxfmtandoxlintonly to staged files whengit-hooksis installed
bun test with coverage gating at 95% lines / 95% functions.
oxfmt - Rust-based formatter from the oxc project.
| Script | Description |
|---|---|
check |
Run all checks sequentially |
check:format |
Verify formatting |
check:lint |
Run oxlint (type-aware) |
check:types |
Type check with tsgo |
check:cov |
Run tests + enforce coverage thresholds |
check:package |
Validate package with publint |
check:exports |
Validate exports with attw |
check:ci |
Lint GitHub Actions workflows |
fix |
Auto-fix format + lint |
fix:format |
Fix formatting |
fix:lint |
Auto-fix lint issues (--fix-dangerously) |
build |
Build with tsgo |
test |
Run tests |
PR workflow:
- actionlint, format, lint, types, publint, exports checks
- Tests on ubuntu/macos/windows
- Coverage gating
Trunk workflow:
- Automated canary release via Dripip
This template includes AI tooling for Claude Code:
- Effect MCP (
.mcp.json): Provides Effect documentation access to Claude Code agents - Hookify rule (
.claude/hookify.use-tsgo.md): Blockstscusage, directs agents to usetsgoinstead
Configure tsgo globally in ~/.config/zed/settings.json:
{
"languages": {
"TypeScript": {
"language_servers": ["tsgo", "!vtsls", "oxc"]
}
}
}