Typeroll
Typeroll is a high-performance TypeScript declaration file generator and bundler designed to consolidate all `.d.ts` files into a single output. It focuses on speed and simplicity, making it ideal for library authors who need to ship a unified declaration file for their packages. Currently at version 0.7.7, it is under active development with a release cadence typically tied to critical bug fixes, performance enhancements, and compatibility with newer TypeScript versions. Its primary differentiator is the ability to produce a highly optimized, single declaration file, often outperforming the built-in `tsc --emitDeclarationOnly` for bundling scenarios, especially in larger projects. It provides a programmatic API for integration into build workflows.
Common errors
-
Error: Cannot find module 'typeroll' from '<your-project-path>'
cause This typically occurs when attempting to import Typeroll using CommonJS `require()` syntax in an ESM-only context, or if the package is not correctly installed.fixEnsure you are using ESM `import` syntax: `import { bundle } from 'typeroll';`. Also, verify `typeroll` is listed in your `package.json` dependencies and installed correctly (`npm install` or `yarn install`). -
TypeScript diagnostic error: 'Duplicate identifier <SymbolName>.'
cause When bundling, if multiple entry points or internal modules declare the same global symbol (e.g., a global interface, type, or variable in non-module files), Typeroll (or the underlying TypeScript compiler) will report a conflict.fixEnsure all files intended for bundling are proper modules (have `import` or `export` statements). If global declarations are intentional, consider carefully how they are exposed and ensure uniqueness, or adjust your bundling strategy to avoid including conflicting global scripts. -
TypeError: Cannot read properties of undefined (reading 'getEmitOutput')
cause This error often indicates an internal issue within Typeroll related to the TypeScript compiler API, frequently caused by an incompatible `typescript` version or a severely misconfigured `tsconfig.json` that prevents successful initial compilation.fixVerify your `typescript` peer dependency matches Typeroll's requirements. Double-check your `tsconfig.json` for any syntax errors or logical inconsistencies that might prevent `tsc` from compiling your source files successfully on its own. Run `tsc --noEmit` on your source to diagnose underlying TS errors.
Warnings
- breaking As a 0.x.x version package, Typeroll may introduce breaking changes in minor versions (e.g., 0.6.x to 0.7.x). Always review the changelog when upgrading to avoid unexpected issues with API changes or option modifications.
- gotcha Typeroll relies heavily on your `tsconfig.json` configuration. Incorrect settings, especially related to `rootDir`, `outDir`, `declaration`, `declarationMap`, or `paths` aliases, can lead to unexpected output or compilation errors within Typeroll.
- gotcha Typeroll has a peer dependency on `typescript`. Mismatches between the `typescript` version installed in your project and the version Typeroll expects can lead to subtle compilation errors or unexpected behavior due to API differences in the TypeScript compiler.
Install
-
npm install typeroll -
yarn add typeroll -
pnpm add typeroll
Imports
- bundle
const { bundle } = require('typeroll');import { bundle } from 'typeroll'; - BundleOptions
import { BundleOptions } from 'typeroll';import type { BundleOptions } from 'typeroll'; - TyperollError
import { TyperollError } from 'typeroll';
Quickstart
import { bundle } from 'typeroll';
import * as path from 'path';
import * as fs from 'fs/promises';
const projectRoot = path.resolve(__dirname, 'my-project');
const entryFile = path.join(projectRoot, 'src', 'index.ts');
const outputDir = path.join(projectRoot, 'dist');
const outputFile = path.join(outputDir, 'my-lib.d.ts');
// Create dummy project files for demonstration
async function setupDummyProject() {
await fs.mkdir(path.join(projectRoot, 'src'), { recursive: true });
await fs.mkdir(outputDir, { recursive: true });
await fs.writeFile(path.join(projectRoot, 'tsconfig.json'), JSON.stringify({
"compilerOptions": {
"target": "es2018",
"module": "esnext",
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}, null, 2));
await fs.writeFile(path.join(projectRoot, 'src', 'index.ts'), 'export * from "./utils";\nexport const version = "1.0.0";\n');
await fs.writeFile(path.join(projectRoot, 'src', 'utils.ts'), 'export function greet(name: string): string { return `Hello, ${name}!`; }\n');
}
async function runBundle() {
await setupDummyProject();
try {
console.log(`Bundling declarations from ${entryFile}...`);
const result = await bundle({
entryPoints: [entryFile],
tsConfigPath: path.join(projectRoot, 'tsconfig.json'),
outputFile,
cwd: projectRoot,
// Optional: Set to false to disable declaration map generation
// declarationMap: false,
});
console.log(`Declaration bundle written to ${outputFile}`);
console.log(`
Generated Content Preview:\n---\n${(await fs.readFile(outputFile, 'utf-8')).slice(0, 300)}...\n---`);
// Clean up dummy project
await fs.rm(projectRoot, { recursive: true, force: true });
} catch (error) {
console.error('Error bundling declarations:', error);
await fs.rm(projectRoot, { recursive: true, force: true });
process.exit(1);
}
}
runBundle();