or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

custom-transformers.mdfile-processing.mdindex.mdlogging-diagnostics.mdplugin-configuration.mdtypescript-integration.md

custom-transformers.mddocs/

0

# Custom Transformers

1

2

Experimental support for TypeScript transformers to modify the compilation process.

3

4

## Capabilities

5

6

### Transformer Interface

7

8

Define custom transformers that modify TypeScript compilation at different stages.

9

10

```typescript { .api }

11

/**

12

* Custom transformer configuration interface

13

*/

14

interface ICustomTransformer {

15

/** Transformers to run before TypeScript compilation */

16

before?: tsTypes.TransformerFactory<tsTypes.SourceFile>;

17

18

/** Transformers to run after TypeScript compilation */

19

after?: tsTypes.TransformerFactory<tsTypes.SourceFile>;

20

21

/** Transformers to run after declaration file generation */

22

afterDeclarations?: tsTypes.TransformerFactory<tsTypes.Bundle | tsTypes.SourceFile>;

23

}

24

25

/**

26

* Function that creates transformer configurations from language service

27

* @param ls - TypeScript language service instance

28

* @returns Transformer configuration or TypeScript's native CustomTransformers

29

*/

30

type TransformerFactoryCreator = (

31

ls: tsTypes.LanguageService

32

) => tsTypes.CustomTransformers | ICustomTransformer;

33

```

34

35

### Transformer Factory Creation

36

37

Create transformers that have access to the TypeScript program and type information.

38

39

```typescript { .api }

40

/**

41

* TypeScript's native transformer factory type

42

*/

43

type TransformerFactory<T extends tsTypes.Node> = (

44

context: tsTypes.TransformationContext

45

) => tsTypes.Transformer<T>;

46

47

/**

48

* TypeScript's transformation context

49

*/

50

interface TransformationContext {

51

/** Compiler options */

52

getCompilerOptions(): tsTypes.CompilerOptions;

53

54

/** Start lexical environment */

55

startLexicalEnvironment(): void;

56

57

/** End lexical environment and get hoisted declarations */

58

endLexicalEnvironment(): tsTypes.Statement[] | undefined;

59

60

/** Hoist function declaration */

61

hoistFunctionDeclaration(node: tsTypes.FunctionDeclaration): void;

62

63

/** Hoist variable declaration */

64

hoistVariableDeclaration(node: tsTypes.Identifier): void;

65

66

/** Request emit helper */

67

requestEmitHelper(helper: tsTypes.EmitHelper): void;

68

69

/** Read emit helper uniqueness */

70

readEmitHelpers(): tsTypes.EmitHelper[] | undefined;

71

72

/** Enable emit notification */

73

enableEmitNotification(kind: tsTypes.SyntaxKind): void;

74

75

/** Check if emit notification is enabled */

76

isEmitNotificationEnabled(node: tsTypes.Node): boolean;

77

78

/** Called when node is emitted */

79

onEmitNode(hint: tsTypes.EmitHint, node: tsTypes.Node, emitCallback: (hint: tsTypes.EmitHint, node: tsTypes.Node) => void): void;

80

81

/** Enable substitution */

82

enableSubstitution(kind: tsTypes.SyntaxKind): void;

83

84

/** Check if substitution is enabled */

85

isSubstitutionEnabled(node: tsTypes.Node): boolean;

86

87

/** Called when node is substituted */

88

onSubstituteNode(hint: tsTypes.EmitHint, node: tsTypes.Node): tsTypes.Node;

89

}

90

```

91

92

### Usage Examples

93

94

**Basic Transformer:**

95

96

