I have seen in many projects (like React
), they use require()
to conditionally re-export properties like so:
// index.js
if (process.env.NODE_ENV === 'production' {
exports = require('./cjs/react.production.min.js')
} else {
exports = require('./cjs/react.development.js')
}
I am trying to convert this to its equivalent valid ES6
syntax, preserving individual exported members (for tree shaking).
I have tried this and it works, but you cannot import individual members:
import * as prod from './cjs/react.production.min.js'
import * as dev from './cjs/react.development.js'
let exports = {}
if (process.env.NODE_ENV === 'production') {
exports = prod
} else {
exports = dev
}
export default exports
Is there an equivalent for the original using valid ES6
syntax?
I suppose with a smart enough compiler, the assigned keys within the nested modules could be propagated to the top level at build time like this:
import { value_1 as prod_value_1 } from './cjs/react.production.min.js'
import { value_1 as dev_value_1 } from './cjs/react.development.js'
let value_1 = undefined
if (process.env.NODE_ENV === 'production') {
value_1 = prod_value_1
} else {
value_1 = dev_value_1
}
export { value_1 /* value_2, etc */ }
UPDATE: A little less janky but still requires knowing the exported key names:
import * as prod from './cjs/react.production.min.js'
import * as dev from './cjs/react.development.js'
let selected = undefined
if (true) {
selected = prod
} else {
selected = dev
}
export const { value_1, value_2, etc } = selected
Converting the conditional require()
statements in CommonJS
to ES6
syntax while preserving tree shaking capabilities can be a bit challenging. The approach you've described seems to be on the right track.
However, it's important to understand that in ES6
, import
and export
statements are static and can't be used inside conditional blocks like if
statements.
The approach you've described in the latter part of your question, where you import individual members and then conditionally assign them, is one way to handle this.
Here's an expanded and slightly refined version of your approach:
import * as prod from './cjs/react.production.min.js';
import * as dev from './cjs/react.development.js';
let finalExports = {};
const keys = Object.keys(prod);
keys.forEach(key => {
finalExports[key] = process.env.NODE_ENV === 'production' ? prod[key] : dev[key];
});
// Explicitly export each member
export const {
Component1,
Component2,
// ... other exports
} = finalExports;
But this has some downsides:
An alternative approach is to use a build tool like Webpack
or Rollup
to handle environment-specific
bundling. These tools can replace imports
based on the environment during the build process, which allows for more efficient tree shaking and smaller bundle sizes.
Here's an example using Webpack
's DefinePlugin
:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
// ... other webpack config
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
resolve: {
alias: {
'react': process.env.NODE_ENV === 'production'
? './cjs/react.production.min.js'
: './cjs/react.development.js'
}
}
};
In your source code, you would then just import from react
, and Webpack
will handle replacing it with the correct file based on the environment.