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

naming.mddocs/

0

# Bundle Naming

1

2

The Namer plugin generates output filenames for bundles based on content, configuration, and optimization strategies. Namers control how bundles are named in the final build output.

3

4

## Capabilities

5

6

### Namer Class

7

8

Base class for creating bundle naming plugins.

9

10

```typescript { .api }

11

/**

12

* Base class for bundle naming plugins

13

* @template T - Configuration type for this namer

14

*/

15

export declare class Namer<T> {

16

constructor(opts: NamerOpts<T>);

17

}

18

19

/**

20

* Namer plugin configuration interface

21

* @template ConfigType - Type of configuration returned by loadConfig

22

*/

23

interface NamerOpts<ConfigType> {

24

/** Load configuration for this namer */

25

loadConfig?: (args: {

26

config: Config;

27

options: PluginOptions;

28

logger: PluginLogger;

29

tracer: PluginTracer;

30

}) => Promise<ConfigType> | ConfigType;

31

32

/** Generate a filename for a bundle (required) */

33

name(args: {

34

bundle: Bundle;

35

bundleGraph: BundleGraph<Bundle>;

36

config: ConfigType;

37

options: PluginOptions;

38

logger: PluginLogger;

39

tracer: PluginTracer;

40

}): Promise<FilePath | null>;

41

}

42

```

43

44

**Usage Example:**

45

46

```javascript

47

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

48

import path from "path";

49

import crypto from "crypto";

50

51

export default new Namer({

52

// Load namer configuration

53

loadConfig({config}) {

54

return {

55

pattern: config.pattern || '[name].[hash].[ext]',

56

hashLength: config.hashLength || 8,

57

preserveEntryNames: config.preserveEntryNames !== false

58

};

59

},

60

61

// Generate bundle filename (required)

62

async name({bundle, bundleGraph, config, options}) {

63

const bundleAssets = bundleGraph.getBundleAssets(bundle);

64

65

// Use entry name for entry bundles

66

if (bundle.entryAsset && config.preserveEntryNames) {

67

const entryName = path.basename(

68

bundle.entryAsset.filePath,

69

path.extname(bundle.entryAsset.filePath)

70

);

71

72

return this.formatName(config.pattern, {

73

name: entryName,

74

hash: this.generateHash(bundleAssets),

75

ext: this.getExtension(bundle.type)

76

});

77

}

78

79

// Generate name based on content

80

const contentHash = this.generateHash(bundleAssets);

81

const baseName = this.generateBaseName(bundle, bundleAssets);

82

83

return this.formatName(config.pattern, {

84

name: baseName,

85

hash: contentHash.slice(0, config.hashLength),

86

ext: this.getExtension(bundle.type)

87

});

88

},

89

90

// Helper methods

91

generateHash(assets) {

92

const hasher = crypto.createHash('md5');

93

for (const asset of assets) {

94

hasher.update(asset.getCode());

95

}

96

return hasher.digest('hex');

97

},

98

99

generateBaseName(bundle, assets) {

100

if (assets.length === 1) {

101

return path.basename(assets[0].filePath, path.extname(assets[0].filePath));

102

}

103

return 'chunk';

104

},

105

106

formatName(pattern, vars) {

107

return pattern.replace(/\[(\w+)\]/g, (match, key) => vars[key] || match);

108

},

109

110

getExtension(bundleType) {

111

const extensions = {

112

'js': 'js',

113

'css': 'css',

114

'html': 'html',

115

'json': 'json'

116

};

117

return extensions[bundleType] || bundleType;

118

}

119

});

120

```

121

122

### Bundle Information for Naming

123

124

