or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-context.mdbuild-plugins.mdcontext-creation.mdindex.mdnamespace-management.md

build-plugins.mddocs/

0

# Build-time Transformation

1

2

Build-time transformation plugins automatically transform async functions to preserve context across `await` statements, enabling universal async context support without runtime dependencies.

3

4

## API Reference

5

6

```typescript { .api }

7

/**

8

* Universal bundler plugin for async context transformation

9

* Created with createUnplugin, provides bundler-specific methods

10

*/

11

export const unctxPlugin: {

12

rollup(options?: UnctxPluginOptions): Plugin;

13

vite(options?: UnctxPluginOptions): Plugin;

14

webpack(options?: UnctxPluginOptions): Plugin;

15

};

16

17

/**

18

* Create AST transformer for async context preservation

19

* @param options - Transformation configuration

20

* @returns Transformer with transform and shouldTransform methods

21

*/

22

function createTransformer(options?: TransformerOptions): {

23

transform(code: string, options?: { force?: false }): TransformResult | undefined;

24

shouldTransform(code: string): boolean;

25

};

26

27

interface UnctxPluginOptions extends TransformerOptions {

28

/**

29

* Filter function to determine which files to transform

30

* @param id - File path/identifier

31

* @returns true if file should be transformed

32

*/

33

transformInclude?: (id: string) => boolean;

34

}

35

36

interface TransformerOptions {

37

/**

38

* Function names to be transformed for async context preservation

39

* @default ['withAsyncContext']

40

*/

41

asyncFunctions?: string[];

42

43

/**

44

* Module name to import helper functions from

45

* @default 'unctx'

46

*/

47

helperModule?: string;

48

49

/**

50

* Name of the helper function for async execution

51

* @default 'executeAsync'

52

*/

53

helperName?: string;

54

55

/**

56

* Object property transformation rules

57

* Map function names to property names to transform

58

* @default {}

59

*/

60

objectDefinitions?: Record<string, string[]>;

61

}

62

63

interface TransformResult {

64

code: string;

65

magicString: MagicString;

66

}

67

```

68

69

## Plugin Installation

70

71

### Vite

72

73

```javascript

74

// vite.config.js

75

import { unctxPlugin } from "unctx/plugin";

76

77

export default {

78

plugins: [

79

unctxPlugin.vite({

80

// Transform functions named 'withAsyncContext' and 'callAsync'

81

asyncFunctions: ['withAsyncContext', 'callAsync'],

82

83

// Only transform specific files

84

transformInclude: (id) => {

85

return id.includes('src/') && !id.includes('node_modules/');

86

}

87

})

88

]

89

};

90

```

91

92

### Rollup

93

94

```javascript

95

// rollup.config.js

96

import { unctxPlugin } from "unctx/plugin";

97

98

export default {

99

plugins: [

100

unctxPlugin.rollup({

101

asyncFunctions: ['withAsyncContext', 'callAsync'],

102

transformInclude: (id) => id.endsWith('.ts') || id.endsWith('.js')

103

})

104

]

105

};

106

```

107

108

### Webpack

109

110

```javascript

111

// webpack.config.js

112

const { unctxPlugin } = require("unctx/plugin");

113

114

module.exports = {

115

plugins: [

116

unctxPlugin.webpack({

117

asyncFunctions: ['withAsyncContext'],

118

helperModule: 'unctx',

119

helperName: 'executeAsync'

120

})

121

]

122

};

123

```

124

125

## Transformation Process

126

127

### Before Transformation

128

129

```typescript

130

import { createContext, withAsyncContext } from "unctx";

131

132

const userContext = createContext<User>();

133

134

const processUser = withAsyncContext(async () => {

135

console.log(userContext.use()); // Available

136

137

await fetch("/api/data");

138

139

console.log(userContext.use()); // Would be undefined without transformation

140

141

await processMoreData();

142

143

return userContext.use();

144

});

145

```

146

147

### After Transformation

148

149

The plugin automatically transforms the code to:

150

151

```typescript

152

import { executeAsync as __executeAsync } from "unctx";

153

import { createContext, withAsyncContext } from "unctx";

154

155

const userContext = createContext<User>();

156

157

const processUser = withAsyncContext(async () => {

158

let __temp, __restore;

159

160

console.log(userContext.use()); // Available

161

162

(([__temp, __restore] = __executeAsync(() => fetch("/api/data"))),

163

__temp = await __temp, __restore(), __temp);

164

165

console.log(userContext.use()); // ✅ Context restored automatically

166

167

(([__temp, __restore] = __executeAsync(() => processMoreData())),

168

__temp = await __temp, __restore(), __temp);

169

170

return userContext.use();

171

}, 1);

172

```

173

174

## Using callAsync with Transformation

175

176

Enable `callAsync` transformation by adding it to `asyncFunctions`:

177

178

```javascript

179

// Build configuration

180

unctxPlugin.vite({

181

asyncFunctions: ['withAsyncContext', 'callAsync']

182

})

183

```

184

185

```typescript

186

import { createContext } from "unctx";

187

188

const ctx = createContext<Data>();

189

190

// Now callAsync is automatically transformed

191

await ctx.callAsync(data, async () => {

192

console.log(ctx.use()); // Available

193

194

await operation1();

195

console.log(ctx.use()); // ✅ Restored automatically

196

197

await operation2();

198

console.log(ctx.use()); // ✅ Restored automatically

199

});

200

```

201

202

## Custom Function Transformation

203

204

Transform your own async functions:

205

206

```javascript

207

// Build configuration

208

unctxPlugin.vite({

209

asyncFunctions: ['withAsyncContext', 'myAsyncWrapper', 'customAsync']

210

})

211

```

