Skip to content

Commit

Permalink
Added top-level browsers config for configuring browser support with …
Browse files Browse the repository at this point in the history
…a browserslist query

Breaking: configuring webpack.autoprefixer as a browserslist query is deprecated and won't do anything
Breaking: default browser support for CSS prefixes when building changed from '>1%, last 4 versions, Firefox ESR, not ie < 9' to '>0.2%, not dead, not op_mini all'

#550
  • Loading branch information
insin committed May 17, 2020
1 parent bf05318 commit c8e9dbf
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 37 deletions.
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@
- Removed default polyfills for `Promise`, `fetch()` and `Object.assign()` and deprecated `polyfill` config.
- If you need to support older browsers, you will need to provide the necessary polyfills yourself.
- 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.
- 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).
- 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).
- 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).

## Added

- 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.

## Changed

- 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).

## Dependencies

- chalk: v3.0.0 → [v4.0.0](https://github.com/chalk/chalk/releases/tag/v4.0.0)
Expand Down
59 changes: 42 additions & 17 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
...or a function which returns a configuration object when called:

```js
module.exports = function(args) {
module.exports = function(nwb) {
return {
// ...
}
Expand Down Expand Up @@ -68,6 +68,7 @@ The configuration object can include the following properties:

- nwb Configuration
- [`type`](#type-string-required-for-generic-build-commands)
- [`browsers`](#browsers-string--arraystring--object) - browserslist queries for the browsers your app supports
- [Babel Configuration](#babel-configuration)
- [`babel`](#babel-object)
- [`babel.cherryPick`](#cherrypick-string--arraystring) - enable cherry-picking for destructured `import` statements
Expand All @@ -85,7 +86,7 @@ The configuration object can include the following properties:
- [Webpack Configuration](#webpack-configuration)
- [`webpack`](#webpack-object)
- [`webpack.aliases`](#aliases-object) - rewrite certain import paths
- [`webpack.autoprefixer`](#autoprefixer-string--object) - options for Autoprefixer
- [`webpack.autoprefixer`](#autoprefixer-object) - options for Autoprefixer
- [`webpack.compat`](#compat-object) - enable Webpack compatibility tweaks for commonly-used modules
- [`webpack.copy`](#copy-array--object) - patterns and options for `CopyWebpackPlugin`
- [`webpack.debug`](#debug-boolean) - create a more debuggable production build
Expand Down Expand Up @@ -139,6 +140,40 @@ If configured, it must be one of the following:
- `'web-app'`
- `'web-module'`

#### `browsers`: `String | Array<String> | Object`

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

If you don't configure this, nwb's default configuration is:

- `'last 1 chrome version, last 1 firefox version, last 1 safari version'` for development (when running a development server with `nwb serve`)
- `'>0.2%, not dead, not op_mini all'` for production (when building your app with `nwb build`)

To override the defaults individually, configure an object with `development` or `production` properties:

```js
module.exports = {
browsers: {
production: '>0.5%, not IE 11'
}
}
```

If you configure a string or a list of strings, it will be used for both development and production:

```js
module.exports = {
browsers: [
'last 2 chrome versions',
'last 2 firefox versions',
'last 2 safari versions',
'IE 11'
]
}
```

You can check which browsers your query will target using the [browserl.ist](http://browserl.ist) service.

### Babel Configuration

#### `babel`: `Object`
Expand Down Expand Up @@ -413,36 +448,26 @@ module.exports = {

You should be careful to avoid creating aliases which conflict with the names of Node.js built-ins or npm packages, as you will then be unable to import them.

##### `autoprefixer`: `String | Object`
##### `autoprefixer`: `Object`

Configures [Autoprefixer options](https://github.com/postcss/autoprefixer#options) for nwb's default PostCSS configuration.

If you just need to configure the range of browsers prefix addition/removal is based on (nwb's default is `>1%, last 4 versions, Firefox ESR, not ie < 9`), you can use a String:
If you want to configure the range of browsers prefix management is based on, the top-level [`browsers` config](#browsers-string--arraystring--object) is passed to Autoprefixer by default for its `overrideBrowserslist` option.

```js
module.exports = {
webpack: {
autoprefixer: '> 1%, last 2 versions, Firefox ESR, ios >= 8'
}
}
```

Use an Object if you need to set any of Autoprefixer's other options.
Provide an Object to configure any of Autoprefixer's other options.

e.g. if you also want to disable removal of prefixes which aren't required for the configured range of browsers:
e.g. if you want to enable grid prefixes for IE 10 and IE 11:

```js
module.exports = {
webpack: {
autoprefixer: {
remove: false,
grid: 'autoplace'
}
}
}
```

You can check which browsers your Autoprefixer configuration will target using the [browserl.ist](http://browserl.ist) service.

##### `compat`: `Object`

Certain libraries require specific configuration to play nicely with Webpack - nwb can take care of the details for you if you use a `compat` object to tell it when you're using them.
Expand Down
28 changes: 27 additions & 1 deletion src/config/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ export function processUserConfig({
let report = new UserConfigReport({configFileExists, configPath})

let {
type, polyfill,
type, browsers,
// TODO Deprecated - remove
polyfill,
babel, devServer, karma, npm, webpack: _webpack, // eslint-disable-line no-unused-vars
...unexpectedConfig
} = userConfig
Expand All @@ -162,6 +164,30 @@ export function processUserConfig({
report.error('type', userConfig.type, `Must be one of: ${joinAnd(Array.from(PROJECT_TYPES), 'or')}`)
}

if ('browsers' in userConfig) {
if (typeOf(browsers) === 'string' || typeOf(browsers) === 'array') {
userConfig.browsers = {
development: browsers,
production: browsers
}
}
else if (typeOf(browsers) === 'object') {
if (!browsers.hasOwnProperty('development') && !browsers.hasOwnProperty('production')) {
report.hint(
'browsers',
`You provided ${chalk.cyan('browsers')} config but didn't provide ${chalk.cyan('development')} or ${chalk.cyan('production')} settings`
)
}
}
else {
report.error(
'browsers',
`type: ${typeOf(browsers)}`,
`Must be a ${chalk.cyan('String')}, ${chalk.cyan('Array')} or ${chalk.cyan('Object')}`
)
}
}

