Transform plugins for Metro bundler that provide code optimization and platform-specific transformations for React Native applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Inlines top-level require() calls to enable lazy loading and reduce bundle overhead by replacing variable assignments with direct require() calls at usage sites.
Transforms top-level require() assignments into inline require() calls for improved lazy loading and bundle performance.
/**
* Creates a Babel plugin that inlines require() calls
* @returns Babel plugin object that processes require() assignments
*/
function inlineRequiresPlugin(): PluginObj<State>;
interface InlineRequiresPluginOptions {
/** Array of require calls to ignore (e.g., ['fs', 'path']) */
ignoredRequires?: ReadonlyArray<string>;
/** Function names that can be inlined (default: ['require']) */
inlineableCalls?: ReadonlyArray<string>;
/** Modules that should not be memoized even when memoizeCalls is true */
nonMemoizedModules?: ReadonlyArray<string>;
/** Whether to memoize repeated require() calls (default: false) */
memoizeCalls?: boolean;
}
interface State {
opts?: InlineRequiresPluginOptions;
ignoredRequires: Set<string>;
inlineableCalls: Set<string>;
membersAssigned: Map<string, Set<string>>;
}Usage Examples:
const babel = require("@babel/core");
const { inlineRequiresPlugin } = require("metro-transform-plugins");
const code = `
var fs = require('fs');
var path = require('path');
var lodash = require('lodash');
function readConfig() {
return fs.readFileSync('./config.json', 'utf8');
}
function processPath(inputPath) {
return path.join(inputPath, 'processed');
}
console.log(lodash.map([1, 2, 3], x => x * 2));
`;
const transformed = babel.transformSync(code, {
plugins: [
[inlineRequiresPlugin, {
ignoredRequires: ['fs'], // Don't inline fs module
memoizeCalls: true
}]
]
});
// Result:
// var fs = require('fs'); // Not inlined (in ignoredRequires)
// var _path;
// var _lodash;
//
// function readConfig() {
// return fs.readFileSync('./config.json', 'utf8');
// }
//
// function processPath(inputPath) {
// return (_path || (_path = require('path'))).join(inputPath, 'processed');
// }
//
// console.log((_lodash || (_lodash = require('lodash'))).map([1, 2, 3], x => x * 2));Handles basic require() variable assignments:
/**
* Simple require assignment inlining
* var module = require('module-name');
* module.method() → require('module-name').method()
*/
// Before transformation
var React = require('react');
var Component = React.Component;
function render() {
return React.createElement('div');
}
// After transformation
var Component = require('react').Component;
function render() {
return require('react').createElement('div');
}Inlines require() calls with property access:
/**
* Member expression require inlining
* var member = require('module').property;
* member.method() → require('module').property.method()
*/
// Before transformation
var createElement = require('react').createElement;
var Fragment = require('react').Fragment;
function component() {
return createElement(Fragment, null, 'Hello');
}
// After transformation
function component() {
return require('react').createElement(require('react').Fragment, null, 'Hello');
}Handles ES6 destructuring patterns:
/**
* Destructuring assignment inlining
* const {method} = require('module');
* method() → require('module').method()
*/
// Before transformation
const { useState, useEffect } = require('react');
const { join, resolve } = require('path');
function component() {
const [state, setState] = useState(0);
const configPath = join(__dirname, 'config.json');
return state;
}
// After transformation
function component() {
const [state, setState] = require('react').useState(0);
const configPath = require('path').join(__dirname, 'config.json');
return state;
}Optional memoization to avoid repeated module loading:
/**
* Memoized require pattern (when memoizeCalls: true)
* Creates memoization variables for frequently used modules
*/
// Before transformation (with memoizeCalls: true)
var utils = require('./utils');
function a() { return utils.helper1(); }
function b() { return utils.helper2(); }
function c() { return utils.helper3(); }
// After transformation
var _utils;
function a() { return (_utils || (_utils = require('./utils'))).helper1(); }
function b() { return (_utils || (_utils = require('./utils'))).helper2(); }
function c() { return (_utils || (_utils = require('./utils'))).helper3(); }Handles require.resolve() patterns for module resolution:
/**
* require.resolve() call inlining
* var module = require(require.resolve('module-name'));
*/
// Before transformation
var config = require(require.resolve('./config'));
config.setting = 'value';
// After transformation
require(require.resolve('./config')).setting = 'value';Implements safety mechanisms to prevent breaking transformations:
/**
* Safety exclusions prevent incorrect inlining
*/
// Excluded: Modules with member assignments
var utils = require('./utils');
utils.cache = {}; // Member assignment detected - won't inline utils
// Excluded: Non-constant violations
var fs = require('fs');
fs = mockFs; // Reassignment - won't inline fs
// Excluded: Babel runtime modules (automatically)
var helper = require('@babel/runtime/helpers/something'); // Never inlined
// Excluded: Modules in ignoredRequires option
var path = require('path'); // Won't inline if 'path' in ignoredRequiresOptimizes memoization based on code block structure:
/**
* Block-level memoization optimization
* Uses different patterns based on code structure
*/
// Within same block - uses memoization variable
function sameBlock() {
const a = (_utils || (_utils = require('./utils'))).methodA();
const b = _utils.methodB(); // Reuses memoized variable
}
// Across different blocks - uses full memoization expression
function differentBlocks() {
if (condition) {
return (_utils || (_utils = require('./utils'))).methodA();
} else {
return (_utils || (_utils = require('./utils'))).methodB();
}
}Maintains original source locations for debugging and error reporting:
/**
* Source location tracking
* Preserves original require() call location information
*/
// Adds METRO_INLINE_REQUIRES_INIT_LOC metadata to inlined expressions
// This helps with:
// - Source map accuracy
// - Error stack trace precision
// - Debugging experienceSupports custom function names beyond 'require':
/**
* Custom inlineable function support
* @param inlineableCalls - Array of function names to inline
*/
// Configuration: inlineableCalls: ['require', 'load', 'import']
var module1 = require('module1'); // Inlined
var module2 = load('module2'); // Inlined
var module3 = import('module3'); // Inlined
var module4 = customLoader('module4'); // Not inlinedPrevents memoization for specific modules that should always be fresh:
/**
* Non-memoized module handling
* @param nonMemoizedModules - Modules to never memoize
*/
// Configuration: nonMemoizedModules: ['./config', 'dotenv']
var config = require('./config'); // Always fresh, never memoized
var dotenv = require('dotenv'); // Always fresh, never memoized
var utils = require('./utils'); // Can be memoized (if memoizeCalls: true)Graceful handling of transformation failures:
/**
* Error recovery mechanism
* If replacement fails, keeps original require() assignment
*/
// If inlining fails (e.g., due to complex AST structure):
// - Logs the error internally
// - Preserves original require() assignment
// - Continues processing other requires
// - Prevents build failures from transformation issues