0
# Code Generation
1
2
Utilities for generating valid JavaScript code, identifiers, and ES modules from data structures. These functions help create bundle-safe code and transform data into tree-shakable modules.
3
4
## Capabilities
5
6
### makeLegalIdentifier
7
8
Converts strings into valid JavaScript identifiers that are safe for use in bundles. Handles special characters, reserved words, and naming conflicts.
9
10
```typescript { .api }
11
/**
12
* Constructs a bundle-safe identifier from a string
13
* Converts invalid characters and handles reserved words
14
* @param str - The string to convert to a legal identifier
15
* @returns A valid JavaScript identifier
16
*/
17
function makeLegalIdentifier(str: string): string;
18
```
19
20
**Parameters:**
21
22
- `str` (string): The string to convert to a legal JavaScript identifier
23
24
**Returns:** A valid JavaScript identifier string
25
26
**Transformation Rules:**
27
28
- Converts hyphens to camelCase: `'foo-bar'` → `'fooBar'`
29
- Replaces invalid characters with underscores
30
- Prefixes with underscore if starts with number: `'123abc'` → `'_123abc'`
31
- Handles reserved words: `'typeof'` → `'_typeof'`
32
- Returns `'_'` for empty strings
33
34
**Usage Examples:**
35
36
```typescript
37
import { makeLegalIdentifier } from "@rollup/pluginutils";
38
39
// Basic transformations
40
makeLegalIdentifier('foo-bar'); // 'fooBar'
41
makeLegalIdentifier('my-component'); // 'myComponent'
42
makeLegalIdentifier('foo_bar'); // 'foo_bar' (already valid)
43
44
// Handle special characters
45
makeLegalIdentifier('foo@bar'); // 'foo_bar'
46
makeLegalIdentifier('foo.bar'); // 'foo_bar'
47
makeLegalIdentifier('foo bar'); // 'foo_bar'
48
49
// Reserved words and edge cases
50
makeLegalIdentifier('typeof'); // '_typeof'
51
makeLegalIdentifier('class'); // '_class'
52
makeLegalIdentifier('123abc'); // '_123abc'
53
makeLegalIdentifier(''); // '_'
54
55
// Use in code generation
56
export default function myPlugin() {
57
return {
58
generateBundle(options, bundle) {
59
for (const [fileName, chunk] of Object.entries(bundle)) {
60
if (chunk.type === 'chunk') {
61
// Generate safe variable names from file names
62
const varName = makeLegalIdentifier(
63
fileName.replace(/\.[^.]+$/, '') // remove extension
64
);
65
66
// Use in generated code
67
const wrapper = `
68
const ${varName} = (function() {
69
${chunk.code}
70
return exports;
71
})();
72
`;
73
74
chunk.code = wrapper;
75
}
76
}
77
}
78
};
79
}
80
81
// Generate imports from module names
82
function generateImportName(moduleName) {
83
// Convert scoped packages: '@rollup/plugin-utils' → 'rollupPluginUtils'
84
const cleaned = moduleName.replace(/^@/, '').replace(/[^\w]/g, '-');
85
return makeLegalIdentifier(cleaned);
86
}
87
88
generateImportName('@rollup/plugin-utils'); // 'rollupPluginUtils'
89
generateImportName('lodash-es'); // 'lodashEs'
90
generateImportName('my-library/sub-module'); // 'myLibrarySubModule'
91
```
92
93
### dataToEsm
94
95
Transforms JavaScript objects into tree-shakable ES module source code. Generates both named exports and default exports with comprehensive formatting options.
96
97
```typescript { .api }
98
/**
99
* Transforms objects into tree-shakable ES Module imports
100
* Generates both named and default exports with formatting options
101
* @param data - An object to transform into an ES module
102
* @param options - Configuration options for code generation
103
* @returns Generated ES module source code
104
*/
105
function dataToEsm(data: unknown, options?: DataToEsmOptions): string;
106
107
interface DataToEsmOptions {
108
compact?: boolean;
109
includeArbitraryNames?: boolean;
110
indent?: string;
111
namedExports?: boolean;
112
objectShorthand?: boolean;
113
preferConst?: boolean;
114
}
115
```
116
117
**Parameters:**
118
119
- `data` (unknown): The data to convert to ES module format
120
- `options` (DataToEsmOptions, optional): Configuration options
121
122
**DataToEsmOptions:**
123
124
- `compact?: boolean` - Minimize whitespace in generated code (default: `false`)
125
- `includeArbitraryNames?: boolean` - Support non-identifier keys as named exports using arbitrary module namespace identifier names (default: `false`)
126
- `indent?: string` - Indentation string for formatting (default: `'\t'`)
127
- `namedExports?: boolean` - Generate named exports for object properties (default: `true`)
128
- `objectShorthand?: boolean` - Use ES6 object property shorthand syntax (default: `true`)
129
- `preferConst?: boolean` - Use `const` instead of `var` for declarations (default: `false`)
130
131
**Returns:** Generated ES module source code as a string
132
133
**Supported Data Types:**
134
135
- Objects with property values
136
- Arrays with numeric indices
137
- Primitive values (string, number, boolean)
138
- Special values (Date, RegExp, BigInt, Symbol, Infinity, NaN, undefined)
139
- Nested structures
140
141
**Usage Examples:**
142
143
```typescript
144
import { dataToEsm } from "@rollup/pluginutils";
145
146
// Basic object transformation
147
const config = {
148
apiUrl: 'https://api.example.com',
149
timeout: 5000,
150
debug: true
151
};
152
153
const moduleCode = dataToEsm(config, {
154
compact: false,
155
indent: ' ',
156
preferConst: true,
157
objectShorthand: true,
158
namedExports: true
159
});
160
161
console.log(moduleCode);
162
/*
163
export const apiUrl = 'https://api.example.com';
164
export const timeout = 5000;
165
export const debug = true;
166
export default { apiUrl, timeout, debug };
167
*/
168
169
// Complex data with formatting options
170
const complexData = {
171
users: ['alice', 'bob'],
172
settings: {
173
theme: 'dark',
174
notifications: true
175
},
176
created: new Date('2023-01-01'),
177
pattern: /^\w+$/,
178
bigNumber: BigInt('9007199254740991')
179
};
180
181
const compactCode = dataToEsm(complexData, {
182
compact: true,
183
namedExports: true,
184
includeArbitraryNames: false
185
});
186
187
// Use in plugin for configuration files
188
export default function configPlugin(options = {}) {
189
return {
190
resolveId(id) {
191
if (id === 'virtual:config') {
192
return id;
193
}
194
},
195
196
load(id) {
197
if (id === 'virtual:config') {
198
// Convert plugin options to ES module
199
return dataToEsm(options, {
200
preferConst: true,
201
objectShorthand: true,
202
namedExports: true
203
});
204
}
205
}
206
};
207
}
208
209
// Handle non-identifier keys
210
const dataWithSpecialKeys = {
211
'normal-key': 'value1',
212
'special@key': 'value2',
213
'123numeric': 'value3'
214
};
215
216
const codeWithArbitraryNames = dataToEsm(dataWithSpecialKeys, {
217
includeArbitraryNames: true,
218
namedExports: true
219
});
220
221
/*
222
export const normalKey = 'value1';
223
export { 'special@key' as specialKey };
224
export { '123numeric' as _123numeric };
225
export default { normalKey, 'special@key': specialKey, '123numeric': _123numeric };
226
*/
227
228
// Generate module from JSON file
229
export default function jsonPlugin() {
230
return {
231
transform(code, id) {
232
if (id.endsWith('.json')) {
233
const data = JSON.parse(code);
234
235
// Convert JSON to tree-shakable ES module
236
const esModule = dataToEsm(data, {
237
preferConst: true,
238
namedExports: true,
239
objectShorthand: true
240
});
241
242
return { code: esModule };
243
}
244
}
245
};
246
}
247
```
248
249
## Common Patterns
250
251
### Configuration Module Generation
252
253
```typescript
254
import { dataToEsm, makeLegalIdentifier } from "@rollup/pluginutils";
255
256
export default function configPlugin(userConfig = {}) {
257
const defaultConfig = {
258
apiEndpoint: 'https://api.example.com',
259
timeout: 5000,
260
retries: 3,
261
debug: false
262
};
263
264
const finalConfig = { ...defaultConfig, ...userConfig };
265
266
return {
267
resolveId(id) {
268
if (id === 'virtual:app-config') return id;
269
},
270
271
load(id) {
272
if (id === 'virtual:app-config') {
273
// Generate tree-shakable config module
274
return dataToEsm(finalConfig, {
275
preferConst: true,
276
namedExports: true,
277
objectShorthand: true,
278
compact: false
279
});
280
}
281
}
282
};
283
}
284
285
// Usage in application code:
286
// import { apiEndpoint, timeout } from 'virtual:app-config';
287
```
288
289
### Dynamic Import Generation
290
291
```typescript
292
import { makeLegalIdentifier, dataToEsm } from "@rollup/pluginutils";
293
294
export default function dynamicImportPlugin(options = {}) {
295
const { modules = {} } = options;
296
297
return {
298
generateBundle() {
299
// Generate loader module with dynamic imports
300
const loaderCode = Object.entries(modules)
301
.map(([key, modulePath]) => {
302
const importName = makeLegalIdentifier(key);
303
return `
304
export const ${importName} = () => import('${modulePath}');
305
`.trim();
306
})
307
.join('\n');
308
309
// Also generate static config
310
const configModule = dataToEsm({
311
availableModules: Object.keys(modules),
312
moduleCount: Object.keys(modules).length
313
});
314
315
this.emitFile({
316
type: 'asset',
317
fileName: 'dynamic-loader.js',
318
source: loaderCode
319
});
320
321
this.emitFile({
322
type: 'asset',
323
fileName: 'loader-config.js',
324
source: configModule
325
});
326
}
327
};
328
}
329
```
330
331
### Safe Variable Generation
332
333
```typescript
334
import { makeLegalIdentifier } from "@rollup/pluginutils";
335
336
export default function namespacePlugin(options = {}) {
337
const { namespace = 'MyLibrary' } = options;
338
339
return {
340
generateBundle(options, bundle) {
341
for (const [fileName, chunk] of Object.entries(bundle)) {
342
if (chunk.type === 'chunk') {
343
// Generate safe namespace from file name
344
const safeName = makeLegalIdentifier(
345
fileName.replace(/\.[^.]+$/, '') // remove extension
346
);
347
348
// Wrap exports in namespace
349
const namespacedCode = `
350
(function(global) {
351
'use strict';
352
353
// Original code
354
${chunk.code}
355
356
// Create namespace
357
global.${namespace} = global.${namespace} || {};
358
global.${namespace}.${safeName} = exports;
359
360
})(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this);
361
`;
362
363
chunk.code = namespacedCode;
364
}
365
}
366
}
367
};
368
}
369
```