Babel plugin enabling dynamic imports with code splitting for lazy loading of dependencies. Transforms import() calls to enable runtime loading of NPM packages and app modules.
Enable dynamic import support by adding the babel plugin to your build configuration:
// In ember-cli-build.js
let app = new EmberApp(defaults, {
babel: {
plugins: [require.resolve('ember-auto-import/babel-plugin')]
}
});For addon authors, configure in index.js:
// In addon's index.js
module.exports = {
name: 'my-addon',
options: {
babel: {
plugins: [require.resolve('ember-auto-import/babel-plugin')]
}
}
};The main babel plugin function that transforms dynamic imports:
/**
* Babel plugin for transforming dynamic imports
* Transforms import() calls for NPM packages to use webpack dynamic imports
* @param babel - Babel instance with types and utilities
* @returns Babel plugin configuration object
*/
function emberAutoImport(babel: typeof Babel): BabelPlugin;
interface BabelPlugin {
/** Inherits from babel-plugin-syntax-dynamic-import for syntax support */
inherits: any;
/** Babel visitor methods for transforming AST nodes */
visitor: {
/** Transform import() calls for dynamic imports */
Import(path: NodePath<t.Import>, state: any): void;
/** Transform CallExpression nodes for importSync handling */
CallExpression(path: any): void;
};
}Usage Example:
// The plugin automatically transforms these patterns:
// NPM package dynamic import
import('chart.js').then(Chart => {
// Becomes: emberAutoImportDynamic('chart.js')
});
// Template literal dynamic import
const libName = 'lodash';
import(`${libName}/get`).then(get => {
// Becomes: emberAutoImportDynamic('${e}', libName)
});
// importSync transformation
import { importSync } from '@embroider/macros';
const lodash = importSync('lodash');
// Becomes: require('lodash')Transform import() calls for NPM packages and local modules:
/**
* Transforms import() calls based on the imported path type
* Handles static strings, template literals, and relative paths
*/
visitor: {
Import(path: NodePath, state: BabelState): void;
}The transformation process:
emberAutoImportDynamic()Static String Imports:
// Original
import('lodash').then(module => module.default);
// Transformed
emberAutoImportDynamic('lodash').then(module => module.default);Template Literal Imports:
// Original
const lib = 'chart';
import(`${lib}.js`).then(module => module.default);
// Transformed
emberAutoImportDynamic('${e}', lib).then(module => module.default);Transform importSync() calls from @embroider/macros to regular require() calls:
/**
* Transforms importSync() calls to require() or emberAutoImportSync()
* Handles both static strings and template literals
*/
visitor: {
CallExpression(path: NodePath): void;
}Static ImportSync:
import { importSync } from '@embroider/macros';
// Original
const lodash = importSync('lodash');
// Transformed
const lodash = require('lodash');Template Literal ImportSync:
// Original
const libName = 'lodash';
const lib = importSync(`${libName}/get`);
// Transformed
const lib = emberAutoImportSync('${e}', libName);The runtime functions created by the transformation:
/**
* Runtime function for dynamic imports created by the babel plugin
* Handles lazy loading of NPM packages with webpack code splitting
*/
function emberAutoImportDynamic(specifier: string, ...expressions: any[]): Promise<any>;
/**
* Runtime function for synchronous imports created by the babel plugin
* Handles template literal imports with variable substitution
*/
function emberAutoImportSync(pattern: string, ...expressions: any[]): any;These functions are automatically available in your runtime environment and handle:
Usage Examples:
// Dynamic import with code splitting
export default Route.extend({
async model() {
// This creates a separate bundle for chart.js
const Chart = await import('chart.js');
const data = await fetch('/api/chart-data').then(r => r.json());
return { Chart: Chart.default, data };
}
});
// Dynamic import with variables
async loadUtility(utilityName) {
// Template literal allows dynamic selection
const utility = await import(`lodash/${utilityName}`);
return utility.default;
}
// Loading multiple chunks
Promise.all([
import('d3'),
import('topojson'),
import('lodash')
]).then(([d3, topojson, lodash]) => {
// All three libraries loaded as separate chunks
});The babel plugin includes error handling for common issues:
/**
* Error cases handled by the babel plugin:
* - Missing module-resolver plugin for relative imports
* - URL imports with importSync (not supported)
* - Invalid template literal patterns
*/Common Errors:
// Error: URL imports not supported with importSync
import { importSync } from '@embroider/macros';
const lib = importSync('https://cdn.example.com/lib.js'); // Throws error
// Error: Missing module-resolver plugin
import(`./relative-${path}`).then(module => {
// Requires babel-plugin-module-resolver to be configured
});Dynamic imports behave differently in development and production:
Development:
eval sourcemaps for fast rebuilds (unless forbidEval: true)Production:
Deployment Considerations:
// Ensure your deployment includes all generated chunks
// Default location: dist/assets/chunk.*.js and dist/assets/chunk.*.css
// Configure custom public path if deploying to CDN
autoImport: {
publicAssetURL: 'https://cdn.example.com/assets/'
}/**
* Babel AST node path for traversal
*/
interface NodePath {
node: Node;
parent: Node;
parentPath: NodePath;
replaceWith(node: Node): void;
}
/**
* Babel transformation state
*/
interface BabelState {
file: {
opts: {
filename: string;
plugins: PluginEntry[];
};
};
opts: any;
}
/**
* Babel plugin entry configuration
*/
interface PluginEntry {
key: string;
options: any;
}