@author-today-tools/utils
v0.1.1
Published
Shared utilities for Author.Today tools
Maintainers
Readme
@author-today-tools/utils
Shared utilities for Author.Today tools, including runtime assertions, type guards, and common helpers.
Features
- Tree-shakeable assertions - Automatically removed in production builds
- Type-safe - Full TypeScript support with assertion functions
- Tiny - < 1KB footprint (uses tiny-invariant)
- Zero runtime overhead - Development assertions stripped in production
Installation
pnpm add @author-today-tools/utilsUsage
Basic Assertions
import { assert, assertDefined } from '@author-today-tools/utils';
function processUser(user: User | undefined) {
assert(user !== undefined, 'User is required');
// TypeScript knows user is defined here
return user.name;
}
function getConfig(config: Config | undefined) {
assertDefined(config, 'Configuration is required');
// TypeScript knows config is non-null here
return config.apiUrl;
}Exhaustive Type Checking
import { assertNever } from '@author-today-tools/utils';
type Status = 'pending' | 'success' | 'error';
function handleStatus(status: Status) {
switch (status) {
case 'pending':
return 'Loading...';
case 'success':
return 'Done!';
case 'error':
return 'Failed!';
default:
return assertNever(status); // TypeScript error if not exhaustive
}
}Development-Only Assertions
import { devAssert } from '@author-today-tools/utils';
function expensiveOperation(data: unknown[]) {
// This check is completely removed in production builds
devAssert(data.length < 1000, 'Array too large for dev environment');
return data.map(processItem);
}Type Guards with Assertions
import { assertTypeGuard } from '@author-today-tools/utils';
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function processValue(value: unknown) {
assertTypeGuard(value, isString, 'Value must be a string');
// TypeScript knows value is string here
return value.toUpperCase();
}API Reference
assert(condition, message?)
Assert that a condition is true. Throws error if false.
- Type:
(condition: unknown, message?: string) => asserts condition - Tree-shaking: Kept in production
assertDefined(value, message?)
Assert that a value is not null or undefined.
- Type:
<T>(value: T, message?: string) => asserts value is NonNullable<T> - Tree-shaking: Kept in production
assertNever(value, message?)
Assert that code path is never reached. Use for exhaustive type checking.
- Type:
(value: never, message?: string) => never - Tree-shaking: Kept in production
devAssert(condition, message?)
Development-only assertion. Completely removed in production builds.
- Type:
(condition: unknown, message?: string) => asserts condition - Tree-shaking: Removed in production
assertTypeGuard(value, predicate, message?)
Combine type guard with assertion for runtime type checking.
- Type:
<T>(value: unknown, predicate: (v: unknown) => v is T, message?: string) => asserts value is T - Tree-shaking: Kept in production
invariant(condition, message?)
Re-exported from tiny-invariant for advanced usage.
Tree-Shaking
The package is designed for optimal tree-shaking:
- Marked as side-effect free (
"sideEffects": falsein package.json) - Uses tiny-invariant - Bundler-friendly assertion library
- NODE_ENV checks -
devAssert()usesprocess.env.NODE_ENVfor dead code elimination - Dual format - Provides both ESM and CommonJS builds
How It Works
// Source code
export function devAssert(condition: unknown, message?: string): asserts condition {
if (process.env.NODE_ENV !== 'production') {
invariant(condition, message);
}
}
// Production build (after tree-shaking by consumer bundler)
export function devAssert(condition: unknown, message?: string): asserts condition {
// Entire function body removed!
}Verifying Tree-Shaking
1. Check Built Output
Run the verification script:
pnpm --filter @author-today-tools/utils verify-treeshakeThis confirms that the utils package build preserves the NODE_ENV check for consumer bundlers to handle.
2. Test in Consumer Builds
When you bundle a consuming package (CLI, API, TUI), the consumer's bundler (pkgroll, webpack, rollup, esbuild, vite) will:
- Replace
process.env.NODE_ENVwith the actual value (e.g.,"production") - Remove dead code branches when the condition is always false
Example with pkgroll (used by CLI/API packages):
# Development build (NODE_ENV not set or 'development')
pnpm --filter @author-today-tools/cli build
# devAssert() code is included
# Production build (NODE_ENV='production')
NODE_ENV=production pnpm --filter @author-today-tools/cli build
# devAssert() body is removed by tree-shaking3. Bundle Size Difference
Test the actual impact:
# Run the demo in development mode
node examples/tree-shaking-demo.js
# Output: devAssert() throws errors
# Run the demo in production mode
NODE_ENV=production node examples/tree-shaking-demo.js
# Output: devAssert() does nothing (tree-shaken)How Consumer Bundlers Handle It
Modern bundlers automatically:
Define Plugin (webpack) / Replace Plugin (rollup/vite):
- Replaces
process.env.NODE_ENVwith"production"string literal
- Replaces
Terser / Minifier:
- Evaluates
if ("production" !== "production")→if (false) - Removes unreachable code (dead code elimination)
- Evaluates
Result:
devAssert()function exists but has empty body in production
No configuration needed - works out of the box with:
- pkgroll (used by this monorepo)
- Vite
- Rollup
- webpack (with mode: 'production')
- esbuild
- Parcel
Best Practices
- Use
assert()for critical invariants that should always be checked - Use
devAssert()for expensive checks only needed during development - Use
assertDefined()for null safety instead of type assertions (as,!) - Use
assertNever()in exhaustive switches to catch missing cases at compile time - Combine with type guards using
assertTypeGuard()for reusable validation
TypeScript Configuration
Ensure your tsconfig.json has strict mode enabled:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}License
MIT