```javascript

97

import typescript from 'rollup-plugin-typescript2';

98

99

// Simple transformer that adds console.log to functions

100

const addLoggingTransformer = (program) => {

101

return (context) => {

102

return (sourceFile) => {

103

function visit(node) {

104

if (typescript.isFunctionDeclaration(node) && node.name) {

105

// Add console.log at start of function

106

const logStatement = typescript.factory.createExpressionStatement(

107

typescript.factory.createCallExpression(

108

typescript.factory.createPropertyAccessExpression(

109

typescript.factory.createIdentifier('console'),

110

typescript.factory.createIdentifier('log')

111

),

112

undefined,

113

[typescript.factory.createStringLiteral(`Entering ${node.name.text}`)]

114

)

115

);

116

117

const updatedBody = typescript.factory.updateBlock(

118

node.body,

119

[logStatement, ...node.body.statements]

120

);

121

122

return typescript.factory.updateFunctionDeclaration(

123

node,

124

node.decorators,

125

node.modifiers,

126

node.asteriskToken,

127

node.name,

128

node.typeParameters,

129

node.parameters,

130

node.type,

131

updatedBody

132

);

133

}

134

return typescript.visitEachChild(node, visit, context);

135

}

136

return typescript.visitNode(sourceFile, visit);

137

};

138

};

139

};

140

141

// Use in plugin configuration

142

export default {

143

plugins: [

144

typescript({

145

transformers: [

146

(service) => ({

147

before: [addLoggingTransformer(service.getProgram())]

148

})

149

]

150

})

151

]

152

};

153

```

154

155

**Advanced Transformer with Type Information:**

156

157

```javascript

158

import keysTransformer from 'ts-transformer-keys/transformer';

159

160

// Using ts-transformer-keys for compile-time type key extraction

161

const keysTransformerFactory = (service) => {

162

const program = service.getProgram();

163

const typeChecker = program.getTypeChecker();

164

165

return {

166

before: [keysTransformer(program).before],

167

after: []

168

};

169

};

170

171

typescript({

172

transformers: [keysTransformerFactory]

173

})

174

```

175

176

### Transformer Categories

177

178

Transformers can be applied at different stages of the compilation process:

179

180

```typescript { .api }

181

/**

182

* Transformer execution stages

183

*/

184

interface TransformerStages {

185

/**

186

* Before TypeScript compilation

187

* - Operates on TypeScript AST before type checking

188

* - Can modify source code structure

189

* - Has access to full type information

190

*/

191

before: tsTypes.TransformerFactory<tsTypes.SourceFile>[];

192

193

/**

194

* After TypeScript compilation

195

* - Operates on JavaScript AST after compilation

196

* - Can modify generated JavaScript

197

* - Limited type information available

198

*/

199

after: tsTypes.TransformerFactory<tsTypes.SourceFile>[];

200

201

/**

202

* After declaration file generation

203

* - Operates on declaration files (.d.ts)

204

* - Can modify type definitions

205

* - Useful for documentation generation

206

*/

207

afterDeclarations: tsTypes.TransformerFactory<tsTypes.Bundle | tsTypes.SourceFile>[];

208

}

209

```

210

211

### Common Transformer Patterns

212

213

**Metadata Collection:**

214

215

```javascript

216

// Collect class metadata for dependency injection

217

const metadataTransformer = (program) => {

218

const typeChecker = program.getTypeChecker();

219

220

return (context) => {

221

return (sourceFile) => {

222

function visit(node) {

223

if (typescript.isClassDeclaration(node)) {

224

// Extract constructor parameter types

225

const constructor = node.members.find(

226

member => typescript.isConstructorDeclaration(member)

227

);

228

229

if (constructor) {

230

const paramTypes = constructor.parameters.map(param => {

231

const type = typeChecker.getTypeAtLocation(param);

232

return typeChecker.typeToString(type);

233

});

234

235

// Add metadata as static property

236

const metadataProperty = typescript.factory.createPropertyDeclaration(

237

undefined,

238

[typescript.factory.createModifier(typescript.SyntaxKind.StaticKeyword)],

239

'metadata',

240

undefined,

241

undefined,

242

typescript.factory.createArrayLiteralExpression(

243

paramTypes.map(type =>

244

typescript.factory.createStringLiteral(type)

245

)

246

)

247

);

248

249

return typescript.factory.updateClassDeclaration(

250

node,

251

node.decorators,

252

node.modifiers,

253

node.name,

254

node.typeParameters,

255

node.heritageClauses,

256

[...node.members, metadataProperty]

257

);

258

}

259

}

260

return typescript.visitEachChild(node, visit, context);

261

}

262

return typescript.visitNode(sourceFile, visit);

263

};

264

};

265

};

266

```

267

268

**Code Generation:**

269

270

