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