or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-rollup--plugin-dynamic-import-vars

A Rollup plugin that transforms dynamic imports containing variables into static imports with runtime switching logic.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@rollup/plugin-dynamic-import-vars@2.1.x

To install, run

npx @tessl/cli install tessl/npm-rollup--plugin-dynamic-import-vars@2.1.0

index.mddocs/

@rollup/plugin-dynamic-import-vars

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.

Package Information

  • Package Name: @rollup/plugin-dynamic-import-vars
  • Package Type: npm
  • Language: TypeScript
  • Node.js Requirements: >= 14.0.0
  • Rollup Requirements: >= 1.20.0 || 2.0.0+ || 3.0.0+ || 4.0.0+
  • Installation: npm install @rollup/plugin-dynamic-import-vars --save-dev

Core Imports

import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';

For CommonJS:

const dynamicImportVars = require('@rollup/plugin-dynamic-import-vars');

Named imports:

import dynamicImportVars, { dynamicImportToGlob, VariableDynamicImportError } from '@rollup/plugin-dynamic-import-vars';

Basic Usage

// rollup.config.js
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';

export default {
  input: 'src/index.js',
  output: {
    dir: 'dist',
    format: 'es'
  },
  plugins: [
    dynamicImportVars({
      include: ['src/**/*.js'],
      exclude: ['node_modules/**'],
      warnOnError: false,
      errorWhenNoFilesFound: false
    })
  ]
};

The plugin transforms code like this:

// Before transformation
function importLocale(locale) {
  return import(`./locales/${locale}.js`);
}

Into this:

