or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

built-in-resolvers.mdcustom-resolvers.mdindex.mdplugin-configuration.mdutility-functions.md

custom-resolvers.mddocs/

0

# Custom Resolvers

1

2

Extensible resolver system for handling non-standard file types during TypeScript declaration generation, enabling support for custom file formats and preprocessing pipelines.

3

4

## Capabilities

5

6

### Resolver Interface

7

8

Core interface defining how custom resolvers integrate with the plugin's TypeScript compilation process.

9

10

```typescript { .api }

11

/**

12

* Custom resolver for transforming non-standard files to TypeScript declarations

13

*/

14

interface Resolver {

15

/** Unique name identifier (later resolvers with same name overwrite earlier ones) */

16

name: string;

17

18

/** Determine whether this resolver can handle the given file */

19

supports: (id: string) => void | boolean;

20

21

/** Transform source code to declaration file outputs */

22

transform: (payload: {

23

id: string;

24

code: string;

25

root: string;

26

outDir: string;

27

host: ts.CompilerHost;

28

program: ts.Program;

29

}) => MaybePromise<

30

| { outputs: { path: string; content: string }[]; emitSkipped?: boolean; diagnostics?: readonly ts.Diagnostic[] }

31

| { path: string; content: string }[]

32

>;

33

}

34

```

35

36

### Creating Custom Resolvers

37

38

Factory pattern for creating resolvers that handle specific file types or transformation requirements.

39

40

```typescript { .api }

41

/**

42

* Create a custom resolver for specific file types

43

* @param name - Unique identifier for the resolver

44

* @param supportsPattern - File extension or pattern matching function

45

* @param transformFn - Function to transform source to declarations

46

* @returns Configured resolver instance

47

*/

48

function createCustomResolver(

49

name: string,

50

supportsPattern: string | RegExp | ((id: string) => boolean),

51

transformFn: (payload: ResolverPayload) => ResolverResult

52

): Resolver;

53

54

interface ResolverPayload {

55

id: string;

56

code: string;

57

root: string;

58

outDir: string;

59

host: ts.CompilerHost;

60

program: ts.Program;

61

}

62

63

type ResolverResult = MaybePromise<

64

| { outputs: { path: string; content: string }[]; emitSkipped?: boolean; diagnostics?: readonly ts.Diagnostic[] }

65

| { path: string; content: string }[]

66

>;

67

```

68

69

**Usage Examples:**

70

71

```typescript

72

import dts from "vite-plugin-dts";

73

74

// Custom resolver for .yaml configuration files

75

const yamlResolver: Resolver = {

76

name: "yaml-config",

77

supports: (id) => id.endsWith(".yaml") || id.endsWith(".yml"),

78

transform: async ({ id, code, outDir, root }) => {

79

// Parse YAML and generate TypeScript definitions

80

const configName = path.basename(id, path.extname(id));

81

const capitalizedName = configName.charAt(0).toUpperCase() + configName.slice(1);

82

83

const declaration = `

84

export interface ${capitalizedName}Config {

85

[key: string]: any;

86

}

87

88

declare const config: ${capitalizedName}Config;

89

export default config;

90

`;

91

92

return [{

93

path: path.resolve(outDir, `${configName}.d.ts`),

94

content: declaration

95

}];

96

}

97

};

98

99

// Custom resolver for GraphQL schema files

100

const graphqlResolver: Resolver = {

101

name: "graphql-schema",

102

supports: (id) => id.endsWith(".graphql") || id.endsWith(".gql"),

103

transform: async ({ id, code, outDir }) => {

104

// Generate TypeScript types from GraphQL schema

105

const schemaName = path.basename(id, path.extname(id));

106

107

// Simple example - in practice you'd parse the GraphQL schema

108

const typeDefinitions = `

109

export interface ${schemaName}Schema {

110

query: Query;

111

mutation?: Mutation;

112

subscription?: Subscription;

113

}

114

115

interface Query {

116

[field: string]: any;

117

}

118

119

interface Mutation {

120

[field: string]: any;

121

}

122

123

interface Subscription {

124

[field: string]: any;

125

}

126

`;

127

128

return {

129

outputs: [{

130

path: path.resolve(outDir, `${schemaName}.schema.d.ts`),

131

content: typeDefinitions

132

}],

133

emitSkipped: false

134

};

135

}

136

};

137

138

// Use custom resolvers in plugin configuration

139

export default defineConfig({

140

plugins: [

141

dts({

142

resolvers: [yamlResolver, graphqlResolver]

143

})

144

]

145

});

146

```

147

148

### Resolver Registration and Management

149

150

System for managing multiple resolvers and handling conflicts between resolver names.

151

152

```typescript { .api }

153

/**

154

* Parse and deduplicate resolvers by name

155

* Later resolvers with the same name overwrite earlier ones

156

* @param resolvers - Array of resolver instances

157

* @returns Deduplicated array of resolvers

158

*/

159

function parseResolvers(resolvers: Resolver[]): Resolver[];

160

```

161

162

**Usage Examples:**

163

164