// TODO Deprecated - remove
if ('polyfill' in userConfig) {
if (!warnedAboutPolyfillConfig) {
Expand Down
16 changes: 12 additions & 4 deletions src/config/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {joinAnd, pluralise as s, typeOf} from '../utils'

const DEFAULT_STYLE_LOADERS = new Set(['css', 'postcss'])

let warnedAboutAutoPrefixerString = false

export function processWebpackConfig({pluginConfig, report, userConfig}) {
let {
aliases,
Expand Down Expand Up @@ -58,16 +60,22 @@ export function processWebpackConfig({pluginConfig, report, userConfig}) {

// autoprefixer
if ('autoprefixer' in userConfig.webpack) {
// Convenience: allow Autoprefixer browsers config to be configured as a String
// TODO Deprecated - remove
if (typeOf(autoprefixer) === 'string') {
userConfig.webpack.autoprefixer = {overrideBrowserslist: autoprefixer}
if (!warnedAboutAutoPrefixerString) {
report.deprecated(
'webpack.autoprefixer as a String',
'Replaced by top-level "browsers" config in nwb v0.25.0 - webpack.autoprefixer can no longer be a String',
)
warnedAboutAutoPrefixerString = true
}
userConfig.webpack.autoprefixer = {}
}
else if (typeOf(autoprefixer) !== 'object') {
report.error(
'webpack.autoprefixer',
`type: ${typeOf(autoprefixer)}`,
`Must be a ${chalk.cyan('String')} (for ${chalk.cyan('browsers')} config only) ` +
`or an ${chalk.cyan('Object')} (for any Autoprefixer options)`
`Must be an ${chalk.cyan('Object')}`
)
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ export const PROJECT_TYPES = new Set([
WEB_APP,
WEB_MODULE,
])

export const DEFAULT_BROWSERS_DEV = 'last 1 chrome version, last 1 firefox version, last 1 safari version'
export const DEFAULT_BROWSERS_PROD = '>0.2%, not dead, not op_mini all'
13 changes: 7 additions & 6 deletions src/createWebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import webpack from 'webpack'
import merge from 'webpack-merge'

import createBabelConfig from './createBabelConfig'
import {DEFAULT_BROWSERS_DEV, DEFAULT_BROWSERS_PROD} from './constants'
import debug from './debug'
import {UserError} from './errors'
import {deepToString, replaceArrayMerge, typeOf} from './utils'
Expand Down Expand Up @@ -590,14 +591,14 @@ export function createPlugins(
}

function createDefaultPostCSSPlugins(userWebpackConfig) {
let overrideBrowserslist = process.env.NODE_ENV === 'production'
? (userWebpackConfig.browsers && userWebpackConfig.browsers.production) ||
DEFAULT_BROWSERS_PROD
: (userWebpackConfig.browsers && userWebpackConfig.browsers.development) ||
DEFAULT_BROWSERS_DEV
return [
autoprefixer({
overrideBrowserslist: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
],
overrideBrowserslist,
...userWebpackConfig.autoprefixer
})
]
Expand Down
22 changes: 13 additions & 9 deletions tests/config-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ function process(config) {

describe('processUserConfig()', () => {
describe('validation', () => {
it('config file has an invalid type', () => {
it('type is not a string', () => {
check({type: 'invalid'}, 'type', /Must be/)
})
it('browsers is an invalid type', () => {
check({browsers: /invalid/}, 'browsers', /Must be/)
})
it('babel.plugins is not an array', () => {
check({babel: {plugins: {}}}, 'babel.plugins', /Must be/)
})
Expand All @@ -76,10 +79,7 @@ describe('processUserConfig()', () => {
it('babel.proposals is not an object or false', () => {
check({babel: {proposals: /invalid/}}, 'babel.proposals', /Must be/)
})
it('babel.react is not an string or an object', () => {
check({babel: {react: /invalid/}}, 'babel.react', /Must be/)
})
it('babel.react is not an string or an object', () => {
it('babel.react is not a string or an object', () => {
check({babel: {react: /invalid/}}, 'babel.react', /Must be/)
})
it('babel.reactConstantElements is not a boolean', () => {
Expand Down Expand Up @@ -200,6 +200,14 @@ describe('processUserConfig()', () => {
})

describe('convenience shorthand', () => {
it('allows browsers to be a string', () => {
let config = process({browsers: 'test'})
expect(config.browsers).toEqual({development: 'test', production: 'test'})
})
it('allows browsers to be an array', () => {
let config = process({browsers: ['test']})
expect(config.browsers).toEqual({development: ['test'], production: ['test']})
})
it('allows babel.react to be a string', () => {
let config = process({babel: {react: 'test'}})
expect(config.babel.react).toEqual({runtime: 'test'})
Expand All @@ -208,10 +216,6 @@ describe('processUserConfig()', () => {
let config = process({npm: {umd: 'test'}})
expect(config.npm.umd).toEqual({global: 'test'})
})
it('allows webpack.autoprefixer to be a browser string', () => {
let config = process({webpack: {autoprefixer: 'test'}})
expect(config.webpack.autoprefixer).toEqual({overrideBrowserslist: 'test'})
})
it('allows webpack.copy to be an array', () => {
let config = process({webpack: {copy: ['test']}})
expect(config.webpack.copy).toEqual({patterns: ['test']})
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/worst-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// A config file in which everything is wrong
module.exports = {
type: 'invalid',
browsers: 45,
polyfill: 45,
invalidTopLevelConfig: true,
moreInvalidTopLevelConfig: true,
Expand Down

0 comments on commit c8e9dbf

Please sign in to comment.