@rollup/plugin-typescript
🍣 A Rollup plugin for seamless integration between Rollup and Typescript.
Requirements
This plugin requires an LTS Node version (v14.0.0+) and Rollup v2.14.0+. This plugin also requires at least TypeScript 3.7.
Install
Using npm:
npm install @rollup/plugin-typescript --save-devNote that both typescript and tslib are peer dependencies of this plugin that need to be installed separately.
Why?
See @rollup/plugin-babel.
Usage
Create a rollup.config.js configuration file and import the plugin:
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
export default {
input: 'src/index.ts',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [typescript()]
};Then call rollup either via the CLI or the API.
Options
The plugin loads any compilerOptions from the tsconfig.json file by default. Passing options to the plugin directly overrides those options:
...
export default {
input: './main.ts',
plugins: [
typescript({ compilerOptions: {lib: ["es5", "es6", "dom"], target: "es5"}})
]
}The following options are unique to @rollup/plugin-typescript:
exclude
Type: String | Array[...String]
Default: null
A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should ignore. By default no files are ignored.
include
Type: String | Array[...String]
Default: null
A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. By default all .ts and .tsx files are targeted. If your tsconfig.json (or plugin compilerOptions) sets allowJs: true, the default include expands to also cover .js, .jsx, .mjs, and .cjs files so that JavaScript sources are downleveled by TypeScript as well.
Note: When
allowJsis enabled and nooutDiris configured, this plugin creates a temporary output directory for TypeScript emit to avoid TS5055 (overwriting input files). The effectiveoutDir(whether user-provided or the temporary one) is excluded from plugin processing to prevent re-processing emitted files. The temporary directory is removed after a non-watch build completes; in watch mode it is removed when the watcher stops.When
allowJsexpands the default include and you have not specified patterns viainclude/exclude, the plugin also excludes**/node_modules/**by default to avoid transforming third-party code.
filterRoot
Type: String | Boolean
Default: rootDir ?? tsConfig.compilerOptions.rootDir ?? process.cwd()
Optionally resolves the include and exclude patterns against a directory other than process.cwd(). If a String is specified, then the value will be used as the base directory. Relative paths will be resolved against process.cwd() first. If false, then the patterns will not be resolved against any directory.
By default, patterns resolve against the rootDir set in your TS config file.
This can fix plugin errors when parsing files outside the current working directory (process.cwd()).
tsconfig
Type: String | Boolean
Default: true
When set to false, ignores any options specified in the config file. If set to a string that corresponds to a file path, the specified file will be used as config file.
typescript
Type: import('typescript')
Default: peer dependency
Overrides the TypeScript module used for transpilation.
typescript({
typescript: require('some-fork-of-typescript')
});tslib
Type: String
Default: peer dependency
Overrides the injected TypeScript helpers with a custom version.
typescript({
tslib: require.resolve('some-fork-of-tslib')
});transformers
Type: { [before | after | afterDeclarations]: TransformerFactory[] } | ((program: ts.Program) => ts.CustomTransformers)
Default: undefined
Allows registration of TypeScript custom transformers at any of the supported stages:
- before: transformers will execute before the TypeScript's own transformers on raw TypeScript files
- after: transformers will execute after the TypeScript transformers on transpiled code
- afterDeclarations: transformers will execute after declaration file generation allowing to modify existing declaration files
Supported transformer factories:
all built-in TypeScript custom transformer factories:
import('typescript').TransformerFactoryannotated TransformerFactory belowimport('typescript').CustomTransformerFactoryannotated CustomTransformerFactory below
ProgramTransformerFactory represents a transformer factory allowing the resulting transformer to grab a reference to the Program instance
{ type: 'program', // An optional `getProgram` getter is provided in all modes. In non‑watch it returns // the same Program as the first argument. In watch mode, when the // `recreateTransformersOnRebuild` option is enabled, the getter reflects the latest // Program across rebuilds; otherwise it refers to the initial Program. factory: (program: Program, getProgram?: () => Program) => TransformerFactory | CustomTransformerFactory }TypeCheckerTransformerFactory represents a transformer factory allowing the resulting transformer to grab a reference to the TypeChecker instance
{ type: 'typeChecker', factory: (typeChecker: TypeChecker) => TransformerFactory | CustomTransformerFactory }
typescript({
transformers: {
before: [
{
// Allow the transformer to get a Program reference in its factory.
// Prefer deferring `getProgram()` usage to transformation time so watch
// mode can see the freshest Program when `recreateTransformersOnRebuild`
// is enabled.
type: 'program',
factory: (program, getProgram) => {
const get = getProgram ?? (() => program);
return (context) => (source) => {
const latest = get();
// use `latest` here
return ts.visitEachChild(source, (n) => n, context);
};
}
},
{
type: 'typeChecker',
factory: (typeChecker) => {
// Allow the transformer to get a TypeChecker reference in its factory
return TypeCheckerRequiringTransformerFactory(typeChecker);
}
}
],
after: [
// You can use normal transformers directly
require('custom-transformer-based-on-Context')
],
afterDeclarations: [
// Or even define in place
function fixDeclarationFactory(context) {
return function fixDeclaration(source) {
function visitor(node) {
// Do real work here
return ts.visitEachChild(node, visitor, context);
}
return ts.visitEachChild(source, visitor, context);
};
}
]
}
});Alternatively, the transformers can be created inside a factory.
Supported transformer factories:
all built-in TypeScript custom transformer factories:
import('typescript').TransformerFactoryannotated TransformerFactory belowimport('typescript').CustomTransformerFactoryannotated CustomTransformerFactory below
The example above could be written like this:
typescript({
transformers: function (program) {
return {
before: [
ProgramRequiringTransformerFactory(program),
TypeCheckerRequiringTransformerFactory(program.getTypeChecker())
],
after: [
// You can use normal transformers directly
require('custom-transformer-based-on-Context')
],
afterDeclarations: [
// Or even define in place
function fixDeclarationFactory(context) {
return function fixDeclaration(source) {
function visitor(node) {
// Do real work here
return ts.visitEachChild(node, visitor, context);
}
return ts.visitEachChild(source, visitor, context);
};
}
]
};
}
});Note on watch mode
By default (legacy behavior), this plugin reuses the same custom transformer factories for the lifetime of a watch session. Advanced users can opt into recreating factories on every TypeScript rebuild by enabling the recreateTransformersOnRebuild option. When enabled, both program- and typeChecker-based factories are rebuilt per watch cycle, and getProgram() (when used) reflects the latest Program across rebuilds.
recreateTransformersOnRebuild
Type: Boolean
Default: false (legacy behavior)
When true, the plugin recreates custom transformer factories on each TypeScript watch rebuild. This ensures factories capture the current Program/TypeChecker per cycle and that the optional getProgram() getter provided to program-based factories reflects the latest Program across rebuilds. Most users do not need this; enable it if your transformers depend on up‑to‑date Program/TypeChecker identities.
// Opt-in to per-rebuild transformer recreation in watch mode
typescript({
recreateTransformersOnRebuild: true,
transformers: {
before: [
{
type: 'program',
factory(program, getProgram) {
/* ... */
}
}
]
}
});cacheDir
Type: String
Default: .rollup.cache
When compiling with incremental or composite options the plugin will
store compiled files in this folder. This allows the use of incremental
compilation.
typescript({
cacheDir: '.rollup.tscache'
});noForceEmit
Type: Boolean
Default: false
Earlier version of @rollup/plugin-typescript required that the compilerOptions noEmit and emitDeclarationOnly both false to guarantee that source code was fed into the next plugin/output. This is no longer true. This option disables the plugin forcing the values of those options and instead defers to the values set in tsconfig.json.
noForceEmit can be very useful if you use with @rollup/plugin-babel and @babel/preset-typescript. Having @rollup/plugin-typescript only do typechecking / declarations with "emitDeclarationOnly": true while deferring to @rollup/plugin-babel for transpilation can dramatically reduce rollup build times for large projects.
Typescript compiler options
Some of Typescript's CompilerOptions affect how Rollup builds files.
noEmitOnError
Type: Boolean
Default: false
If a type error is detected, the Rollup build is aborted when this option is set to true.
files, include, exclude
Type: Array[...String]
Default: []
Declaration files are automatically included if they are listed in the files field in your tsconfig.json file. Source files in these fields are ignored as Rollup's configuration is used instead.
Ignored options
These compiler options are ignored by Rollup:
noEmitHelpers,importHelpers: Thetslibhelper module always must be used.noEmit,emitDeclarationOnly: Typescript needs to emit code for the plugin to work with.- Note: While this was true for early iterations of
@rollup/plugin-typescript, it is no longer. To override this behavior, and defer totsconfig.jsonfor these options, see thenoForceEmitoption
- Note: While this was true for early iterations of
noResolve: Preventing Typescript from resolving code may break compilation
Importing CommonJS
Though it is not recommended, it is possible to configure this plugin to handle imports of CommonJS files from TypeScript. For this, you need to specify CommonJS as the module format and add @rollup/plugin-commonjs to transpile the CommonJS output generated by TypeScript to ES Modules so that rollup can process it.
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import commonjs from '@rollup/plugin-commonjs';
export default {
input: './main.ts',
plugins: [
typescript({ compilerOptions: { module: 'CommonJS' } }),
commonjs({ extensions: ['.js', '.ts'] }) // the ".ts" extension is required
]
};Note that this will often result in less optimal output.
Preserving JSX output
Whenever choosing to preserve JSX output to be further consumed by another transform step via tsconfig compilerOptions by setting jsx: 'preserve' or overriding options, please bear in mind that, by itself, this plugin won't be able to preserve JSX output, usually failing with:
[!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
file.tsx (1:15)
1: export default <span>Foobar</span>
^To prevent that, make sure to use the acorn plugin, namely acorn-jsx, which will make Rollup's parser acorn handle JSX tokens. (See https://rollupjs.org/guide/en/#acorninjectplugins)
After adding acorn-jsx plugin, your Rollup config would look like the following, correctly preserving your JSX output.
import jsx from 'acorn-jsx';
import typescript from '@rollup/plugin-typescript';
export default {
// … other options …
acornInjectPlugins: [jsx()],
plugins: [typescript({ compilerOptions: { jsx: 'preserve' } })]
};Faster compiling
Previous versions of this plugin used Typescript's transpileModule API, which is faster but does not perform typechecking and does not support cross-file features like const enums and emit-less types. If you want this behaviour, you can use @rollup/plugin-sucrase instead.