Skip to content
This repository was archived by the owner on Feb 15, 2025. It is now read-only.

Commit 524a642

Browse files
committed
Add targets config for preset-env
Add preset-env config to support polyfilling with core-js@3 Add Babel plugins (temporarily) for nullish coalescing and optional chaining, as Webpack can't currently handle these untranspiled Add BrowserSupport docs Closes #550
1 parent d028177 commit 524a642

13 files changed

+271
-18
lines changed

CHANGES.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,43 @@
33
## Breaking Changes
44

55
- Node.js 8 is no longer supported; Node.js 10.13.0 is now the minimum required version, as per many of nwb's dependencies.
6-
- Removed support for deprecated `babel.stage` and `webpack.uglify` config.
6+
7+
**Browser Support**
8+
79
- Removed default polyfills for `Promise`, `fetch()` and `Object.assign()` and deprecated `polyfill` config.
8-
- If you need to support older browsers, you will now need to provide the necessary polyfills yourself in your app.
9-
- The [react-app-polyfill](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill#react-app-polyfill) module provides polyfills for IE 9-11, and for stable language features - its instructions also work for apps using nwb.
10-
- Deprecated using a string for [`webpack.autoprefixer` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#autoprefixer-object) to configure supported browsers - this will no longer do anything and should be moved to the new [`browsers` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object).
10+
11+
If you need to support older browsers, you will now need to include the necessary polyfills in your app - see the new [Browser Support docs](https://github.com/insin/nwb/blob/master/docs/BrowserSupport.md#browser-support) for details on polyfilling and suggested modules it's to provide them.
12+
13+
If this change affects your app, a quick fix is to use [react-app-polyfill](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill)'s IE11 polyfill, which is equivalent to what nwb's default polyfill used to be:
14+
15+
```js
16+
import 'react-app-polyfill/ie11'
17+
```
18+
19+
- For apps and quick commands, `@babel/preset-env` is now configured to [only transpile the necessary ECMAScript 2015+ for supported browsers](https://github.com/insin/nwb/blob/master/docs/BrowserSupport.md#default-browser-support).
20+
21+
When running a development server, this defaults to the most recent version of Chrome, Firefox or Safari, so you _may_ need to adjust [`browsers.development` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object) if you're using an out of date browser and you **will** need to adjust it if you're developing with an older browser supported by your app.
22+
1123
- Default browser configuration for Autoprefixer when building an app has changed from [`>1%, last 4 versions, Firefox ESR, not ie < 9`](https://browserl.ist/?q=%3E1%25%2C+last+4+versions%2C+Firefox+ESR%2C+not+ie+%3C+9) to [`>0.2%, not dead, not op_mini all`](https://browserl.ist/?q=%3E0.2%25%2C+not+dead%2C+not+op_mini+all).
12-
- copy-webpack-plugin v6.0.0 [has breaking changes to its options](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md#600-2020-05-15) which you should read if you've configured the [`webpack.copy` option](https://github.com/insin/nwb/blob/master/docs/Configuration.md#copy-array--object).
24+
25+
When running a development server, the default browser configuration has changed to [`last 1 chrome version, last 1 firefox version, last 1 safari version`](https://browserl.ist/?q=last+1+chrome+version%2C+last+1+firefox+version%2C+last+1+safari+version).
26+
27+
**Configuration**
28+
29+
- Deprecated using a string for [`webpack.autoprefixer` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#autoprefixer-object) to configure supported browsers - this will no longer do anything and should be moved to the new [`browsers` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object).
30+
- Removed support for `babel.stage` and `webpack.uglify` config deprecated in nwb v0.24.0.
31+
- copy-webpack-plugin v6.0.0 [has breaking changes to its options](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md#600-2020-05-15) which you should read if you're using [`webpack.copy` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#copy-array--object).
1332

1433
In particular, the `ignore` option in a copy pattern must now be put inside the new `globOptions` option.
15-
- file-loader v6.0.0 [changed its default hashing algorithm](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md#600-2020-03-17).
1634

17-
## Added
35+
**Dependencies**
1836

19-
- Added top-level [`browsers` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object) to configure supported browsers. This supports using separate browserslist configuration for development and production.
37+
- file-loader v6.0.0 [changed its default hashing algorithm](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md#600-2020-03-17) so hashes in output filenames will change after updating to this release, even if their contents haven't changed.
2038

21-
## Changed
39+
## Added
2240

23-
- Default browser configuration for Autoprefixer when running the development server for an app has changed from [`>1%, last 4 versions, Firefox ESR, not ie < 9`](https://browserl.ist/?q=%3E1%25%2C+last+4+versions%2C+Firefox+ESR%2C+not+ie+%3C+9) to [`last 1 chrome version, last 1 firefox version, last 1 safari version`](https://browserl.ist/?q=last+1+chrome+version%2C+last+1+firefox+version%2C+last+1+safari+version).
41+
- Added top-level [`browsers` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object) to configure supported browsers. This supports using separate browserslist queries for development and production.
42+
- Added [Browser Support docs](https://github.com/insin/nwb/blob/master/docs/BrowserSupport.md#browser-support), with a section on [polyfilling missing language features](https://github.com/insin/nwb/blob/master/docs/BrowserSupport.md#polyfilling-missing-language-features).
2443

2544
## Dependencies
2645

docs/BrowserSupport.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
## Browser Support
2+
3+
nwb's default configuration supports modern browsers. Support for Internet Explorer 9, 10 and 11 requires [polyfills](#supporting-internet-explorer).
4+
5+
### Default Browser Support
6+
7+
nwb uses the following [Browserslist](https://github.com/browserslist/browserslist#browserslist-) queries by default:
8+
9+
- [`last 1 chrome version, last 1 firefox version, last 1 safari version`](https://browserl.ist/?q=last+1+chrome+version%2C+last+1+firefox+version%2C+last+1+safari+version) for development, when running the development server with `nwb serve` (or quick commands such as `npm react run`)
10+
- [`>0.2%, not dead, not op_mini all`](https://browserl.ist/?q=%3E0.2%25%2C+not+dead%2C+not+op_mini+all) for production, when creating a build with `nwb build` (or quick commands such as `npm react build`)
11+
12+
> Use the links above to check which browsers and versions these queries currently resolve to.
13+
14+
These are used to configure:
15+
16+
- [`@babel/preset-env`'s `targets` option](https://babeljs.io/docs/en/babel-preset-env#targets), so it only transpiles the necessary ECMAScript 2015+ for supported browsers.
17+
- [Autoprefixer's `overrideBrowserslist` option](https://github.com/postcss/autoprefixer#options), so it only includes the necessary CSS prefixes for supported browsers.
18+
19+
### Configuring Browser Support
20+
21+
If your app needs to support more (or fewer!) browsers, you can tweak browser support settings using [`browsers` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object).
22+
23+
Broadening the range of supported browsers will ensure your app works for everone who needs to use it, while narrowing the range may help decrease your bundle sizes, if less code needs to be transpiled and fewer Babel helpers need to be imported.
24+
25+
For example, IE9 is considered a "dead" browser in Browserslist queries, so if you needed to support it, you could specifically enable it in [`browsers.production` config](https://github.com/insin/nwb/blob/master/docs/Configuration.md#browsers-string--arraystring--object) like so:
26+
27+
```js
28+
module.exports = {
29+
browsers {
30+
production: '>0.2%, not dead, not op_mini all, ie 9'
31+
}
32+
}
33+
```
34+
35+
You can see that [IE 9 has now been added](https://browserl.ist/?q=%3E0.2%25%2C+not+dead%2C+not+op_mini+all%2C+ie+9) to the list of supported browsers.
36+
37+
## Polyfilling Missing Language Features
38+
39+
### Supporting Internet Explorer
40+
41+
[react-app-polyfill](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill#react-app-polyfill) provides convenient collection of polyfills for IE9 and IE11.
42+
43+
If you need to support Internet Explorer, install react-app-polyfill and import the appropriate polyfill entry point as the first thing in your app's entry point (usually `src/index.js`):
44+
45+
```
46+
npm install react-app-polyfill
47+
```
48+
```js
49+
import 'react-app-polyfill/ie11'
50+
```
51+
52+
See [react-app-polyfill's Supporting Internet Explorer docs](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill#supporting-internet-explorer) for more details.
53+
54+
### Manual Polyfilling
55+
56+
If there are specific language features missing from one of your supported browsers, you can polyfill them manually by installing [core-js](https://github.com/zloirock/core-js#core-js) and importing the appropriate polyfills at the top of your app's entry point (usually `src/index.js`).
57+
58+
e.g. if you want to use [`Object.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values) in your app, but one of your target browsers doesn't support it:
59+
60+
```
61+
npm install core-js
62+
```
63+
```js
64+
import 'core-js/features/object/values'
65+
```
66+
67+
### Automatic Polyfilling
68+
69+
nwb configures `@babel/preset-env`'s [`useBuiltins: 'entry'` option](https://babeljs.io/docs/en/next/babel-preset-env), which will look for a core-js entry point import in your code and replace it with a specific list of polyfill imports to cover the range of supported browsers. See the [core-js docs for an example of this feature in action](https://github.com/zloirock/core-js#babelpreset-env).
70+
71+
To make use of this, import a core-js entry point at the top of your app's entry point (usually `src/index.js`):
72+
73+
```js
74+
import 'core-js/stable'
75+
```
76+
77+
react-app-polyfill also provides [an entry point for polyfilling stable language features using core-js](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill#polyfilling-other-language-features), so nwb's Babel config supports transpiling react-app-polyfill to allow `@babel/preset-env` to do its thing:
78+
79+
```js
80+
import 'react-app-polyfill/stable'
81+
```

docs/Configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ If configured, it must be one of the following:
144144

145145
Configures [Browserslist](https://github.com/browserslist/browserslist#browserslist-) queries specifying the range of browsers your app supports.
146146

147+
This is used to configure [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env#targets) (so it only transpiles the necessary ECMAScript 2015+ for your target browsers) and [Autoprefixer](https://github.com/postcss/autoprefixer#browsers) (for managing vendor prefixes in your CSS).
148+
147149
If you don't configure this, nwb's default configuration is:
148150

149151
- `'last 1 chrome version, last 1 firefox version, last 1 safari version'` for development (when running a development server with `nwb serve`)

docs/Features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
- **A toolkit, not a boilerplate.**
44
- Uses [Webpack 4](https://webpack.js.org/), [Babel 7](https://babeljs.io/) and [Karma 5](https://karma-runner.github.io/).
55
- Provides tooling for [React](https://facebook.github.io/react/) apps and components, [Preact](https://preactjs.com/) apps, [Inferno](https://infernojs.org/) apps, and vanilla JS web apps and npm modules.
6-
- Use modern JavaScript features, and JSX.
6+
- Use modern JavaScript features and JSX.
77
- Use proposed JavaScript features now.
88
- Import CSS (and font resources) and images to be managed by Webpack.
99
- [Autoprefixed](https://github.com/postcss/autoprefixer#autoprefixer-) CSS, so you don't need to write browser prefixes; you can also configure your own [PostCSS](https://postcss.org/) plugins.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848

4949
"@babel/cli": "7.8.4",
5050
"@babel/core": "7.9.6",
51+
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
52+
"@babel/plugin-proposal-optional-chaining": "7.9.0",
5153
"@babel/plugin-syntax-dynamic-import": "7.8.3",
5254
"@babel/plugin-syntax-jsx": "7.8.3",
5355
"@babel/plugin-transform-react-constant-elements": "7.9.0",

src/appCommands.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import runSeries from 'run-series'
66
import merge from 'webpack-merge'
77

88
import cleanApp from './commands/clean-app'
9+
import {DEFAULT_BROWSERS_DEV, DEFAULT_BROWSERS_PROD} from './constants'
910
import {directoryExists, install} from './utils'
1011
import webpackBuild from './webpackBuild'
1112
import webpackServer from './webpackServer'
@@ -58,6 +59,14 @@ export function createBuildConfig(args: Object, extra: Object = {}) {
5859
let filenamePattern = production ? '[name].[chunkhash:8].js' : '[name].js'
5960

6061
let config: Object = {
62+
babel: {
63+
env: {
64+
targets: DEFAULT_BROWSERS_PROD,
65+
useBuiltIns: 'entry',
66+
corejs: 3,
67+
exclude: ['transform-typeof-symbol'],
68+
},
69+
},
6170
devtool: 'source-map',
6271
entry: {
6372
app: [entry],
@@ -94,6 +103,14 @@ export function createServeConfig(args: Object, ...extra: Object[]) {
94103
let dist = path.resolve(args._[2] || 'dist')
95104

96105
let config: Object = {
106+
babel: {
107+
env: {
108+
targets: DEFAULT_BROWSERS_DEV,
109+
useBuiltIns: 'entry',
110+
corejs: 3,
111+
exclude: ['transform-typeof-symbol'],
112+
},
113+
},
97114
entry: [entry],
98115
output: {
99116
path: dist,

src/commands/build-demo.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path'
22

33
import runSeries from 'run-series'
44

5+
import {DEFAULT_BROWSERS_PROD} from '../constants'
56
import {directoryExists} from '../utils'
67
import webpackBuild from '../webpackBuild'
78
import cleanDemo from './clean-demo'
@@ -15,6 +16,12 @@ function getCommandConfig(args) {
1516

1617
let config = {
1718
babel: {
19+
env: {
20+
targets: DEFAULT_BROWSERS_PROD,
21+
useBuiltIns: 'entry',
22+
corejs: 3,
23+
exclude: ['transform-typeof-symbol'],
24+
},
1825
presets: ['react'],
1926
},
2027
devtool: 'source-map',

src/commands/serve-react-demo.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from 'path'
22

3+
import {DEFAULT_BROWSERS_DEV} from '../constants'
34
import {directoryExists} from '../utils'
45
import webpackServer from '../webpackServer'
56

@@ -13,6 +14,12 @@ export default function serveReactDemo(args, cb) {
1314

1415
let config = {
1516
babel: {
17+
env: {
18+
targets: DEFAULT_BROWSERS_DEV,
19+
useBuiltIns: 'entry',
20+
corejs: 3,
21+
exclude: ['transform-typeof-symbol'],
22+
},
1623
presets: ['react'],
1724
},
1825
entry: [path.resolve('demo/src/index.js')],

src/createBabelConfig.js

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ type BuildOptions = {
2323
webpack?: boolean,
2424
};
2525

26+
type BrowserOptions = {
27+
development?: string | string[],
28+
production?: string | string[],
29+
}
30+
2631
type UserOptions = {
2732
cherryPick?: string | string[],
2833
config?: (BabelConfig) => BabelConfig,
@@ -40,11 +45,13 @@ type UserOptions = {
4045
export default function createBabelConfig(
4146
buildConfig: BuildOptions = {},
4247
userConfig: UserOptions = {},
43-
userConfigPath: string = ''
48+
userConfigPath: string = '',
49+
userConfigBrowsers: BrowserOptions = {},
4450
): BabelConfig {
4551
let {
4652
absoluteRuntime,
4753
commonJSInterop,
54+
env: buildEnv = {},
4855
modules = false,
4956
plugins: buildPlugins = [],
5057
presets: buildPresets,
@@ -57,7 +64,7 @@ export default function createBabelConfig(
5764
let {
5865
cherryPick,
5966
config: userConfigFunction,
60-
env = {},
67+
env: userEnv = {},
6168
loose,
6269
plugins: userPlugins = [],
6370
presets: userPresets,
@@ -69,15 +76,41 @@ export default function createBabelConfig(
6976
} = userConfig
7077

7178
let presets: BabelPluginConfig[] = []
72-
let plugins: BabelPluginConfig[] = []
79+
let plugins: BabelPluginConfig[] = [
80+
// XXX Webpack can't currently handle untranspiled ?. and ?? syntax
81+
// See https://github.com/webpack/webpack/issues/10227#issuecomment-588409413
82+
require.resolve('@babel/plugin-proposal-optional-chaining'),
83+
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
84+
]
7385

7486
// Default to loose mode unless explicitly configured
7587
if (typeof loose === 'undefined') {
7688
loose = true
7789
}
7890

91+
// Build config controls whether or not we set browser targets. Users can
92+
// override this using `browsers` or `babel.env.targets` config.
93+
let userTargets = {}
94+
if (buildEnv.targets) {
95+
let targets = userConfigBrowsers && (
96+
process.env.NODE_ENV === 'production'
97+
? userConfigBrowsers.production
98+
: userConfigBrowsers.development
99+
)
100+
if (targets) {
101+
userTargets.targets = targets
102+
}
103+
}
79104
presets.push(
80-
[require.resolve('@babel/preset-env'), {loose, modules, ...env}]
105+
[require.resolve('@babel/preset-env'), {
106+
loose,
107+
...buildEnv,
108+
modules,
109+
// Targets config from top-level browsers config if present
110+
...userTargets,
111+
// The user gets a last go at all the env options
112+
...userEnv
113+
}]
81114
)
82115

83116
// Additional build presets

src/createWebpackConfig.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ export function createRules(
340340
createRule('babel', {
341341
test: /\.js$/,
342342
loader: require.resolve('babel-loader'),
343-
exclude: /node_modules/,
343+
exclude: /node_modules[\\/](?!react-app-polyfill)/,
344344
options: {
345345
// Don't look for .babelrc files
346346
babelrc: false,
@@ -591,6 +591,8 @@ export function createPlugins(
591591
}
592592

593593
function createDefaultPostCSSPlugins(userWebpackConfig) {
594+
// Users can override browser versions for Autoprefixer using `browsers` or
595+
// `webpack.autoprefixer.overrideBrowserslist` config.
594596
let overrideBrowserslist = process.env.NODE_ENV === 'production'
595597
? (userWebpackConfig.browsers && userWebpackConfig.browsers.production) ||
596598
DEFAULT_BROWSERS_PROD
@@ -699,7 +701,9 @@ export default function createWebpackConfig(
699701
}
700702

701703
// Generate config for babel-loader and set it as loader config for the build
702-
buildRulesConfig.babel = {options: createBabelConfig(buildBabelConfig, userConfig.babel, userConfig.path)}
704+
buildRulesConfig.babel = {
705+
options: createBabelConfig(buildBabelConfig, userConfig.babel, userConfig.path, userConfig.browsers)
706+
}
703707

704708
let webpackConfig = {
705709
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',

src/quickCommands.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import runSeries from 'run-series'
66
import merge from 'webpack-merge'
77

88
import cleanApp from './commands/clean-app'
9+
import {DEFAULT_BROWSERS_DEV, DEFAULT_BROWSERS_PROD} from './constants'
910
import {UserError} from './errors'
1011
import {install} from './utils'
1112
import webpackBuild from './webpackBuild'
@@ -80,6 +81,12 @@ export function createBuildConfig(args: Object, options: QuickConfigOptions) {
8081

8182
let config: Object = {
8283
babel: {
84+
env: {
85+
targets: DEFAULT_BROWSERS_PROD,
86+
useBuiltIns: 'entry',
87+
corejs: 3,
88+
exclude: ['transform-typeof-symbol'],
89+
},
8390
proposals: {
8491
all: true
8592
}
@@ -140,6 +147,12 @@ export function createServeConfig(args: Object, options: QuickConfigOptions) {
140147

141148
let config: Object = {
142149
babel: {
150+
env: {
151+
targets: DEFAULT_BROWSERS_DEV,
152+
useBuiltIns: 'entry',
153+
corejs: 3,
154+
exclude: ['transform-typeof-symbol'],
155+
},
143156
proposals: {
144157
all: true
145158
}

0 commit comments

Comments
 (0)