or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bundling.mdcompression.mdindex.mdnaming.mdoptimization.mdpackaging.mdreporting.mdresolution.mdruntime.mdtransformation.mdvalidation.md

validation.mddocs/

0

# Code Validation

1

2

The Validator plugin validates code for errors, style issues, and other quality concerns during the build process. Validators can run linting, type checking, and other code quality analysis.

3

4

## Capabilities

5

6

### Validator Class

7

8

Base class for creating code validation plugins.

9

10

```typescript { .api }

11

/**

12

* Base class for code validation plugins

13

* @template T - Configuration type for this validator

14

*/

15

export declare class Validator<T> {

16

constructor(opts: ValidatorOpts);

17

}

18

19

/**

20

* Validator plugin configuration (union of two validator types)

21

*/

22

type ValidatorOpts = DedicatedThreadValidator | MultiThreadValidator;

23

24

/**

25

* Validator that processes all assets at once in a dedicated thread

26

*/

27

interface DedicatedThreadValidator {

28

/** Validate all assets together */

29

validateAll(args: {

30

assets: Asset[];

31

resolveConfigWithPath: ResolveConfigWithPathFn;

32

options: PluginOptions;

33

logger: PluginLogger;

34

tracer: PluginTracer;

35

}): Promise<Array<ValidateResult | null>>;

36

}

37

38

/**

39

* Validator that processes assets individually across multiple threads

40

*/

41

interface MultiThreadValidator {

42

/** Get configuration for an asset */

43

getConfig?(args: {

44

asset: Asset;

45

resolveConfig: ResolveConfigFn;

46

options: PluginOptions;

47

logger: PluginLogger;

48

tracer: PluginTracer;

49

}): Promise<ConfigResult>;

50

51

/** Validate a single asset */

52

validate(args: {

53

asset: Asset;

54

config: ConfigResult | void;

55

options: PluginOptions;

56

logger: PluginLogger;

57

tracer: PluginTracer;

58

}): Promise<ValidateResult | void>;

59

}

60

```

61

62

### Validation Results

63

64

```typescript { .api }

65

/**

66

* Result from validating an asset

67

*/

68

interface ValidateResult {

69

/** Validation diagnostics (errors, warnings) */

70

diagnostics: Array<Diagnostic>;

71

72

/** Whether validation passed */

73

isValid: boolean;

74

75

/** Additional metadata */

76

meta?: Record<string, any>;

77

}

78

79

/**

80

* Configuration loading result

81

*/

82

interface ConfigResult {

83

/** Configuration object */

84

config: any;

85

86

/** Files that were read to load this config */

87

files: Array<FilePath>;

88

}

89

90

/**

91

* Function to resolve configuration with path information

92

*/

93

type ResolveConfigWithPathFn = (

94

configNames: Array<string>,

95

fromPath: FilePath

96

) => Promise<ConfigResult | null>;

97

98

/**

99

* Function to resolve configuration

100

*/

101

type ResolveConfigFn = (

102

configNames: Array<string>

103

) => Promise<any>;

104

```

105

106

**Single Asset Validator Example:**

107

108

