or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

built-in-rules.mdconfiguration.mdindex.mdplugin-development.mdprogrammatic-api.md

plugin-development.mddocs/

0

# Plugin Development

1

2

Plugin system for creating custom rules and extending Stylelint functionality. Provides utilities for rule creation, validation, option handling, and seamless integration with the core linting engine.

3

4

## Capabilities

5

6

### Plugin Creation

7

8

Create custom Stylelint plugins with the createPlugin utility function.

9

10

```typescript { .api }

11

/**

12

* Create a Stylelint plugin

13

* @param ruleName - Unique name for the rule

14

* @param rule - Rule implementation function

15

* @returns Plugin object for registration

16

*/

17

function createPlugin(ruleName: string, rule: Rule): Plugin;

18

19

interface Plugin {

20

ruleName: string;

21

rule: Rule;

22

}

23

```

24

25

**Usage Example:**

26

27

```javascript

28

import stylelint from "stylelint";

29

const { createPlugin, utils } = stylelint;

30

31

const myCustomRule = (primaryOption, secondaryOptions, context) => {

32

return (root, result) => {

33

// Rule implementation

34

root.walkDecls((decl) => {

35

if (decl.prop === 'color' && decl.value === 'red') {

36

utils.report({

37

ruleName: 'my-namespace/no-red-colors',

38

result,

39

node: decl,

40

message: 'Unexpected red color'

41

});

42

}

43

});

44

};

45

};

46

47

const plugin = createPlugin('my-namespace/no-red-colors', myCustomRule);

48

export default plugin;

49

```

50

51

### Rule Interface

52

53

Core rule function signature and structure for implementing custom linting logic.

54

55

```typescript { .api }

56

interface Rule<P = any, S = any, M = RuleMessages> {

57

/**

58

* Rule implementation function

59

* @param primaryOption - Main rule configuration

60

* @param secondaryOptions - Additional rule options

61

* @param context - Rule execution context

62

* @returns PostCSS plugin function

63

*/

64

(primaryOption: P, secondaryOptions: S, context: RuleContext):

65

(root: PostCSS.Root, result: PostcssResult) => Promise<void> | void;

66

67

/** Unique rule name */

68

ruleName: string;

69

/** Rule message templates */

70

messages: M;

71

/** Whether primary option is an array */

72

primaryOptionArray?: boolean;

73

/** Rule metadata */

74

meta?: RuleMeta;

75

}

76

77

interface RuleContext {

78

/** Configuration comment prefix */

79

configurationComment?: string;

80

/** Fix mode enabled */

81

fix?: boolean;

82

/** Newline character for the file */

83

newline?: string;

84

}

85

86

interface RuleMeta {

87

/** URL to rule documentation */

88

url: string;

89

/** Whether rule is deprecated */

90

deprecated?: boolean;

91

/** Whether rule supports auto-fixing */

92

fixable?: boolean;

93

}

94

```

95

96

### Rule Messages

97

98

Define message templates for rule violations and user-facing text.

99

100

```typescript { .api }

101

type RuleMessages = { [message: string]: RuleMessage };

102

type RuleMessage = string | RuleMessageFunc;

103

type RuleMessageFunc = (...args: (string | number | boolean | RegExp)[]) => string;

104

105

/**

106

* Create rule-specific messages with rule name suffix

107

* @param ruleName - Name of the rule

108

* @param messages - Message templates

109

* @returns Processed messages with rule names

110

*/

111

function ruleMessages<T extends RuleMessages>(

112

ruleName: string,

113

messages: T

114

): T;

115

```

116

117

**Usage Example:**

118

119

```javascript

120

import stylelint from "stylelint";

121

const { utils } = stylelint;

122

123

const messages = utils.ruleMessages('my-rule/no-red', {

124

rejected: (value) => `Unexpected red color "${value}"`,

125

expected: 'Expected a color other than red'

126

});

127

128

// Usage in rule

129

utils.report({

130

ruleName: 'my-rule/no-red',

131

result,

132

node: decl,

133

message: messages.rejected(decl.value),

134

messageArgs: [decl.value]

135

});

136

```

137

138

### Option Validation

139

140

Validate rule options using the built-in validation system.

141

142

```typescript { .api }

143

/**

144

* Validate rule options

145

* @param result - PostCSS result object

146

* @param ruleName - Name of the rule

147

* @param optionDescriptions - Option validation descriptors

148

* @returns Whether all options are valid

149

*/

150

function validateOptions(

151

result: PostcssResult,

152

ruleName: string,

153

...optionDescriptions: RuleOptions[]

154

): boolean;

155

156

interface RuleOptions {

157

/** Actual option value to validate */

158

actual: unknown;

159

/** Possible valid values or validation functions */

160

possible?: RuleOptionsPossibleFunc | RuleOptionsPossible[] | Record<string, RuleOptionsPossible[]>;

161

/** Whether this option is optional */

162

optional?: boolean;

163

}

164

165

type RuleOptionsPossible = boolean | number | string | RuleOptionsPossibleFunc;

166

type RuleOptionsPossibleFunc = (value: unknown) => boolean;

167

```

168

169

**Usage Example:**

170

171

```javascript

172

const rule = (primaryOption, secondaryOptions) => {

173

return (root, result) => {

174

// Validate options

175

const validOptions = utils.validateOptions(

176

result,

177

ruleName,

178

{

179

actual: primaryOption,

180

possible: ['always', 'never']

181

},

182

{

183

actual: secondaryOptions,

184

possible: {

185

ignore: ['comments', 'whitespace'],

186

severity: ['warning', 'error']

187

},

188

optional: true

189

}

190

);

191

192

if (!validOptions) return;

193

194

// Rule implementation...

195

};

196

};

197

```

198

199

