or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-processing.mdcli.mdcore-configuration.mddebugging.mdfile-operations.mdfrontmatter.mdindex.mdplugin-system.mdutilities.md

plugin-system.mddocs/

0

# Plugin System

1

2

Plugin management and execution system for extending Metalsmith functionality with custom transformations. All Metalsmith logic is handled through plugins that manipulate files in a middleware-style pattern.

3

4

## Capabilities

5

6

### Adding Plugins

7

8

Add plugins to the Metalsmith processing pipeline. Plugins are executed in the order they are added.

9

10

```javascript { .api }

11

/**

12

* Add a plugin function to the processing stack

13

* @param plugin - Plugin function or array of plugin functions

14

* @returns Metalsmith instance for chaining

15

*/

16

use(plugin: Plugin | Plugin[]): Metalsmith;

17

18

/**

19

* Metalsmith plugin function signature

20

* @param files - Object containing all files being processed

21

* @param metalsmith - The Metalsmith instance

22

* @param callback - Callback to signal completion (for async plugins)

23

*/

24

type Plugin = (

25

files: Files,

26

metalsmith: Metalsmith,

27

callback: (error?: Error) => void

28

) => void | Promise<void>;

29

```

30

31

**Usage Examples:**

32

33

```javascript

34

import Metalsmith from "metalsmith";

35

import markdown from "@metalsmith/markdown";

36

import layouts from "@metalsmith/layouts";

37

38

// Add plugins in sequence

39

metalsmith

40

.use(markdown())

41

.use(layouts({

42

pattern: "**/*.html"

43

}));

44

45

// Add multiple plugins at once

46

metalsmith.use([

47

markdown(),

48

layouts({ pattern: "**/*.html" })

49

]);

50

```

51

52

### Plugin Access to Instance

53

54

Plugins have full access to the Metalsmith instance and can read configuration, metadata, and other settings.

55

56

```javascript { .api }

57

/**

58

* Plugin can access all Metalsmith methods and properties

59

*/

60

function examplePlugin(files, metalsmith, done) {

61

// Access configuration

62

const srcDir = metalsmith.source();

63

const destDir = metalsmith.destination();

64

const metadata = metalsmith.metadata();

65

66

// Access environment variables

67

const debugMode = metalsmith.env('DEBUG');

68

69

// Manipulate files

70

Object.keys(files).forEach(filepath => {

71

const file = files[filepath];

72

// Transform file...

73

});

74

75

// Signal completion

76

done();

77

}

78

```

79

80

### Plugin Types

81

82

Metalsmith supports both synchronous and asynchronous plugins.

83

84

**Synchronous Plugin:**

85

86

```javascript

87

function syncPlugin(files, metalsmith) {

88

// No callback needed for sync plugins

89

Object.keys(files).forEach(filepath => {

90

// Transform files synchronously

91

files[filepath].processed = true;

92

});

93

}

94

```

95

96

**Asynchronous Plugin with Callback:**

97

98

```javascript

99

function asyncPlugin(files, metalsmith, done) {

100

// Perform async operations

101

setTimeout(() => {

102

Object.keys(files).forEach(filepath => {

103

files[filepath].delayed = true;

104

});

105

done(); // Must call done() when finished

106

}, 100);

107

}

108

```

109

110

**Promise-based Plugin:**

111

112

```javascript

113

async function promisePlugin(files, metalsmith) {

114

// Return a promise or use async/await

115

await new Promise(resolve => setTimeout(resolve, 100));

116

117

Object.keys(files).forEach(filepath => {

118

files[filepath].promised = true;

119

});

120

}

121

```

122

123

### Plugin Configuration Patterns

124

125

Common patterns for configurable plugins that accept options.

126

127

```javascript

128

// Plugin factory pattern

129

function configurablePlugin(options = {}) {

130

return function plugin(files, metalsmith, done) {

131

const settings = {

132

pattern: '**/*',

133

...options

134

};

135

136

// Use settings to configure behavior

137

Object.keys(files)

138

.filter(filepath => metalsmith.match(settings.pattern, [filepath]).length)

139

.forEach(filepath => {

140

// Process matching files

141

});

142

143

done();

144

};

145

}

146

147

// Usage

148

metalsmith.use(configurablePlugin({

149

pattern: '**/*.md',

150

customOption: 'value'

151

}));

152

```

153

154

### File Object Manipulation

155

156

Plugins work with file objects that contain contents, metadata, and filesystem information.

157

158

```javascript { .api }

159

interface File {

160

/** File contents as Buffer */

161

contents: Buffer;

162

/** Filesystem stats object */

163

stats?: import('fs').Stats;

164

/** File permission mode */

165

mode?: string;

166

/** Front-matter and custom properties */

167

[key: string]: any;

168

}

169

170

interface Files {

171

/** Mapping of file paths to File objects */

172

[filepath: string]: File;

173

}

174

```

175

176

**File Manipulation Examples:**

177

178

```javascript

179

function fileManipulationPlugin(files, metalsmith, done) {

180

Object.keys(files).forEach(filepath => {

181

const file = files[filepath];

182

183

// Read file contents

184

const content = file.contents.toString();

185

186

// Access front-matter data

187

const title = file.title;

188

const date = file.date;

189

190

// Modify contents

191

file.contents = Buffer.from(content.toUpperCase());

192

193

// Add metadata

194

file.processed = true;

195

file.processedAt = new Date();

196

197

// Change file path (rename/move)

198

if (filepath.endsWith('.md')) {

199

const newPath = filepath.replace('.md', '.html');

200

files[newPath] = file;

201

delete files[filepath];

202

}

203

});

204

205

done();

206

}

207

```

208

209

### Error Handling in Plugins

210

211

Proper error handling patterns for plugins.

212

213

```javascript

214

function errorHandlingPlugin(files, metalsmith, done) {

215

try {

216

Object.keys(files).forEach(filepath => {

217

// Operations that might fail

218

if (someCondition) {

219

throw new Error(`Processing failed for ${filepath}`);

220

}

221

});

222

done(); // Success

223

} catch (error) {

224

done(error); // Pass error to Metalsmith

225

}

226

}

227

228

// Promise-based error handling

229

async function promiseErrorPlugin(files, metalsmith) {

230

for (const filepath of Object.keys(files)) {

231

try {

232

await someAsyncOperation(files[filepath]);

233

} catch (error) {

234

throw new Error(`Failed to process ${filepath}: ${error.message}`);

235

}

236

}

237

}

238

```

239

240

### Plugin Development Tips

241

242

Best practices for developing Metalsmith plugins:

243

244

```javascript

245

function wellBehavedPlugin(options = {}) {

246

// Validate options

247

if (options.required && typeof options.required !== 'string') {

248

throw new TypeError('required option must be a string');

249

}

250

251

return function plugin(files, metalsmith, done) {

252

// Create debug logger

253

const debug = metalsmith.debug('my-plugin');

254

debug('Processing %d files', Object.keys(files).length);

255

256

// Access metalsmith configuration

257

const metadata = metalsmith.metadata();

258

const srcDir = metalsmith.source();

259

260

try {

261

Object.keys(files).forEach(filepath => {

262

debug('Processing file: %s', filepath);

263

const file = files[filepath];

264

265

// Plugin logic here

266

267

debug('Finished processing: %s', filepath);

268

});

269

270

debug('Plugin completed successfully');

271

done();

272

} catch (error) {

273

debug('Plugin failed: %s', error.message);

274

done(error);

275

}

276

};

277

}

278

```