212

213

```typescript

214

// Define custom async wrapper

215

function myAsyncWrapper<T>(fn: () => Promise<T>): () => Promise<T> {

216

return fn; // Actual implementation

217

}

218

219

// Usage - will be transformed

220

const handler = myAsyncWrapper(async () => {

221

const ctx = myContext.use();

222

await asyncOperation();

223

return myContext.use(); // Context preserved

224

});

225

```

226

227

## Object Property Transformation

228

229

Transform specific properties within object definitions:

230

231

```javascript

232

// Build configuration

233

unctxPlugin.vite({

234

objectDefinitions: {

235

'defineMiddleware': ['handler'],

236

'defineRoute': ['middleware', 'handler']

237

}

238

})

239

```

240

241

```typescript

242

// These object properties will be transformed

243

const middleware = defineMiddleware({

244

handler: async (req, res) => {

245

const ctx = requestContext.use();

246

await processRequest();

247

// Context automatically restored

248

return requestContext.use();

249

}

250

});

251

252

const route = defineRoute({

253

path: '/api/users',

254

middleware: async (req, res, next) => {

255

await authenticate();

256

// Context preserved

257

next();

258

},

259

handler: async (req, res) => {

260

await handleRequest();

261

// Context preserved

262

}

263

});

264

```

265

266

## Manual Transformer Usage

267

268

Create custom transformers for advanced use cases:

269

270

```typescript

271

import { createTransformer } from "unctx/transform";

272

273

const transformer = createTransformer({

274

asyncFunctions: ['myAsyncFn'],

275

helperModule: 'my-context-lib',

276

helperName: 'executeWithContext'

277

});

278

279

// Check if code needs transformation

280

const code = `

281

const handler = myAsyncFn(async () => {

282

await fetch('/api');

283

});

284

`;

285

286

if (transformer.shouldTransform(code)) {

287

const result = transformer.transform(code);

288

console.log(result.code); // Transformed code

289

}

290

```

291

292

## TypeScript Integration

293

294

The plugin works seamlessly with TypeScript:

295

296

```typescript

297

// tsconfig.json - no special configuration needed

298

{

299

"compilerOptions": {

300

"target": "ES2020",

301

"module": "ESNext"

302

}

303

}

304

```

305

306

```typescript

307

// TypeScript code is transformed correctly

308

interface UserData {

309

id: string;

310

name: string;

311

}

312

313

const userContext = createContext<UserData>();

314

315

const processUser = withAsyncContext(async (): Promise<UserData> => {

316

const user = userContext.use(); // Typed correctly

317

318

await validateUser(user);

319

320

return userContext.use(); // TypeScript knows this is UserData

321

});

322

```

323

324

## File Filtering

325

326

Control which files are transformed:

327

328

```javascript

329

unctxPlugin.vite({

330

transformInclude: (id) => {

331

// Only transform application code, skip node_modules

332

if (id.includes('node_modules/')) return false;

333

334

// Only transform specific file types

335

if (!id.match(/\.(ts|js|vue)$/)) return false;

336

337

// Only transform files in src directory

338

return id.includes('/src/');

339

}

340

})

341

```

342

343

## Performance Considerations

344

345

### Build Performance

346

347

```javascript

348

// Optimize build performance

349

unctxPlugin.vite({

350

// Limit transformation to necessary functions

351

asyncFunctions: ['withAsyncContext'], // Don't include unnecessary functions

352

353

// Use precise file filtering

354

transformInclude: (id) => {

355

// Skip large third-party files

356

if (id.includes('node_modules/')) return false;

357

// Only transform files that actually use unctx

358

return id.includes('src/') && !id.includes('.spec.');

359

}

360

})

361

```

362

363

### Runtime Performance

364

365

The transformation adds minimal runtime overhead:

366

367

```typescript

368

// Generated code is efficient

369

const result = (([__temp, __restore] = __executeAsync(() => operation())),

370

__temp = await __temp, __restore(), __temp);

371

372

// Equivalent to manual context restoration but automated

373

```

374

375

## Debugging Transformed Code

376

377

### Enable Source Maps

378

379

```javascript

380

unctxPlugin.vite({

381

// Source maps are generated automatically

382

// Use browser dev tools to debug original code

383

})

384

```

385

386

### Inspect Transformed Output

387

388

```typescript

389

import { createTransformer } from "unctx/transform";

390

391

const transformer = createTransformer();

392

const result = transformer.transform(sourceCode);

393

394

console.log('Transformed code:');

395

console.log(result.code);

396

```

397

398

## Common Issues and Solutions

399

400

### Issue: Functions Not Being Transformed

401

402

```javascript

403

// ❌ Function name not in asyncFunctions list

404

const handler = myCustomAsync(async () => {

405

// Context lost after await

406

});

407

408

// ✅ Add function name to config

409

unctxPlugin.vite({

410

asyncFunctions: ['withAsyncContext', 'myCustomAsync']

411

})

412

```

413

414

### Issue: TypeScript Compilation Errors

415

416

```typescript

417

// ❌ Missing import for generated helper

418

// The plugin automatically adds: import { executeAsync as __executeAsync } from "unctx";

419

420

// ✅ Ensure unctx is installed as dependency

421

// npm install unctx

422

```

423

424

### Issue: Context Still Lost

425

426

```typescript

427

// ❌ Variable assignment prevents transformation

428

const handler = withAsyncContext;

429

const myHandler = handler(async () => {

430

// Not transformed - indirect call

431

});

432

433

// ✅ Direct function call enables transformation

434

const myHandler = withAsyncContext(async () => {

435

// Transformed correctly

436

});

437

```