Babel plugin that fixes CommonJS default export behavior by adding module.exports = exports.default when only a default export exists
npx @tessl/cli install tessl/npm-babel-plugin-add-module-exports@1.0.0A Babel plugin that fixes CommonJS default export behavior by adding module.exports = exports.default when only a default export exists. This plugin restores the Babel 5 behavior, eliminating the need to use .default when requiring modules in Node.js.
npm install babel-plugin-add-module-exports --save-devThis is a Babel plugin, not a runtime library. It's used in Babel configuration files:
{
"plugins": ["add-module-exports"]
}Add the plugin to your Babel configuration to automatically fix CommonJS default exports:
{
"presets": ["@babel/preset-env"],
"plugins": ["add-module-exports"]
}Input code:
export default 'foo';Without plugin output:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = 'foo';With plugin output:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = 'foo';
module.exports = exports.default;Result: require('./bundle.js') returns 'foo' instead of { default: 'foo' }
The plugin operates through Babel's plugin lifecycle system with several key architectural components:
ExportsFinder class analyzes the entire module structure to detect export patternsmodule.exports statements after module transformation is completevisitor.Program method captures plugin options from the transformation statepost hook traverses the transformed AST to find exports.default patternsExportsFinder determines if transformation conditions are metmodule.exports statementsThis architecture ensures the plugin works correctly with Babel's module transformation pipeline while avoiding conflicts with other module formats and transformation steps.
The main export is a Babel plugin factory function that creates a plugin configuration.
/**
* Babel plugin factory function
* @param {Object} babel - Babel instance with template helper
* @param {Function} babel.template - Template helper for AST generation
* @returns {BabelPluginConfig} Plugin configuration object
*/
function babelPluginAddModuleExports({ template }): BabelPluginConfig;
interface BabelPluginConfig {
/** AST visitor configuration */
visitor: {
/** Captures plugin options from transformation state */
Program(path: any, state: { opts: PluginOptions }): void;
};
/** Post-transformation hook that adds module.exports statements */
post(fileMap: { path: any }): void;
}Configuration options that can be passed to the plugin.
interface PluginOptions {
/**
* When true, adds both module.exports = exports.default and
* module.exports.default = exports.default for backward compatibility
* @default false
*/
addDefaultProperty?: boolean;
}Usage with options:
{
"presets": ["@babel/preset-env"],
"plugins": [
["add-module-exports", { "addDefaultProperty": true }]
]
}When addDefaultProperty is true, the plugin generates:
module.exports = exports.default;
module.exports.default = exports.default;The plugin applies transformations based on specific conditions:
exports.default is the only exportexport const foo = 'bar') prevents transformationmodule.exports is already assigned// ✅ Transformed - only default export
export default 'value';
export default function() {};
export default { key: 'value' };
// ❌ Not transformed - has named exports
export default 'value';
export const named = 'other';
// ❌ Not transformed - already has module.exports
export default 'value';
module.exports = something;
// ❌ Not transformed - ES modules (modules: false)
export default 'value'; // when using { modules: false }/**
* Supported Babel module transform formats
*/
type SupportedModuleFormats = 'commonjs' | 'umd' | 'cjs';
/**
* Unsupported formats (plugin will skip transformation)
*/
type UnsupportedModuleFormats = 'amd' | 'systemjs' | false;The plugin uses internal analysis to determine when to apply transformations:
The plugin detects these export patterns:
exports.default = valueObject.defineProperty(exports, "default", ...)_exports identifier/**
* AST visitor patterns handled by the plugin
*/
interface VisitorPatterns {
/** Handles Object.defineProperty(exports, "default", ...) calls */
CallExpression: (path: any) => void;
/** Handles exports.default = value assignments */
AssignmentExpression: (path: any) => void;
}The plugin handles edge cases gracefully:
{
"presets": [["@babel/preset-env", { "modules": "commonjs" }]],
"plugins": ["add-module-exports"]
}{
"presets": ["@babel/preset-env", "@babel/preset-typescript"],
"plugins": ["add-module-exports"]
}{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["add-module-exports"]
}/**
* Internal class for analyzing export patterns (not exported)
*/
interface ExportsFinder {
constructor(exportsDefaultPath: any): ExportsFinder;
getRootPath(): any;
isOnlyExportsDefault(): boolean;
findExports(path: any, property?: string): void;
findExportsInCallExpression(path: any): void;
isAmd(): boolean;
}