```typescript { .api }

125

/**

126

* Bundle information available for naming

127

*/

128

interface Bundle {

129

/** Bundle ID */

130

id: string;

131

132

/** Bundle type (js, css, html, etc.) */

133

type: string;

134

135

/** Entry asset for entry bundles */

136

entryAsset?: Asset;

137

138

/** Main entry asset */

139

mainEntryAsset?: Asset;

140

141

/** Target environment */

142

target: Target;

143

144

/** Whether this bundle needs a stable name */

145

needsStableName: boolean;

146

147

/** Bundle behavior */

148

bundleBehavior?: BundleBehavior;

149

150

/** Bundle display name */

151

displayName?: string;

152

153

/** Bundle metadata */

154

meta: Record<string, any>;

155

}

156

157

/**

158

* Bundle graph for accessing related bundles and assets

159

*/

160

interface BundleGraph<TBundle> {

161

/** Get assets in a bundle */

162

getBundleAssets(bundle: TBundle): Array<Asset>;

163

164

/** Get bundle dependencies */

165

getBundleDependencies(bundle: TBundle): Array<TBundle>;

166

167

/** Get bundles that depend on this bundle */

168

getBundleDependents(bundle: TBundle): Array<TBundle>;

169

170

/** Get all bundles */

171

getBundles(): Array<TBundle>;

172

173

/** Check if bundle has dependency on another bundle */

174

bundleHasDependency(bundle: TBundle, dependency: TBundle): boolean;

175

}

176

```

177

178

### Common Naming Patterns

179

180

**Hash-based Naming:**

181

```javascript

182

// Content-based hashing for cache busting

183

const contentHash = crypto.createHash('md5');

184

for (const asset of bundleAssets) {

185

contentHash.update(asset.getCode());

186

}

187

const hash = contentHash.digest('hex').slice(0, 8);

188

189

return `${baseName}.${hash}.${extension}`;

190

```

191

192

**Entry Name Preservation:**

193

```javascript

194

// Preserve original entry file names

195

if (bundle.entryAsset && bundle.needsStableName) {

196

const originalName = path.basename(

197

bundle.entryAsset.filePath,

198

path.extname(bundle.entryAsset.filePath)

199

);

200

return `${originalName}.${extension}`;

201

}

202

```

203

204

**Hierarchical Naming:**

205

```javascript

206

// Create directory structure based on bundle type

207

const typeDir = bundle.type;

208

const targetDir = bundle.target.name;

209

210

return path.join(targetDir, typeDir, `${baseName}.${extension}`);

211

```

212

213

**Chunk Naming:**

214

```javascript

215

// Name shared chunks based on their dependencies

216

const dependencies = bundleGraph.getBundleDependencies(bundle);

217

if (dependencies.length > 1) {

218

const depNames = dependencies

219

.map(dep => dep.displayName || 'chunk')

220

.sort()

221

.join('-');

222

return `shared-${depNames}.${hash}.${extension}`;

223

}

224

```

225

226

### File Extensions by Bundle Type

227

228

```typescript { .api }

229

/**

230

* Common file extensions for different bundle types

231

*/

232

interface BundleTypeExtensions {

233

'js': 'js';

234

'css': 'css';

235

'html': 'html';

236

'json': 'json';

237

'xml': 'xml';

238

'txt': 'txt';

239

'wasm': 'wasm';

240

'webmanifest': 'json';

241

}

242

```

243

244

### Naming Configuration Options

245

246

```typescript { .api }

247

/**

248

* Common naming configuration options

249

*/

250

interface NamingConfig {

251

/** Filename pattern with placeholders */

252

pattern?: string;

253

254

/** Length of content hash */

255

hashLength?: number;

256

257

/** Whether to preserve entry file names */

258

preserveEntryNames?: boolean;

259

260

/** Output subdirectories by type */

261

outputDir?: Record<string, string>;

262

263

/** Custom name mapping */

264

customNames?: Record<string, string>;

265

}

266

```

267

268

**Pattern Placeholders:**

269

- `[name]` - Base name of the file or bundle

270

- `[hash]` - Content hash for cache busting

271

- `[ext]` - File extension based on bundle type

272

- `[id]` - Bundle ID

273

- `[type]` - Bundle type

274

- `[target]` - Target environment name