```javascript

109

import { Validator } from "@parcel/plugin";

110

import eslint from "eslint";

111

112

export default new Validator({

113

// Get configuration for validation

114

async getConfig({asset, resolveConfig}) {

115

// Only validate JavaScript files

116

if (!asset.filePath.endsWith('.js')) {

117

return null;

118

}

119

120

// Load ESLint configuration

121

const config = await resolveConfig([

122

'.eslintrc.js',

123

'.eslintrc.json',

124

'.eslintrc'

125

]);

126

127

return { config, files: [] };

128

},

129

130

// Validate single asset

131

async validate({asset, config, logger}) {

132

if (!config) {

133

return null; // Skip validation if no config

134

}

135

136

const code = await asset.getCode();

137

const linter = new eslint.ESLint({

138

baseConfig: config.config,

139

useEslintrc: false

140

});

141

142

try {

143

const results = await linter.lintText(code, {

144

filePath: asset.filePath

145

});

146

147

const diagnostics = [];

148

149

for (const result of results) {

150

for (const message of result.messages) {

151

diagnostics.push({

152

level: message.severity === 2 ? 'error' : 'warning',

153

message: message.message,

154

filePath: asset.filePath,

155

start: {

156

line: message.line,

157

column: message.column

158

},

159

hints: message.fix ? ['Run ESLint with --fix to auto-correct this issue'] : undefined

160

});

161

}

162

}

163

164

return {

165

diagnostics,

166

isValid: diagnostics.every(d => d.level !== 'error')

167

};

168

169

} catch (error) {

170

return {

171

diagnostics: [{

172

level: 'error',

173

message: `ESLint validation failed: ${error.message}`,

174

filePath: asset.filePath

175

}],

176

isValid: false

177

};

178

}

179

}

180

});

181

```

182

183

**Batch Validator Example:**

184

185

```javascript

186

import { Validator } from "@parcel/plugin";

187

import typescript from "typescript";

188

189

export default new Validator({

190

// Validate all TypeScript assets together

191

async validateAll({assets, resolveConfigWithPath, options, logger}) {

192

// Filter TypeScript assets

193

const tsAssets = assets.filter(asset =>

194

asset.filePath.endsWith('.ts') || asset.filePath.endsWith('.tsx')

195

);

196

197

if (tsAssets.length === 0) {

198

return [];

199

}

200

201

// Load TypeScript configuration

202

const configResult = await resolveConfigWithPath([

203

'tsconfig.json'

204

], options.projectRoot);

205

206

const tsConfig = configResult?.config || {};

207

208

// Create TypeScript program

209

const fileNames = tsAssets.map(asset => asset.filePath);

210

const program = typescript.createProgram(fileNames, tsConfig.compilerOptions || {});

211

212

// Get diagnostics

213

const diagnostics = typescript.getPreEmitDiagnostics(program);

214

215

// Convert to validation results

216

const results = [];

217

218

for (const asset of tsAssets) {

219

const assetDiagnostics = diagnostics

220

.filter(d => d.file?.fileName === asset.filePath)

221

.map(d => this.convertTSDiagnostic(d));

222

223

results.push({

224

diagnostics: assetDiagnostics,

225

isValid: assetDiagnostics.every(d => d.level !== 'error')

226

});

227

}

228

229

return results;

230

},

231

232

convertTSDiagnostic(diagnostic) {

233

const message = typescript.flattenDiagnosticMessageText(

234

diagnostic.messageText,

235

'\n'

236

);

237

238

let start = undefined;

239

if (diagnostic.file && diagnostic.start != null) {

240

const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);

241

start = {

242

line: pos.line + 1,

243

column: pos.character

244

};

245

}

246

247

return {

248

level: diagnostic.category === typescript.DiagnosticCategory.Error ? 'error' : 'warning',

249

message,

250

filePath: diagnostic.file?.fileName,

251

start,

252

documentationURL: `https://typescript.tv/errors/#TS${diagnostic.code}`

253

};

254

}

255

});

256

```

257

258

### Common Validation Patterns

259

260

**File Type Filtering:**

261

```javascript

262

// Only validate specific file types

263

if (!asset.filePath.match(/\.(js|ts|jsx|tsx)$/)) {

264

return null;

265

}

266

```

267

268

**Configuration Loading:**

269

```javascript

270

// Load validation configuration

271

const config = await resolveConfig([

272

'.eslintrc.js',

273

'.eslintrc.json',

274

'package.json' // Look for eslintConfig key

275

]);

276

```

277

278

**Error Aggregation:**

279

```javascript

280

// Collect errors from multiple sources

281

const allDiagnostics = [

282

...syntaxErrors,

283

...lintingErrors,

284

...typeErrors

285

];

286

287

return {

288

diagnostics: allDiagnostics,

289

isValid: allDiagnostics.every(d => d.level !== 'error')

290

};

291

```