0
# @rollup/plugin-dynamic-import-vars
1
2
A Rollup plugin that transforms dynamic imports containing variables into static imports with runtime switching logic. It analyzes template literals and string concatenation patterns in dynamic imports and generates glob patterns to include matching files in the bundle.
3
4
## Package Information
5
6
- **Package Name**: @rollup/plugin-dynamic-import-vars
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Node.js Requirements**: >= 14.0.0
10
- **Rollup Requirements**: >= 1.20.0 || 2.0.0+ || 3.0.0+ || 4.0.0+
11
- **Installation**: `npm install @rollup/plugin-dynamic-import-vars --save-dev`
12
13
## Core Imports
14
15
```javascript
16
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
17
```
18
19
For CommonJS:
20
21
```javascript
22
const dynamicImportVars = require('@rollup/plugin-dynamic-import-vars');
23
```
24
25
Named imports:
26
27
```javascript
28
import dynamicImportVars, { dynamicImportToGlob, VariableDynamicImportError } from '@rollup/plugin-dynamic-import-vars';
29
```
30
31
## Basic Usage
32
33
```javascript
34
// rollup.config.js
35
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
36
37
export default {
38
input: 'src/index.js',
39
output: {
40
dir: 'dist',
41
format: 'es'
42
},
43
plugins: [
44
dynamicImportVars({
45
include: ['src/**/*.js'],
46
exclude: ['node_modules/**'],
47
warnOnError: false,
48
errorWhenNoFilesFound: false
49
})
50
]
51
};
52
```
53
54
The plugin transforms code like this:
55
56
```javascript
57
// Before transformation
58
function importLocale(locale) {
59
return import(`./locales/${locale}.js`);
60
}
61
```
62
63
Into this:
64
65
```javascript
66
// After transformation
67
function __variableDynamicImportRuntime0__(path) {
68
switch (path) {
69
case './locales/en-GB.js': return import('./locales/en-GB.js');
70
case './locales/en-US.js': return import('./locales/en-US.js');
71
case './locales/nl-NL.js': return import('./locales/nl-NL.js');
72
default: return new Promise(function(resolve, reject) {
73
(typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(
74
reject.bind(null, new Error("Unknown variable dynamic import: " + path))
75
);
76
});
77
}
78
}
79
80
function importLocale(locale) {
81
return __variableDynamicImportRuntime0__(`./locales/${locale}.js`);
82
}
83
```
84
85
## Architecture
86
87
The plugin uses a sophisticated multi-stage approach to transform dynamic imports containing variables:
88
89
- **AST Walking**: Uses `estree-walker` to traverse the parsed JavaScript AST and locate `ImportExpression` nodes
90
- **Pattern Analysis**: The `dynamicImportToGlob` utility converts variable dynamic import expressions into glob patterns by analyzing template literals, string concatenation, and method chaining patterns
91
- **File Discovery**: Uses `fast-glob` to find all files matching the generated glob pattern relative to the importing module
92
- **Runtime Function Injection**: Generates a switch-case runtime function that maps each discovered file path to a static import statement
93
- **Code Transformation**: Uses `magic-string` to replace the original dynamic import with a call to the generated runtime function while preserving import assertions
94
95
This approach ensures that all potential imports are statically analyzable and included in the bundle while maintaining runtime behavior that matches the original dynamic import logic.
96
97
## Capabilities
98
99
### Plugin Factory Function
100
101
Creates a Rollup plugin instance that transforms dynamic imports containing variables.
102
103
```javascript { .api }
104
/**
105
* Creates a Rollup plugin that transforms dynamic imports containing variables
106
* @param options - Configuration options for the plugin
107
* @returns Rollup plugin instance
108
*/
109
function dynamicImportVars(options?: RollupDynamicImportVariablesOptions): Plugin;
110
111
interface RollupDynamicImportVariablesOptions {
112
/** Files to include in transformation (picomatch pattern or array) */
113
include?: FilterPattern;
114
/** Files to exclude from transformation (picomatch pattern or array) */
115
exclude?: FilterPattern;
116
/** Whether to throw errors when no files match glob patterns (default: false) */
117
errorWhenNoFilesFound?: boolean;
118
/** Whether to warn instead of error on invalid patterns (default: false) */
119
warnOnError?: boolean;
120
}
121
122
type FilterPattern = string | RegExp | Array<string | RegExp> | null;
123
124
interface Plugin {
125
name: string;
126
transform(code: string, id: string): TransformResult | null;
127
}
128
129
interface TransformResult {
130
code: string;
131
map: SourceMap;
132
}
133
```
134
135
**Usage Examples:**
136
137
```javascript
138
// Basic usage with default options
139
export default {
140
plugins: [dynamicImportVars()]
141
};
142
143
// With specific file patterns
144
export default {
145
plugins: [
146
dynamicImportVars({
147
include: ['src/**/*.js', 'lib/**/*.mjs'],
148
exclude: ['src/legacy/**']
149
})
150
]
151
};
152
153
// With error handling configuration
154
export default {
155
plugins: [
156
dynamicImportVars({
157
warnOnError: true,
158
errorWhenNoFilesFound: true
159
})
160
]
161
};
162
```
163
164
### Dynamic Import to Glob Conversion
165
166
Utility function that converts dynamic import AST nodes to glob patterns for file matching.
167
168
```javascript { .api }
169
/**
170
* Converts a dynamic import AST node to a glob pattern
171
* @param node - AST node representing the import expression source
172
* @param sourceString - String representation of the import expression
173
* @returns Glob pattern string or null if not a variable dynamic import
174
* @throws VariableDynamicImportError for invalid import patterns
175
*/
176
function dynamicImportToGlob(node: BaseNode, sourceString: string): string | null;
177
178
interface BaseNode {
179
type: string;
180
start?: number;
181
end?: number;
182
[key: string]: any;
183
}
184
```
185
186
**Usage Examples:**
187
188
```javascript
189
import { dynamicImportToGlob } from '@rollup/plugin-dynamic-import-vars';
190
191
// Example usage in custom AST processing
192
const glob = dynamicImportToGlob(node, "`./locales/${locale}.js`");
193
// Returns: './locales/*.js'
194
195
// Handles various patterns:
196
// `./locales/${locale}.js` -> './locales/*.js'
197
// `./${folder}/${name}.js` -> './*/*.js'
198
// `./modules-${name}/index.js` -> './modules-*/index.js'
199
// './locales/' + locale + '.js' -> './locales/*.js'
200
// './locales/'.concat(locale, '.js') -> './locales/*.js'
201
```
202
203
### Variable Dynamic Import Error
204
205
Custom error class for invalid dynamic import patterns that cannot be statically analyzed.
206
207
```javascript { .api }
208
/**
209
* Error thrown when dynamic imports cannot be statically analyzed
210
* @extends Error
211
*/
212
class VariableDynamicImportError extends Error {
213
constructor(message: string);
214
}
215
```
216
217
**Common error scenarios:**
218
219
```javascript
220
// These patterns throw VariableDynamicImportError:
221
222
// Must start with ./ or ../
223
import(bar); // Invalid: bare import
224
import(`${bar}.js`); // Invalid: starts with variable
225
import(`/foo/${bar}.js`); // Invalid: absolute path
226
227
// Must end with file extension
228
import(`./foo/${bar}`); // Invalid: no extension
229
230
// Own directory imports need specific patterns
231
import(`./${foo}.js`); // Invalid: too generic
232
import(`./module-${foo}.js`); // Valid: specific pattern
233
234
// Cannot contain wildcards
235
import(`./foo/*${bar}.js`); // Invalid: contains asterisk
236
```
237
238
## Supported Import Patterns
239
240
The plugin supports several dynamic import patterns:
241
242
### Template Literals
243
```javascript
244
import(`./locales/${locale}.js`); // -> './locales/*.js'
245
import(`./modules/${type}/${name}.js`); // -> './modules/*/*.js'
246
import(`./components-${variant}.js`); // -> './components-*.js'
247
import(`./modules-${name}/index.js`); // -> './modules-*/index.js'
248
```
249
250
### String Concatenation
251
```javascript
252
import('./locales/' + locale + '.js'); // -> './locales/*.js'
253
import('./src/' + folder + '/' + file); // -> './src/*/*.js'
254
import('./locales/' + locale + foo + bar + '.js'); // -> './locales/*.js'
255
import('./locales/' + `${locale}.js`); // -> './locales/*.js'
256
import('./locales/' + `${foo + bar}.js`); // -> './locales/*.js'
257
```
258
259
### Method Chaining
260
```javascript
261
import('./locales/'.concat(locale, '.js')); // -> './locales/*.js'
262
import('./base/'.concat(type, '/').concat(name, '.js')); // -> './base/*/*.js'
263
import('./'.concat(folder, '/').concat(name, '.js')); // -> './*/*.js'
264
```
265
266
### Import Assertions
267
Import assertions are fully preserved in the transformed code, allowing integration with other plugins:
268
269
```javascript
270
// Input - dynamic import with CSS assertion
271
import(`./styles/${sheet}.css`, { assert: { type: 'css' } });
272
273
// Output - each case preserves the assertion
274
function __variableDynamicImportRuntime0__(path) {
275
switch (path) {
276
case './styles/dark.css': return import('./styles/dark.css', { assert: { type: 'css' } });
277
case './styles/light.css': return import('./styles/light.css', { assert: { type: 'css' } });
278
default: return new Promise(function(resolve, reject) {
279
(typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(
280
reject.bind(null, new Error("Unknown variable dynamic import: " + path))
281
);
282
});
283
}
284
}
285
```
286
287
This preservation enables compatibility with plugins like `rollup-plugin-import-css` that rely on import assertions to determine how to process different file types.
288
289
## Validation Rules
290
291
The plugin enforces several rules to ensure safe static analysis:
292
293
1. **Relative paths only**: Imports must start with `./` or `../`
294
2. **File extensions required**: Static parts must include file extensions
295
3. **Specific patterns for own directory**: Cannot use generic patterns in same directory
296
4. **Single-level globs**: Each variable becomes one `*`, limited to one level deep per directory (e.g., `import(\`./foo/\${x}\${y}/\${z}.js\`)` becomes `./foo/*/*.js`, not `./foo/**/*.js`)
297
5. **No wildcards in input**: Dynamic import strings cannot contain `*` characters
298
299
## Error Handling
300
301
The plugin provides flexible error handling through configuration options:
302
303
- **`warnOnError: false`** (default): Stops build on invalid patterns
304
- **`warnOnError: true`**: Issues warnings but continues build, leaving code unchanged
305
- **`errorWhenNoFilesFound: false`** (default): Continues when no files match glob
306
- **`errorWhenNoFilesFound: true`**: Throws error when glob matches no files
307
308
When `warnOnError` is `true` and `errorWhenNoFilesFound` is `true`, the plugin will warn instead of error for missing files.
309
310
## Types
311
312
```javascript { .api }
313
interface RollupDynamicImportVariablesOptions {
314
include?: FilterPattern;
315
exclude?: FilterPattern;
316
errorWhenNoFilesFound?: boolean;
317
warnOnError?: boolean;
318
}
319
320
type FilterPattern = string | RegExp | Array<string | RegExp> | null;
321
322
class VariableDynamicImportError extends Error {}
323
324
function dynamicImportToGlob(node: BaseNode, sourceString: string): string | null;
325
326
interface BaseNode {
327
type: string;
328
start?: number;
329
end?: number;
330
[key: string]: any;
331
}
332
333
interface Plugin {
334
name: string;
335
transform(code: string, id: string): TransformResult | null;
336
}
337
338
interface TransformResult {
339
code: string;
340
map: SourceMap;
341
}
342
343
interface SourceMap {
344
file: string;
345
includeContent: boolean;
346
hires: boolean;
347
}
348
```