Babel plugin that transforms named component imports into specific module paths for tree-shaking optimization
npx @tessl/cli install tessl/npm-babel-plugin-component@1.1.0Babel Plugin Component is a Babel transformation plugin that enables modular component imports by transforming named imports into specific module paths. It automatically handles both JavaScript module imports and CSS style imports, enabling tree-shaking optimization and reducing bundle sizes for component libraries like Element UI, Ant Design, and custom libraries.
npm install babel-plugin-component -D// As Babel plugin in .babelrc
{
"plugins": [["component", options]]
}// Programmatic usage
const plugin = require('babel-plugin-component');// .babelrc configuration
{
"plugins": [["component", {
"libraryName": "element-ui",
"style": true
}]]
}// Input code
import { Button, message } from 'element-ui';
// Output after transformation (with Babel helpers)
require("element-ui/lib/button/style.css");
var _Button = _interopRequireDefault(require("element-ui/lib/button")).default;
require("element-ui/lib/message/style.css");
var _message = _interopRequireDefault(require("element-ui/lib/message")).default;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }Babel Plugin Component operates through several key mechanisms:
Core function that creates the Babel plugin with specified default library name.
/**
* Creates a Babel plugin function with specified default library name
* @param {string} defaultLibraryName - Default library name to use when not specified in options
* @returns {Function} Babel plugin function
*/
function core(defaultLibraryName);Default export that provides a pre-configured plugin for Element UI.
/**
* Default plugin export configured for 'element-ui' library
* @returns {Function} Babel plugin configured for Element UI
*/
module.exports; // Returns core('element-ui')Complete configuration interface for customizing plugin behavior.
interface PluginOptions {
/** Name of the library to transform imports for */
libraryName?: string;
/** Library directory path (default: 'lib') */
libDir?: string;
/** Style import handling - true for default style.css, string for custom path, false for none */
style?: boolean | string;
/** Root directory for components */
root?: string;
/** Convert camelCase to dash-case for component names (default: true) */
camel2Dash?: boolean;
/** File extension for style files (default: '.css') */
ext?: string;
/** Style library name - regular name or prefixed with ~ for independent theme */
styleLibraryName?: string;
/** Advanced style library configuration */
styleLibrary?: StyleLibraryConfig;
}
interface StyleLibraryConfig {
/** Style library name (same as styleLibraryName) */
name: string;
/** Include base.css file (default: true) */
base?: boolean;
/** Template for style file paths, supports [module] placeholder */
path?: string;
/** Fallback to component's own style if theme style not found */
mixin?: boolean;
/** Root directory for styles */
root?: string;
}Configuration for handling multiple component libraries.
/**
* Multiple library configuration array
* Each array element: [plugin, options, libraryIdentifier]
*/
type MultiLibraryConfig = Array<[Function, PluginOptions, string]>;Usage Example:
{
"plugins": [
["component", {
"libraryName": "antd",
"style": true
}, "antd"],
["component", {
"libraryName": "element-ui",
"style": true
}, "element-ui"]
]
}The plugin handles various import and usage patterns:
Named Imports:
// Input
import { Button, Input } from 'antd';
// Output (with style: true, simplified for clarity)
require("antd/lib/button/style.css");
var _Button = _interopRequireDefault(require("antd/lib/button")).default;
require("antd/lib/input/style.css");
var _Input = _interopRequireDefault(require("antd/lib/input")).default;Function Calls:
// Input
import { message } from 'element-ui';
message('Hello');
// Output
require("element-ui/lib/message/style.css");
var _message = require("element-ui/lib/message").default;
_message('Hello');Member Expressions:
// Input
import Components from 'antd';
Components.Button;
// Output
var _Components = require("antd/lib").default;
_Components.Button;Array Usage:
// Input
import { Button, Input } from 'antd';
const components = [Button, Input];
// Output
var _Button = require("antd/lib/button").default;
var _Input = require("antd/lib/input").default;
const components = [_Button, _Input];Different approaches for handling CSS and style imports:
Basic Style Import:
// Configuration
{ "style": true }
// Transforms to
require("library/lib/component/style.css");Custom Style Path:
// Configuration
{ "style": "custom.css" }
// Transforms to
require("library/lib/component/custom.css");Style Library with Theme:
// Configuration
{
"styleLibraryName": "theme-default"
}
// Transforms to
require("library/lib/theme-default/component.css");Independent Theme Package:
// Configuration
{
"styleLibraryName": "~my-theme"
}
// Transforms to (resolved to current working directory)
require("/path/to/cwd/my-theme/component.css");Advanced Style Library:
// Configuration
{
"styleLibrary": {
"name": "theme-custom",
"base": true,
"path": "[module]/index.css",
"mixin": true
}
}
// Base style import
require("library/lib/theme-custom/base.css");
// Component style import
require("library/lib/theme-custom/button/index.css");The plugin supports various component library directory structures:
Standard Structure:
- lib/
- component-name/
- index.js
- style.cssTheme Structure:
- lib/
- theme-default/
- base.css
- index.css
- component-name.css
- component-name/
- index.jsCustom Path Structure:
- lib/
- theme-custom/
- component-name/
- index.css
- component-name/
- index.jsComponent name transformation options:
/**
* Converts component name based on camel2Dash setting
* @param {string} str - Component name to transform
* @param {boolean} camel2Dash - Whether to convert camelCase to dash-case
* @returns {string} Transformed name
*/
function parseName(str, camel2Dash);Examples:
// camel2Dash: true (default)
'DatePicker' → 'date-picker'
'Button' → 'button'
// camel2Dash: false
'DatePicker' → 'DatePicker'
'Button' → 'Button'The plugin includes error handling for common configuration issues:
Import Conflict Error:
// Throws error when mixing import-all and on-demand imports
throw Error('[babel-plugin-component] If you are using both on-demand and importing all, make sure to invoke the importing all first.');File Existence Checking:
/**
* Checks if style file exists when using mixin option
* Falls back to component's own style if theme style not found
*/
const isExist = require('fs').existsSync;The plugin implements the following Babel visitor methods to transform different AST node types:
Program:
ImportDeclaration:
CallExpression:
MemberExpression:
AssignmentExpression:
ArrayExpression:
Property:
VariableDeclarator:
LogicalExpression:
ConditionalExpression:
IfStatement:
The plugin relies on the following external dependencies:
/**
* Babel helper for adding module imports
*/
const { addSideEffect, addDefault } = require('@babel/helper-module-imports');
/**
* Node.js built-in modules
*/
const resolve = require('path').resolve;
const isExist = require('fs').existsSync;/**
* Babel plugin function signature
*/
type BabelPlugin = ({ types }: { types: any }) => {
visitor: {
[key: string]: (path: any, state: { opts: PluginOptions }) => void;
};
};
/**
* Internal cache structures
*/
interface Cache {
[libraryName: string]: number; // 1 = import-all, 2 = on-demand
}
interface CachePath {
[libraryName: string]: string; // Resolved style library paths
}
interface ImportAll {
[libraryPath: string]: boolean; // Tracks import-all usage
}