```typescript

165

import { parseResolvers } from "vite-plugin-dts/resolvers";

166

167

// Multiple resolvers with potential name conflicts

168

const resolvers = [

169

{ name: "json", supports: () => false, transform: () => [] }, // Will be overwritten

170

{ name: "custom", supports: () => true, transform: () => [] },

171

{ name: "json", supports: (id) => id.endsWith(".json"), transform: () => [] } // Overwrites first

172

];

173

174

const finalResolvers = parseResolvers(resolvers);

175

// Result: [custom resolver, second json resolver]

176

177

// Use in plugin configuration

178

dts({

179

resolvers: finalResolvers

180

});

181

```

182

183

### Advanced Resolver Patterns

184

185

Complex resolver implementations for sophisticated file transformation scenarios.

186

187

```typescript { .api }

188

// Multi-output resolver (generates multiple declaration files from one source)

189

const multiOutputResolver: Resolver = {

190

name: "multi-output",

191

supports: (id) => id.endsWith(".multi.ts"),

192

transform: async ({ id, code, outDir, program }) => {

193

// Generate multiple declaration files from analysis

194

return {

195

outputs: [

196

{

197

path: path.resolve(outDir, "types.d.ts"),

198

content: "export interface GeneratedType {}"

199

},

200

{

201

path: path.resolve(outDir, "constants.d.ts"),

202

content: "export declare const GENERATED_CONSTANT: string;"

203

}

204

],

205

emitSkipped: false

206

};

207

}

208

};

209

210

// Conditional resolver with diagnostics

211

const conditionalResolver: Resolver = {

212

name: "conditional",

213

supports: (id) => id.includes(".conditional."),

214

transform: async ({ id, code, host, program }) => {

215

const diagnostics: ts.Diagnostic[] = [];

216

217

try {

218

// Validate file content

219

if (!code.includes("export")) {

220

diagnostics.push({

221

file: program.getSourceFile(id),

222

start: 0,

223

length: code.length,

224

messageText: "File must contain exports",

225

category: ts.DiagnosticCategory.Warning,

226

code: 9999

227

} as ts.Diagnostic);

228

}

229

230

const declaration = `// Generated from ${path.basename(id)}\nexport {};`;

231

232

return {

233

outputs: [{

234

path: id.replace(/\.(conditional\.\w+)$/, ".d.ts"),

235

content: declaration

236

}],

237

emitSkipped: false,

238

diagnostics

239

};

240

} catch (error) {

241

return {

242

outputs: [],

243

emitSkipped: true,

244

diagnostics: [{

245

file: undefined,

246

start: 0,

247

length: 0,

248

messageText: `Resolver error: ${error.message}`,

249

category: ts.DiagnosticCategory.Error,

250

code: 1

251

} as ts.Diagnostic]

252

};

253

}

254

}

255

};

256

```

257

258

### TypeScript Program Integration

259

260

Advanced resolvers that leverage the TypeScript compiler's program and host for sophisticated analysis.

261

262

```typescript { .api }

263

// Program-aware resolver

264

const programAwareResolver: Resolver = {

265

name: "program-aware",

266

supports: (id) => id.endsWith(".analyzed.ts"),

267

transform: async ({ id, code, host, program, root, outDir }) => {

268

// Access TypeScript compiler services

269

const sourceFile = program.getSourceFile(id);

270

if (!sourceFile) {

271

return { outputs: [], emitSkipped: true };

272

}

273

274

// Analyze the source file using TypeScript APIs

275

const typeChecker = program.getTypeChecker();

276

const exports: string[] = [];

277

278

// Walk the AST to find exports

279

ts.forEachChild(sourceFile, (node) => {

280

if (ts.isExportDeclaration(node) || ts.isFunctionDeclaration(node) && node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {

281

// Extract export information

282

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

283

const symbol = typeChecker.getSymbolAtLocation(node.name);

284

if (symbol) {

285

exports.push(`export declare function ${symbol.getName()}(): any;`);

286

}

287

}

288

}

289

});

290

291

const content = exports.length > 0

292

? exports.join('\n')

293

: '// No exports found\nexport {};';

294

295

return [{

296

path: path.resolve(outDir, path.relative(root, id).replace(/\.analyzed\.ts$/, '.d.ts')),

297

content

298

}];

299

}

300

};

301

```

302

303

### Error Handling and Diagnostics

304

305

Best practices for handling errors and providing useful diagnostics in custom resolvers.

306

307

```typescript { .api }

308

const robustResolver: Resolver = {

309

name: "robust",

310

supports: (id) => id.endsWith(".custom"),

311

transform: async ({ id, code }) => {

312

try {

313

// Attempt transformation

314

const result = await processCustomFile(code);

315

316

return {

317

outputs: [{

318

path: id.replace(/\.custom$/, '.d.ts'),

319

content: result

320

}],

321

emitSkipped: false

322

};

323

} catch (error) {

324

// Provide helpful diagnostics on failure

325

console.warn(`Custom resolver failed for ${id}: ${error.message}`);

326

327

return {

328

outputs: [{

329

path: id.replace(/\.custom$/, '.d.ts'),

330

content: `// Error processing ${path.basename(id)}\n// ${error.message}\nexport {};`

331

}],

332

emitSkipped: false,

333

diagnostics: [{

334

file: undefined,

335

start: 0,

336

length: 0,

337

messageText: `Custom resolver error: ${error.message}`,

338

category: ts.DiagnosticCategory.Warning,

339

code: 9999

340

} as ts.Diagnostic]

341

};

342

}

343

}

344

};

345

```