```javascript

271

// Generate getter/setter methods for properties

272

const accessorTransformer = (program) => {

273

return (context) => {

274

return (sourceFile) => {

275

function visit(node) {

276

if (typescript.isClassDeclaration(node)) {

277

const newMembers = [];

278

279

for (const member of node.members) {

280

newMembers.push(member);

281

282

if (typescript.isPropertyDeclaration(member) && member.name) {

283

const propertyName = member.name.getText();

284

285

// Generate getter

286

const getter = typescript.factory.createGetAccessorDeclaration(

287

undefined,

288

undefined,

289

propertyName,

290

[],

291

undefined,

292

typescript.factory.createBlock([

293

typescript.factory.createReturnStatement(

294

typescript.factory.createPropertyAccessExpression(

295

typescript.factory.createThis(),

296

`_${propertyName}`

297

)

298

)

299

])

300

);

301

302

// Generate setter

303

const setter = typescript.factory.createSetAccessorDeclaration(

304

undefined,

305

undefined,

306

propertyName,

307

[typescript.factory.createParameterDeclaration(

308

undefined,

309

undefined,

310

undefined,

311

'value',

312

undefined,

313

member.type

314

)],

315

typescript.factory.createBlock([

316

typescript.factory.createExpressionStatement(

317

typescript.factory.createBinaryExpression(

318

typescript.factory.createPropertyAccessExpression(

319

typescript.factory.createThis(),

320

`_${propertyName}`

321

),

322

typescript.SyntaxKind.EqualsToken,

323

typescript.factory.createIdentifier('value')

324

)

325

)

326

])

327

);

328

329

newMembers.push(getter, setter);

330

}

331

}

332

333

return typescript.factory.updateClassDeclaration(

334

node,

335

node.decorators,

336

node.modifiers,

337

node.name,

338

node.typeParameters,

339

node.heritageClauses,

340

newMembers

341

);

342

}

343

return typescript.visitEachChild(node, visit, context);

344

}

345

return typescript.visitNode(sourceFile, visit);

346

};

347

};

348

};

349

```

350

351

### Integration with TypeScript Language Service

352

353

Transformers have full access to TypeScript's language service capabilities:

354

355

```typescript { .api }

356

/**

357

* Language service features available to transformers

358

*/

359

interface LanguageServiceFeatures {

360

/** Get the TypeScript program */

361

getProgram(): tsTypes.Program;

362

363

/** Get type checker for type analysis */

364

getTypeChecker(): tsTypes.TypeChecker;

365

366

/** Get semantic diagnostics */

367

getSemanticDiagnostics(fileName: string): tsTypes.Diagnostic[];

368

369

/** Get syntactic diagnostics */

370

getSyntacticDiagnostics(fileName: string): tsTypes.Diagnostic[];

371

372

/** Get quick info at position */

373

getQuickInfoAtPosition(fileName: string, position: number): tsTypes.QuickInfo | undefined;

374

375

/** Get definitions at position */

376

getDefinitionAtPosition(fileName: string, position: number): tsTypes.DefinitionInfo[] | undefined;

377

}

378

```

379

380

**Type-Aware Transformer:**

381

382

```javascript

383

const typeAwareTransformer = (service) => {

384

const program = service.getProgram();

385

const typeChecker = program.getTypeChecker();

386

387

return {

388

before: [(context) => {

389

return (sourceFile) => {

390

function visit(node) {

391

if (typescript.isCallExpression(node)) {

392

// Get type information for function calls

393

const signature = typeChecker.getResolvedSignature(node);

394

if (signature) {

395

const returnType = typeChecker.getReturnTypeOfSignature(signature);

396

const typeName = typeChecker.typeToString(returnType);

397

398

// Add type information as comment

399

typescript.addSyntheticLeadingComment(

400

node,

401

typescript.SyntaxKind.MultiLineCommentTrivia,

402

` Returns: ${typeName} `,

403

false

404

);

405

}

406

}

407

return typescript.visitEachChild(node, visit, context);

408

}

409

return typescript.visitNode(sourceFile, visit);

410

};

411

}]

412

};

413

};

414

```

415

416

### Limitations and Considerations

417

418

**Important Notes:**

419

420

- Transformers are experimental and the API may change

421

- TypeScript may eventually add native transformer support to tsconfig

422

- Not all transformer libraries are compatible with this plugin

423

- Transformers can significantly impact build performance

424

- Complex transformers may interfere with source maps

425

426

**Best Practices:**

427

428

- Keep transformers as simple as possible

429

- Test thoroughly with different TypeScript versions

430

- Document any custom transformers used in your project

431

- Consider performance impact on large codebases

432

- Use verbosity level 3 to debug transformer issues