// After transformation
function __variableDynamicImportRuntime0__(path) {
  switch (path) {
    case './locales/en-GB.js': return import('./locales/en-GB.js');
    case './locales/en-US.js': return import('./locales/en-US.js');
    case './locales/nl-NL.js': return import('./locales/nl-NL.js');
    default: return new Promise(function(resolve, reject) {
      (typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(
        reject.bind(null, new Error("Unknown variable dynamic import: " + path))
      );
    });
  }
}

function importLocale(locale) {
  return __variableDynamicImportRuntime0__(`./locales/${locale}.js`);
}

Architecture

The plugin uses a sophisticated multi-stage approach to transform dynamic imports containing variables:

  • AST Walking: Uses estree-walker to traverse the parsed JavaScript AST and locate ImportExpression nodes
  • Pattern Analysis: The dynamicImportToGlob utility converts variable dynamic import expressions into glob patterns by analyzing template literals, string concatenation, and method chaining patterns
  • File Discovery: Uses fast-glob to find all files matching the generated glob pattern relative to the importing module
  • Runtime Function Injection: Generates a switch-case runtime function that maps each discovered file path to a static import statement
  • Code Transformation: Uses magic-string to replace the original dynamic import with a call to the generated runtime function while preserving import assertions

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.

Capabilities

Plugin Factory Function

Creates a Rollup plugin instance that transforms dynamic imports containing variables.

/**
 * Creates a Rollup plugin that transforms dynamic imports containing variables
 * @param options - Configuration options for the plugin
 * @returns Rollup plugin instance
 */
function dynamicImportVars(options?: RollupDynamicImportVariablesOptions): Plugin;

interface RollupDynamicImportVariablesOptions {
  /** Files to include in transformation (picomatch pattern or array) */
  include?: FilterPattern;
  /** Files to exclude from transformation (picomatch pattern or array) */
  exclude?: FilterPattern;
  /** Whether to throw errors when no files match glob patterns (default: false) */
  errorWhenNoFilesFound?: boolean;
  /** Whether to warn instead of error on invalid patterns (default: false) */
  warnOnError?: boolean;
}

type FilterPattern = string | RegExp | Array<string | RegExp> | null;

interface Plugin {
  name: string;
  transform(code: string, id: string): TransformResult | null;
}

interface TransformResult {
  code: string;
  map: SourceMap;
}

Usage Examples:

// Basic usage with default options
export default {
  plugins: [dynamicImportVars()]
};

// With specific file patterns
export default {
  plugins: [
    dynamicImportVars({
      include: ['src/**/*.js', 'lib/**/*.mjs'],
      exclude: ['src/legacy/**']
    })
  ]
};

// With error handling configuration
export default {
  plugins: [
    dynamicImportVars({
      warnOnError: true,
      errorWhenNoFilesFound: true
    })
  ]
};

Dynamic Import to Glob Conversion

Utility function that converts dynamic import AST nodes to glob patterns for file matching.

/**
 * Converts a dynamic import AST node to a glob pattern
 * @param node - AST node representing the import expression source
 * @param sourceString - String representation of the import expression
 * @returns Glob pattern string or null if not a variable dynamic import
 * @throws VariableDynamicImportError for invalid import patterns
 */
function dynamicImportToGlob(node: BaseNode, sourceString: string): string | null;

interface BaseNode {
  type: string;
  start?: number;
  end?: number;
  [key: string]: any;
}

Usage Examples:

import { dynamicImportToGlob } from '@rollup/plugin-dynamic-import-vars';

// Example usage in custom AST processing
const glob = dynamicImportToGlob(node, "`./locales/${locale}.js`");
// Returns: './locales/*.js'

// Handles various patterns:
// `./locales/${locale}.js` -> './locales/*.js'
// `./${folder}/${name}.js` -> './*/*.js'  
// `./modules-${name}/index.js` -> './modules-*/index.js'
// './locales/' + locale + '.js' -> './locales/*.js'
// './locales/'.concat(locale, '.js') -> './locales/*.js'

Variable Dynamic Import Error

Custom error class for invalid dynamic import patterns that cannot be statically analyzed.

/**
 * Error thrown when dynamic imports cannot be statically analyzed
 * @extends Error
 */
class VariableDynamicImportError extends Error {
  constructor(message: string);
}

Common error scenarios:

// These patterns throw VariableDynamicImportError:

// Must start with ./ or ../
import(bar);                    // Invalid: bare import
import(`${bar}.js`);           // Invalid: starts with variable
import(`/foo/${bar}.js`);      // Invalid: absolute path

// Must end with file extension
import(`./foo/${bar}`);        // Invalid: no extension

// Own directory imports need specific patterns
import(`./${foo}.js`);         // Invalid: too generic
import(`./module-${foo}.js`);  // Valid: specific pattern

// Cannot contain wildcards
import(`./foo/*${bar}.js`);    // Invalid: contains asterisk

Supported Import Patterns

The plugin supports several dynamic import patterns:

Template Literals

import(`./locales/${locale}.js`);           // -> './locales/*.js'
import(`./modules/${type}/${name}.js`);     // -> './modules/*/*.js'
import(`./components-${variant}.js`);       // -> './components-*.js'
import(`./modules-${name}/index.js`);       // -> './modules-*/index.js'

String Concatenation

import('./locales/' + locale + '.js');                    // -> './locales/*.js'
import('./src/' + folder + '/' + file);                   // -> './src/*/*.js'
import('./locales/' + locale + foo + bar + '.js');       // -> './locales/*.js'
import('./locales/' + `${locale}.js`);                   // -> './locales/*.js'
import('./locales/' + `${foo + bar}.js`);                // -> './locales/*.js'

Method Chaining

import('./locales/'.concat(locale, '.js'));              // -> './locales/*.js'
import('./base/'.concat(type, '/').concat(name, '.js')); // -> './base/*/*.js'
import('./'.concat(folder, '/').concat(name, '.js'));    // -> './*/*.js'

Import Assertions

Import assertions are fully preserved in the transformed code, allowing integration with other plugins:

// Input - dynamic import with CSS assertion
import(`./styles/${sheet}.css`, { assert: { type: 'css' } });

// Output - each case preserves the assertion
function __variableDynamicImportRuntime0__(path) {
  switch (path) {
    case './styles/dark.css': return import('./styles/dark.css', { assert: { type: 'css' } });
    case './styles/light.css': return import('./styles/light.css', { assert: { type: 'css' } });
    default: return new Promise(function(resolve, reject) {
      (typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(
        reject.bind(null, new Error("Unknown variable dynamic import: " + path))
      );
    });
  }
}

This preservation enables compatibility with plugins like rollup-plugin-import-css that rely on import assertions to determine how to process different file types.

Validation Rules

The plugin enforces several rules to ensure safe static analysis:

  1. Relative paths only: Imports must start with ./ or ../
  2. File extensions required: Static parts must include file extensions
  3. Specific patterns for own directory: Cannot use generic patterns in same directory
  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`)
  5. No wildcards in input: Dynamic import strings cannot contain * characters

Error Handling

The plugin provides flexible error handling through configuration options:

  • warnOnError: false (default): Stops build on invalid patterns
  • warnOnError: true: Issues warnings but continues build, leaving code unchanged
  • errorWhenNoFilesFound: false (default): Continues when no files match glob
  • errorWhenNoFilesFound: true: Throws error when glob matches no files

When warnOnError is true and errorWhenNoFilesFound is true, the plugin will warn instead of error for missing files.

Types

interface RollupDynamicImportVariablesOptions {
  include?: FilterPattern;
  exclude?: FilterPattern;
  errorWhenNoFilesFound?: boolean;
  warnOnError?: boolean;
}

type FilterPattern = string | RegExp | Array<string | RegExp> | null;

class VariableDynamicImportError extends Error {}

function dynamicImportToGlob(node: BaseNode, sourceString: string): string | null;

interface BaseNode {
  type: string;
  start?: number;
  end?: number;
  [key: string]: any;
}

interface Plugin {
  name: string;
  transform(code: string, id: string): TransformResult | null;
}

interface TransformResult {
  code: string;
  map: SourceMap;
}

interface SourceMap {
  file: string;
  includeContent: boolean;
  hires: boolean;
}