### Problem Reporting

200

201

Report linting violations using the utils.report function.

202

203

```typescript { .api }

204

/**

205

* Report a linting problem

206

* @param problem - Problem details and location

207

*/

208

function report(problem: Problem): void;

209

210

interface Problem {

211

/** Rule name generating the problem */

212

ruleName: string;

213

/** PostCSS result object */

214

result: PostcssResult;

215

/** Problem message text or function */

216

message: RuleMessage;

217

/** Arguments for message functions */

218

messageArgs?: Parameters<RuleMessageFunc>;

219

/** CSS node where problem occurs */

220

node: PostCSS.Node;

221

/** Start index within node */

222

index?: number;

223

/** End index within node */

224

endIndex?: number;

225

/** Start position within node */

226

start?: Position;

227

/** End position within node */

228

end?: Position;

229

/** Specific word causing the problem */

230

word?: string;

231

/** Severity override */

232

severity?: Severity;

233

/** Auto-fix callback or object */

234

fix?: FixCallback | FixObject;

235

}

236

237

interface Position {

238

line: number;

239

column: number;

240

}

241

242

type FixCallback = () => void;

243

interface FixObject {

244

apply?: FixCallback;

245

node?: PostCSS.Node;

246

}

247

```

248

249

### Testing Custom Rules

250

251

Utilities for testing custom rules against CSS inputs.

252

253

```typescript { .api }

254

/**

255

* Test a rule against CSS input

256

* @param options - Test configuration

257

* @param callback - Callback for handling warnings

258

*/

259

function checkAgainstRule<T, O extends Object>(

260

options: {

261

ruleName: string;

262

ruleSettings: ConfigRuleSettings<T, O>;

263

root: PostCSS.Root;

264

result?: PostcssResult;

265

context?: RuleContext;

266

},

267

callback: (warning: PostCSS.Warning) => void

268

): Promise<void>;

269

```

270

271

**Usage Example:**

272

273

```javascript

274

import stylelint from "stylelint";

275

const { utils } = stylelint;

276

import postcss from "postcss";

277

278

// Test the rule

279

const root = postcss.parse('.example { color: red; }');

280

const warnings = [];

281

282

await utils.checkAgainstRule(

283

{

284

ruleName: 'my-rule/no-red',

285

ruleSettings: true,

286

root

287

},

288

(warning) => warnings.push(warning)

289

);

290

291

console.log('Warnings:', warnings.length);

292

```

293

294

### Complete Plugin Example

295

296

```javascript

297

import stylelint from "stylelint";

298

const { createPlugin, utils } = stylelint;

299

300

const ruleName = "my-plugin/no-hardcoded-colors";

301

302

const messages = utils.ruleMessages(ruleName, {

303

rejected: (color) => `Unexpected hardcoded color "${color}". Use CSS custom properties instead.`,

304

rejectedHex: (hex) => `Unexpected hex color "${hex}". Use CSS custom properties instead.`

305

});

306

307

const meta = {

308

url: "https://github.com/my-org/stylelint-my-plugin#no-hardcoded-colors",

309

fixable: false

310

};

311

312

const ruleFunction = (primaryOption, secondaryOptions = {}) => {

313

return (root, result) => {

314

// Validate options

315

const validOptions = utils.validateOptions(

316

result,

317

ruleName,

318

{

319

actual: primaryOption,

320

possible: [true, false]

321

},

322

{

323

actual: secondaryOptions,

324

possible: {

325

ignore: [utils.isString],

326

ignoreProperties: [utils.isString],

327

severity: ['warning', 'error']

328

},

329

optional: true

330

}

331

);

332

333

if (!validOptions || !primaryOption) return;

334

335

const { ignore = [], ignoreProperties = [] } = secondaryOptions;

336

337

root.walkDecls((decl) => {

338

// Skip ignored properties

339

if (ignoreProperties.includes(decl.prop)) return;

340

341

const value = decl.value;

342

343

// Check for hex colors

344

const hexMatch = value.match(/#[0-9a-fA-F]{3,8}/);

345

if (hexMatch && !ignore.includes(hexMatch[0])) {

346

utils.report({

347

ruleName,

348

result,

349

node: decl,

350

message: messages.rejectedHex,

351

messageArgs: [hexMatch[0]],

352

index: decl.source.start.column - 1 + value.indexOf(hexMatch[0]),

353

endIndex: decl.source.start.column - 1 + value.indexOf(hexMatch[0]) + hexMatch[0].length

354

});

355

}

356

357

// Check for named colors

358

const namedColors = ['red', 'blue', 'green', 'black', 'white'];

359

for (const color of namedColors) {

360

if (value.includes(color) && !ignore.includes(color)) {

361

utils.report({

362

ruleName,

363

result,

364

node: decl,

365

message: messages.rejected,

366

messageArgs: [color]

367

});

368

}

369

}

370

});

371

};

372

};

373

374

ruleFunction.ruleName = ruleName;

375

ruleFunction.messages = messages;

376

ruleFunction.meta = meta;

377

378

export default createPlugin(ruleName, ruleFunction);

379

```

380

381

### Plugin Registration

382

383

Register and use custom plugins in Stylelint configuration.

384

385

```javascript

386

// stylelint.config.js

387

import myPlugin from "./my-custom-plugin.js";

388

389

export default {

390

plugins: [myPlugin],

391

rules: {

392

"my-plugin/no-hardcoded-colors": [true, {

393

ignore: ["transparent", "inherit"],

394

ignoreProperties: ["box-shadow"]

395

}]

396

}

397

};

398

399

// Or with plugin name

400

export default {

401

plugins: ["./my-custom-plugin.js"],

402

rules: {

403

"my-plugin/no-hardcoded-colors": true

404

}

405

};

406

```