Skip to content

Commit 6370250

Browse files
committed
adding options for esmodules and commonjs build targets (facebook#5216)
1 parent 0793d48 commit 6370250

File tree

6 files changed

+177
-31
lines changed

6 files changed

+177
-31
lines changed

packages/babel-preset-react-app/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,47 @@ Make sure you have a `tsconfig.json` file at the root directory. You can also us
5151
"presets": [["react-app", { "flow": false, "typescript": true }]]
5252
}
5353
```
54+
55+
## Usage within NPM packages
56+
57+
If you are creating an NPM package that contains a React component you can use the options for `commonjs` and `esmodules` to create proper builds for `lib`, `es` and `dist` folders. The configuration example below will work for most common cases but will not be suitable to all projects. Similar setups are used by popular NPM packages such as [react-redux](https://github.com/reduxjs/react-redux) and [react-router](https://github.com/ReactTraining/react-router/tree/master/packages/react-router).
58+
59+
### `babel.config.js`
60+
61+
When building for `lib`, `es` folders you want to set the `absoluteRuntime` to false. When building for the `dist` folder, you also want to disable helpers (because Rollup manages helpers automatically).
62+
63+
Note that it is recommended to set `NODE_ENV` environment variable to "production" when building an NPM package. Setting `NODE_ENV` to "development" will put the `@babel/preset-react` plugin into development mode, which is undesirable for a published NPM package.
64+
65+
```js
66+
const { NODE_ENV, MODULES_ENV } = process.env;
67+
68+
const isEnvTest = NODE_ENV === 'test';
69+
if (!isEnvTest) {
70+
// force production mode for package builds
71+
process.env.NODE_ENV = 'production';
72+
}
73+
74+
const useCommonJS = isEnvTest || MODULES_ENV === 'commonjs';
75+
const useESModules = MODULES_ENV === 'esmodules';
76+
77+
module.exports = {
78+
presets: [
79+
// for testing with jest/jsdom
80+
useCommonJS && isEnvTest && 'babel-preset-react-app/test',
81+
// building for lib folder
82+
useCommonJS &&
83+
!isEnvTest && [
84+
'babel-preset-react-app/commonjs',
85+
{ absoluteRuntime: false },
86+
],
87+
// building for es folder
88+
useESModules && [
89+
'babel-preset-react-app/esmodules',
90+
{ absoluteRuntime: false },
91+
],
92+
// building for dist folder
93+
!useCommonJS &&
94+
!useESModules && ['babel-preset-react-app', { helpers: false }],
95+
].filter(Boolean),
96+
};
97+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
'use strict';
8+
9+
const create = require('./create');
10+
11+
module.exports = function(api, opts) {
12+
// This is similar to how `env` works in Babel:
13+
// https://babeljs.io/docs/usage/babelrc/#env-option
14+
// We are not using `env` because it’s ignored in versions > [email protected]:
15+
// https://github.com/babel/babel/issues/4539
16+
// https://github.com/facebook/create-react-app/issues/720
17+
// It’s also nice that we can enforce `NODE_ENV` being specified.
18+
const env = process.env.BABEL_ENV || process.env.NODE_ENV;
19+
return create(
20+
api,
21+
Object.assign({ helpers: false }, opts, { useCommonJS: true }),
22+
env
23+
);
24+
};

