Seamless integration between Rollup and TypeScript with comprehensive error reporting.
—
Experimental support for TypeScript transformers to modify the compilation process.
Define custom transformers that modify TypeScript compilation at different stages.
/**
* Custom transformer configuration interface
*/
interface ICustomTransformer {
/** Transformers to run before TypeScript compilation */
before?: tsTypes.TransformerFactory<tsTypes.SourceFile>;
/** Transformers to run after TypeScript compilation */
after?: tsTypes.TransformerFactory<tsTypes.SourceFile>;
/** Transformers to run after declaration file generation */
afterDeclarations?: tsTypes.TransformerFactory<tsTypes.Bundle | tsTypes.SourceFile>;
}
/**
* Function that creates transformer configurations from language service
* @param ls - TypeScript language service instance
* @returns Transformer configuration or TypeScript's native CustomTransformers
*/
type TransformerFactoryCreator = (
ls: tsTypes.LanguageService
) => tsTypes.CustomTransformers | ICustomTransformer;Create transformers that have access to the TypeScript program and type information.
/**
* TypeScript's native transformer factory type
*/
type TransformerFactory<T extends tsTypes.Node> = (
context: tsTypes.TransformationContext
) => tsTypes.Transformer<T>;
/**
* TypeScript's transformation context
*/
interface TransformationContext {
/** Compiler options */
getCompilerOptions(): tsTypes.CompilerOptions;
/** Start lexical environment */
startLexicalEnvironment(): void;
/** End lexical environment and get hoisted declarations */
endLexicalEnvironment(): tsTypes.Statement[] | undefined;
/** Hoist function declaration */
hoistFunctionDeclaration(node: tsTypes.FunctionDeclaration): void;
/** Hoist variable declaration */
hoistVariableDeclaration(node: tsTypes.Identifier): void;
/** Request emit helper */
requestEmitHelper(helper: tsTypes.EmitHelper): void;
/** Read emit helper uniqueness */
readEmitHelpers(): tsTypes.EmitHelper[] | undefined;
/** Enable emit notification */
enableEmitNotification(kind: tsTypes.SyntaxKind): void;
/** Check if emit notification is enabled */
isEmitNotificationEnabled(node: tsTypes.Node): boolean;
/** Called when node is emitted */
onEmitNode(hint: tsTypes.EmitHint, node: tsTypes.Node, emitCallback: (hint: tsTypes.EmitHint, node: tsTypes.Node) => void): void;
/** Enable substitution */
enableSubstitution(kind: tsTypes.SyntaxKind): void;
/** Check if substitution is enabled */
isSubstitutionEnabled(node: tsTypes.Node): boolean;
/** Called when node is substituted */
onSubstituteNode(hint: tsTypes.EmitHint, node: tsTypes.Node): tsTypes.Node;
}Basic Transformer:
import typescript from 'rollup-plugin-typescript2';
// Simple transformer that adds console.log to functions
const addLoggingTransformer = (program) => {
return (context) => {
return (sourceFile) => {
function visit(node) {
if (typescript.isFunctionDeclaration(node) && node.name) {
// Add console.log at start of function
const logStatement = typescript.factory.createExpressionStatement(
typescript.factory.createCallExpression(
typescript.factory.createPropertyAccessExpression(
typescript.factory.createIdentifier('console'),
typescript.factory.createIdentifier('log')
),
undefined,
[typescript.factory.createStringLiteral(`Entering ${node.name.text}`)]
)
);
const updatedBody = typescript.factory.updateBlock(
node.body,
[logStatement, ...node.body.statements]
);
return typescript.factory.updateFunctionDeclaration(
node,
node.decorators,
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
updatedBody
);
}
return typescript.visitEachChild(node, visit, context);
}
return typescript.visitNode(sourceFile, visit);
};
};
};
// Use in plugin configuration
export default {
plugins: [
typescript({
transformers: [
(service) => ({
before: [addLoggingTransformer(service.getProgram())]
})
]
})
]
};Advanced Transformer with Type Information:
import keysTransformer from 'ts-transformer-keys/transformer';
// Using ts-transformer-keys for compile-time type key extraction
const keysTransformerFactory = (service) => {
const program = service.getProgram();
const typeChecker = program.getTypeChecker();
return {
before: [keysTransformer(program).before],
after: []
};
};
typescript({
transformers: [keysTransformerFactory]
})Transformers can be applied at different stages of the compilation process:
/**
* Transformer execution stages
*/
interface TransformerStages {
/**
* Before TypeScript compilation
* - Operates on TypeScript AST before type checking
* - Can modify source code structure
* - Has access to full type information
*/
before: tsTypes.TransformerFactory<tsTypes.SourceFile>[];
/**
* After TypeScript compilation
* - Operates on JavaScript AST after compilation
* - Can modify generated JavaScript
* - Limited type information available
*/
after: tsTypes.TransformerFactory<tsTypes.SourceFile>[];
/**
* After declaration file generation
* - Operates on declaration files (.d.ts)
* - Can modify type definitions
* - Useful for documentation generation
*/
afterDeclarations: tsTypes.TransformerFactory<tsTypes.Bundle | tsTypes.SourceFile>[];
}Metadata Collection:
// Collect class metadata for dependency injection
const metadataTransformer = (program) => {
const typeChecker = program.getTypeChecker();
return (context) => {
return (sourceFile) => {
function visit(node) {
if (typescript.isClassDeclaration(node)) {
// Extract constructor parameter types
const constructor = node.members.find(
member => typescript.isConstructorDeclaration(member)
);
if (constructor) {
const paramTypes = constructor.parameters.map(param => {
const type = typeChecker.getTypeAtLocation(param);
return typeChecker.typeToString(type);
});
// Add metadata as static property
const metadataProperty = typescript.factory.createPropertyDeclaration(
undefined,
[typescript.factory.createModifier(typescript.SyntaxKind.StaticKeyword)],
'metadata',
undefined,
undefined,
typescript.factory.createArrayLiteralExpression(
paramTypes.map(type =>
typescript.factory.createStringLiteral(type)
)
)
);
return typescript.factory.updateClassDeclaration(
node,
node.decorators,
node.modifiers,
node.name,
node.typeParameters,
node.heritageClauses,
[...node.members, metadataProperty]
);
}
}
return typescript.visitEachChild(node, visit, context);
}
return typescript.visitNode(sourceFile, visit);
};
};
};Code Generation:
// Generate getter/setter methods for properties
const accessorTransformer = (program) => {
return (context) => {
return (sourceFile) => {
function visit(node) {
if (typescript.isClassDeclaration(node)) {
const newMembers = [];
for (const member of node.members) {
newMembers.push(member);
if (typescript.isPropertyDeclaration(member) && member.name) {
const propertyName = member.name.getText();
// Generate getter
const getter = typescript.factory.createGetAccessorDeclaration(
undefined,
undefined,
propertyName,
[],
undefined,
typescript.factory.createBlock([
typescript.factory.createReturnStatement(
typescript.factory.createPropertyAccessExpression(
typescript.factory.createThis(),
`_${propertyName}`
)
)
])
);
// Generate setter
const setter = typescript.factory.createSetAccessorDeclaration(
undefined,
undefined,
propertyName,
[typescript.factory.createParameterDeclaration(
undefined,
undefined,
undefined,
'value',
undefined,
member.type
)],
typescript.factory.createBlock([
typescript.factory.createExpressionStatement(
typescript.factory.createBinaryExpression(
typescript.factory.createPropertyAccessExpression(
typescript.factory.createThis(),
`_${propertyName}`
),
typescript.SyntaxKind.EqualsToken,
typescript.factory.createIdentifier('value')
)
)
])
);
newMembers.push(getter, setter);
}
}
return typescript.factory.updateClassDeclaration(
node,
node.decorators,
node.modifiers,
node.name,
node.typeParameters,
node.heritageClauses,
newMembers
);
}
return typescript.visitEachChild(node, visit, context);
}
return typescript.visitNode(sourceFile, visit);
};
};
};Transformers have full access to TypeScript's language service capabilities:
/**
* Language service features available to transformers
*/
interface LanguageServiceFeatures {
/** Get the TypeScript program */
getProgram(): tsTypes.Program;
/** Get type checker for type analysis */
getTypeChecker(): tsTypes.TypeChecker;
/** Get semantic diagnostics */
getSemanticDiagnostics(fileName: string): tsTypes.Diagnostic[];
/** Get syntactic diagnostics */
getSyntacticDiagnostics(fileName: string): tsTypes.Diagnostic[];
/** Get quick info at position */
getQuickInfoAtPosition(fileName: string, position: number): tsTypes.QuickInfo | undefined;
/** Get definitions at position */
getDefinitionAtPosition(fileName: string, position: number): tsTypes.DefinitionInfo[] | undefined;
}Type-Aware Transformer:
const typeAwareTransformer = (service) => {
const program = service.getProgram();
const typeChecker = program.getTypeChecker();
return {
before: [(context) => {
return (sourceFile) => {
function visit(node) {
if (typescript.isCallExpression(node)) {
// Get type information for function calls
const signature = typeChecker.getResolvedSignature(node);
if (signature) {
const returnType = typeChecker.getReturnTypeOfSignature(signature);
const typeName = typeChecker.typeToString(returnType);
// Add type information as comment
typescript.addSyntheticLeadingComment(
node,
typescript.SyntaxKind.MultiLineCommentTrivia,
` Returns: ${typeName} `,
false
);
}
}
return typescript.visitEachChild(node, visit, context);
}
return typescript.visitNode(sourceFile, visit);
};
}]
};
};Important Notes:
Best Practices:
Install with Tessl CLI
npx tessl i tessl/npm-rollup-plugin-typescript2