admin管理员组文章数量:1336631
I wrote a utility library and I want to tree-shaking
them when my user publishes
their app.
In Webpack v4, you need to make your module ES6
to support tree-shaking
, but I also want to split my development build
and my production build
into different files.
What I want is exactly like react's NPM module:
// index.js
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
This leads me questions.
If I make my utility modules all monjs
, I will never get tree-shaking
, my app gets so huge.
If I make my utility modules all ES6 static export
, I will have to include development message
in production code
.
And publishing two modules (eg: my-utility
and my-utility-es
) will not helping, because in development, my code looks like this:
import { someFunc } from 'my-utility';
but in production code, I will have to change it to this:
import { someFunc } from 'my-utility-es';
How can I solve this problem?
Update
To be more clear, my development build
and production build
contains different source code (eg: production build has stripped all error message).
So specify webpack mode isn't satisfying for me.
I wrote a utility library and I want to tree-shaking
them when my user publishes
their app.
In Webpack v4, you need to make your module ES6
to support tree-shaking
, but I also want to split my development build
and my production build
into different files.
What I want is exactly like react's NPM module:
// index.js
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
This leads me questions.
If I make my utility modules all monjs
, I will never get tree-shaking
, my app gets so huge.
If I make my utility modules all ES6 static export
, I will have to include development message
in production code
.
And publishing two modules (eg: my-utility
and my-utility-es
) will not helping, because in development, my code looks like this:
import { someFunc } from 'my-utility';
but in production code, I will have to change it to this:
import { someFunc } from 'my-utility-es';
How can I solve this problem?
Update
To be more clear, my development build
and production build
contains different source code (eg: production build has stripped all error message).
So specify webpack mode isn't satisfying for me.
Share Improve this question edited Mar 26, 2019 at 10:12 Joseph asked Mar 19, 2019 at 21:18 JosephJoseph 4,7258 gold badges40 silver badges80 bronze badges 1- You cannot get tree-shaking using es5/monJS module.exports syntax. But as shared below, you can use webpack --mode to remove the "development" portions of the code. – adamrights Commented Mar 26, 2019 at 20:35
3 Answers
Reset to default 4Here's the best solution I've found, without requiring the user to use Babel macros...
Let's say:
- Our library is called
crazy-ponents
- It exports two React ponents called
ComponentA
andComponentB
- The ponents use code fencing to do some logging in dev mode only
// src/index.js
import React from 'react';
export function ComponentA(props) {
if (process.env.NODE_ENV !== 'production') {
console.log(`Rendering ComponentA with props ${props}`);
}
return <div>ComponentA message: {props.msg}</div>;
}
export function ComponentB(props) {
if (process.env.NODE_ENV !== 'production') {
console.log(`Rendering ComponentB with props ${props}`);
}
return <div>ComponentB message: {props.msg}</div>;
}
We want the the library to:
Be tree-shakable, so if user does
import { ComponentA } from 'crazy-ponents'
, the code forComponentB
does not end up in their bundle.The logging code is stripped from production bundles.
Solution
1. Bundle library with Rollup
2. Configure Rollup to create both CJS and ESM builds in both production and dev versions (production versions with logging code stripped etc).
CJS builds are output to /dist/cjs
, ESM builds to /dist/esm
. Files are called crazy-ponents.prod.min.js
and crazy-ponents.dev.js
.
Only the dev builds contains the logging code (not explaining how to do all this, if you're reading this, you probably already know).
3. Create entry points for CJS and ESM as follows:
// index.js
if (process.env.NODE_ENV === 'production') {
module.exports = require('./dist/cjs/crazy-ponents.min.js');
} else {
module.exports = require('./dist/cjs/crazy-ponents.js');
}
// es/index.js
import {
ComponentA as ComponentA_prod,
ComponentB as ComponentA_prod
} from '../dist/esm/crazy-ponents.prod.min.js';
import {
ComponentA as ComponentA_dev,
ComponentB as ComponentA_dev
} from '../dist/esm/crazy-ponents.dev.js';
export const ComponentA = process.env.NODE_ENV === 'production' ? ComponentA_prod : ComponentA_dev;
export const ComponentB = process.env.NODE_ENV === 'production' ? ComponentB_prod : ComponentB_dev;
4. Point to both entry points in package.json
:
// package.json
{
"name": "crazy-ponents",
"version": "1.0.0",
"main": "index.js",
"module": "es/index.js",
"sideEffects": false
}
5. (optional) Make importable as ESM in NodeJS
Node 12 (with flag) and Node 13+ support ES modules natively.
Add to package.json
:
"exports": {
".": {
"import": "./es/index.js",
"require": "./index.js"
},
"./es": "./es/index.js"
},
Add extra package.json
file in es
folder to flag contents of the folder as ESM to NodeJS:
// es/package.json
{
"type": "module"
}
Use rollup-plugin-copy to get Rollup to also copy this file into dist/esm
:
// rollup.config.js
import copy from 'rollup-plugin-copy';
/* ... other imports ... */
export default {
input: 'src/index.js',
/* ... other config ... */
plugins: [
/* ... other plugins ... */
copy({targets: [{src: 'es/package.json', dest: 'dist/esm'}]})
]
};
Possible improvements
es/index.js
is created by hand, so if you later add ComponentC
, it also needs to be added to es/index.js
. It'd be ideal if there was a Rollup plugin to automate creation of es/index.js
, but I haven't found one.
Also, your mileage may vary. I've only just been trying this out today. It seems to work as you'd expect when the library is imported in a create-react-app app, but I've not tested it with hand-coded Webpack configs.
This approach should be generalisable to any library, not just React ponents, but I've not tried.
Any suggestions for improvements very wele!
I've found out the answer by myself, I think the best way to do this is through babel macros
:
import { something } from 'myLibrary/macro';
// In webpack development:
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
// import { something } from 'myLibrary/development';
// In webpack production:
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
// import { something } from 'myLibrary/production';
My macro implementation:
import { createMacro } from 'babel-plugin-macros';
function macro({ references, state, babel }) {
state.file.path.node.body.forEach(node => {
if (node.type === 'ImportDeclaration') {
if (node.source.value.includes('myLibrary/macro')) {
if (process.env.NODE_ENV === 'production') {
node.source.value = 'myLibrary/module/production';
} else {
node.source.value = 'myLibrary/module/development';
}
}
}
});
return { keepImports: true };
}
export default createMacro(macro);
All you need to solve this problem is to use mode
. See Specify the Mode.
Since webpack v4, specifying mode automatically configures DefinePlugin for you:
webpack.prod.js
const merge = require('webpack-merge');
const mon = require('./webpack.mon.js');
module.exports = merge(mon, {
mode: 'production',
});
They mention React by name:
If you're using a library like react, you should actually see a significant drop in bundle size after adding this plugin.
本文标签: javascriptPossibility about conditional export ES6 module based on processenvNODEENVStack Overflow
版权声明:本文标题:javascript - Possibility about conditional export ES6 module based on process.env.NODE_ENV? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742407951a2469206.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论