packages/babel-preset-react-app/create.js

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ module.exports = function(api, opts, env) {
2929
var isEnvProduction = env === 'production';
3030
var isEnvTest = env === 'test';
3131

32+
var useCommonJS = validateBoolOption(
33+
'useCommonJS',
34+
opts.useCommonJS,
35+
isEnvTest
36+
);
3237
var useESModules = validateBoolOption(
3338
'useESModules',
3439
opts.useESModules,
35-
isEnvDevelopment || isEnvProduction
40+
false
3641
);
3742
var isFlowEnabled = validateBoolOption('flow', opts.flow, true);
3843
var isTypeScriptEnabled = validateBoolOption(
@@ -54,6 +59,24 @@ module.exports = function(api, opts, env) {
5459
);
5560
}
5661

62+
// When building for commonjs or esmodules environments we need to choose
63+
// different targets.
64+
let targets;
65+
if (useCommonJS) {
66+
// For commonjs we target the oldest supported version of node.
67+
// For tests we target the installed version of node.
68+
// https://babeljs.io/docs/en/babel-preset-env#targetsnode
69+
targets = { node: isEnvTest ? 'current' : 6 };
70+
} else if (useESModules) {
71+
// For esmodules we use the special esmodules target.
72+
// https://babeljs.io/docs/en/babel-preset-env#targetsesmodules
73+
targets = { esmodules: true };
74+
} else {
75+
// We want Create React App to be IE 9 compatible until React itself
76+
// no longer works with IE 9
77+
targets = { ie: 9 };
78+
}
79+
5780
if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) {
5881
throw new Error(
5982
'Using `babel-preset-react-app` requires that you specify `NODE_ENV` or ' +
@@ -63,35 +86,34 @@ module.exports = function(api, opts, env) {
6386
'.'
6487
);
6588
}
89+
if (useESModules && useCommonJS) {
90+
throw new Error(
91+
'`babel-preset-react-app` does not support setting both useESModules and useCommonJS to true.'
92+
);
93+
}
6694

6795
return {
6896
presets: [
6997
isEnvTest && [
7098
// ES features necessary for user's Node version
7199
require('@babel/preset-env').default,
72100
{
73-
targets: {
74-
node: 'current',
75-
},
101+
targets,
76102
},
77103
],
78104
(isEnvProduction || isEnvDevelopment) && [
79105
// Latest stable ECMAScript features
80106
require('@babel/preset-env').default,
81107
{
82-
// We want Create React App to be IE 9 compatible until React itself
83-
// no longer works with IE 9
84-
targets: {
85-
ie: 9,
86-
},
108+
targets,
87109
// Users cannot override this behavior because this Babel
88110
// configuration is highly tuned for ES5 support
89111
ignoreBrowserslistConfig: true,
90112
// If users import all core-js they're probably not concerned with
91113
// bundle size. We shouldn't rely on magic to try and shrink it.
92114
useBuiltIns: false,
93-
// Do not transform modules to CJS
94-
modules: false,
115+
// Do not transform modules to CJS (unless we're targeting commonJS)
116+
modules: useCommonJS ? 'cjs' : false,
95117
// Exclude transforms that make all code slower
96118
exclude: ['transform-typeof-symbol'],
97119
},
@@ -157,11 +179,12 @@ module.exports = function(api, opts, env) {
157179
{
158180
corejs: false,
159181
helpers: areHelpersEnabled,
160-
regenerator: true,
182+
// We only need to use regenerator in environments that don't support generators.
183+
regenerator: !(useESModules || useCommonJS),
161184
// https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
162-
// We should turn this on once the lowest version of Node LTS
163-
// supports ES Modules.
164-
useESModules,
185+
// We want to enable this for all builds except commonjs. This allows for smaller
186+
// builds since it doesn't need to preserve commonjs semantics.
187+
useESModules: !useCommonJS,
165188
// Undocumented option that lets us encapsulate our runtime, ensuring
166189
// the correct version is used
167190
// https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42
@@ -177,7 +200,7 @@ module.exports = function(api, opts, env) {
177200
],
178201
// Adds syntax support for import()
179202
require('@babel/plugin-syntax-dynamic-import').default,
180-
isEnvTest &&
203+
useCommonJS &&
181204
// Transform dynamic import to require
182205
require('babel-plugin-dynamic-import-node'),
183206
].filter(Boolean),

packages/babel-preset-react-app/dependencies.js

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ module.exports = function(api, opts) {
3636
var isEnvProduction = env === 'production';
3737
var isEnvTest = env === 'test';
3838

39+
var useCommonJS = validateBoolOption(
40+
'useCommonJS',
41+
opts.useCommonJS,
42+
isEnvTest
43+
);
44+
var useESModules = validateBoolOption(
45+
'useESModules',
46+
opts.useESModules,
47+
false
48+
);
3949
var areHelpersEnabled = validateBoolOption('helpers', opts.helpers, false);
4050
var useAbsoluteRuntime = validateBoolOption(
4151
'absoluteRuntime',
@@ -50,6 +60,24 @@ module.exports = function(api, opts) {
5060
);
5161
}
5262

63+
// When building for commonjs or esmodules environments we need to choose
64+
// different targets.
65+
let targets;
66+
if (useCommonJS) {
67+
// For commonjs we target the oldest supported version of node.
68+
// For tests we target the installed version of node.
69+
// https://babeljs.io/docs/en/babel-preset-env#targetsnode
70+
targets = { node: isEnvTest ? 'current' : 6 };
71+
} else if (useESModules) {
72+
// For esmodules we use the special esmodules target.
73+
// https://babeljs.io/docs/en/babel-preset-env#targetsesmodules
74+
targets = { esmodules: true };
75+
} else {
76+
// We want Create React App to be IE 9 compatible until React itself
77+
// no longer works with IE 9
78+
targets = { ie: 9 };
79+
}
80+
5381
if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) {
5482
throw new Error(
5583
'Using `babel-preset-react-app` requires that you specify `NODE_ENV` or ' +
@@ -59,6 +87,11 @@ module.exports = function(api, opts) {
5987
'.'
6088
);
6189
}
90+
if (useESModules && useCommonJS) {
91+
throw new Error(
92+
'`babel-preset-react-app` does not support setting both useESModules and useCommonJS to true.'
93+
);
94+
}
6295

6396
return {
6497
// Babel assumes ES Modules, which isn't safe until CommonJS
@@ -71,9 +104,7 @@ module.exports = function(api, opts) {
71104
// ES features necessary for user's Node version
72105
require('@babel/preset-env').default,
73106
{
74-
targets: {
75-
node: 'current',
76-
},
107+
targets,
77108
// Do not transform modules to CJS
78109
modules: false,
79110
// Exclude transforms that make all code slower
@@ -84,19 +115,15 @@ module.exports = function(api, opts) {
84115
// Latest stable ECMAScript features
85116
require('@babel/preset-env').default,
86117
{
87-
// We want Create React App to be IE 9 compatible until React itself
88-
// no longer works with IE 9
89-
targets: {
90-
ie: 9,
91-
},
118+
targets,
92119
// Users cannot override this behavior because this Babel
93120
// configuration is highly tuned for ES5 support
94121
ignoreBrowserslistConfig: true,
95122
// If users import all core-js they're probably not concerned with
96123
// bundle size. We shouldn't rely on magic to try and shrink it.
97124
useBuiltIns: false,
98-
// Do not transform modules to CJS
99-
modules: false,
125+
// Do not transform modules to CJS (unless we're targeting commonJS)
126+
modules: useCommonJS ? 'cjs' : false,
100127
// Exclude transforms that make all code slower
101128
exclude: ['transform-typeof-symbol'],
102129
},
@@ -110,11 +137,12 @@ module.exports = function(api, opts) {
110137
{
111138
corejs: false,
112139
helpers: areHelpersEnabled,
113-
regenerator: true,
140+
// We only need to use regenerator in environments that don't support generators.
141+
regenerator: !(useESModules || useCommonJS),
114142
// https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
115-
// We should turn this on once the lowest version of Node LTS
116-
// supports ES Modules.
117-
useESModules: isEnvDevelopment || isEnvProduction,
143+
// We want to enable this for all builds except commonjs. This allows for smaller
144+
// builds since it doesn't need to preserve commonjs semantics.
145+
useESModules: !useCommonJS,
118146
// Undocumented option that lets us encapsulate our runtime, ensuring
119147
// the correct version is used
120148
// https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42
@@ -123,7 +151,7 @@ module.exports = function(api, opts) {
123151
],
124152
// Adds syntax support for import()
125153
require('@babel/plugin-syntax-dynamic-import').default,
126-
isEnvTest &&
154+
useCommonJS &&
127155
// Transform dynamic import to require
128156
require('babel-plugin-transform-dynamic-import').default,
129157
].filter(Boolean),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
'use strict';
8+
9+
const create = require('./create');
10+
11+
module.exports = function(api, opts) {
12+
// This is similar to how `env` works in Babel:
13+
// https://babeljs.io/docs/usage/babelrc/#env-option
14+
// We are not using `env` because it’s ignored in versions > [email protected]:
15+
// https://github.com/babel/babel/issues/4539
16+
// https://github.com/facebook/create-react-app/issues/720
17+
// It’s also nice that we can enforce `NODE_ENV` being specified.
18+
const env = process.env.BABEL_ENV || process.env.NODE_ENV;
19+
return create(
20+
api,
21+
Object.assign({ helpers: false }, opts, { useESModules: true }),
22+
env
23+
);
24+
};

packages/babel-preset-react-app/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
"url": "https://github.com/facebook/create-react-app/issues"
99
},
1010
"files": [
11+
"commonjs.js",
1112
"create.js",
1213
"dependencies.js",
1314
"dev.js",
15+
"esmodules.js",
1416
"index.js",
1517
"webpack-overrides.js",
1618
"prod.js",
@@ -35,6 +37,7 @@
3537
"babel-loader": "8.0.5",
3638
"babel-plugin-dynamic-import-node": "2.2.0",
3739
"babel-plugin-macros": "2.5.0",
40+
"babel-plugin-transform-dynamic-import": "2.1.0",
3841
"babel-plugin-transform-react-remove-prop-types": "0.4.24"
3942
}
4043
}

0 commit comments

Comments
 (0)