Error no browserslist config found to handle the browserslist target

Target Webpack can compile for multiple environments or targets. To understand what a target is in detail, read through the targets concept page. target string [string] false Instructs webpack to generate runtime code for a specific environment. Note that webpack runtime code is not the same as the user code you write, you should […]

Содержание

  1. Target
  2. target
  3. string
  4. browserslist
  5. [string]
  6. false
  7. Уско­ря­ем­ся с по­мо­щью Browserslist
  8. Browserslist Скопировать ссылку
  9. Варианты таргетирования браузеров Скопировать ссылку
  10. 1. Более узкое таргетирование Скопировать ссылку
  11. 2. Анализ аудитории Скопировать ссылку
  12. 3. Условная загрузка ресурсов Скопировать ссылку
  13. Browserslist-useragent Скопировать ссылку
  14. Module/nomodule Скопировать ссылку
  15. Browserslist-useragent-regexp Скопировать ссылку
  16. Browserslist Differential Script Loading Скопировать ссылку
  17. Транспиляция зависимостей Скопировать ссылку
  18. Результаты Скопировать ссылку

Target

Webpack can compile for multiple environments or targets. To understand what a target is in detail, read through the targets concept page.

target

string [string] false

Instructs webpack to generate runtime code for a specific environment. Note that webpack runtime code is not the same as the user code you write, you should transpile those code with transpilers like Babel if you want to target specific environments, e.g, you have arrow functions in source code and want to run the bundled code in ES5 environments. Webpack won’t transpile them automatically with a target configured.

Defaults to ‘browserslist’ or to ‘web’ when no browserslist configuration was found.

string

The following string values are supported via WebpackOptionsApply :

Option Description
async-node[[X].Y] Compile for usage in a Node.js-like environment (uses fs and vm to load chunks asynchronously)
electron[[X].Y]-main Compile for Electron for main process.
electron[[X].Y]-renderer Compile for Electron for renderer process, providing a target using JsonpTemplatePlugin , FunctionModulePlugin for browser environments and NodeTargetPlugin and ExternalsPlugin for CommonJS and Electron built-in modules.
electron[[X].Y]-preload Compile for Electron for renderer process, providing a target using NodeTemplatePlugin with asyncChunkLoading set to true , FunctionModulePlugin for browser environments and NodeTargetPlugin and ExternalsPlugin for CommonJS and Electron built-in modules.
node[[X].Y] Compile for usage in a Node.js-like environment (uses Node.js require to load chunks)
node-webkit[[X].Y] Compile for usage in WebKit and uses JSONP for chunk loading. Allows importing of built-in Node.js modules and nw.gui (experimental)
nwjs[[X].Y] The same as node-webkit
web Compile for usage in a browser-like environment (default)
webworker Compile as WebWorker
esX Compile for specified ECMAScript version. Examples: es5, es2020.
browserslist Infer a platform and the ES-features from a browserslist-config (default if browserslist config is available)

For example, when the target is set to «electron-main» , webpack includes multiple electron specific variables.

A version of node or electron may be optionally specified. This is denoted by the [[X].Y] in the table above.

webpack.config.js

It helps determinate ES-features that may be used to generate a runtime-code (all the chunks and modules are wrapped by runtime code).

browserslist

If a project has a browserslist config, then webpack will use it for:

  • Determinate ES-features that may be used to generate a runtime-code.
  • Infer an environment (e.g: last 2 node versions the same as target: «node» with some output.environment settings).

Supported browserslist values:

  • browserslist — use automatically resolved browserslist config and environment (from the nearest package.json or BROWSERSLIST environment variable, see browserslist documentation for details)
  • browserslist:modern — use modern environment from automatically resolved browserslist config
  • browserslist:last 2 versions — use an explicit browserslist query (config will be ignored)
  • browserslist:/path/to/config — explicitly specify browserslist config
  • browserslist:/path/to/config:modern — explicitly specify browserslist config and an environment

[string]

When multiple targets are passed, then common subset of features will be used:

webpack.config.js

Webpack will generate a runtime code for web platform and will use only ES5 features.

Not all targets may be mixed for now.

webpack.config.js

Will cause an error. Webpack does not support universal target for now.

false

Set target to false if none of the predefined targets from the list above meet your needs, no plugins will be applied.

webpack.config.js

Or you can apply specific plugins you want:

webpack.config.js

When no information about the target or the environment features is provided, then ES2015 will be used.

Источник

Уско­ря­ем­ся с по­мо­щью Browserslist

Редактура Ирина Питаева Вадим Макеев

На сегодняшний день имеется большое количество разных браузеров и ещё большее количество версий каждого из них. Если раньше новые фичи добавлялись в браузеры редко, то сейчас их можно обнаружить практически с каждым новым релизом. Как итог, у разных версий браузера — разная поддержка фич, не говоря уже о разном уровне поддержки среди разных вендоров.

Разработчикам хочется использовать новые фичи, так как зачастую это упрощает жизнь. Благодаря инструментам разработки можно использовать фичи до их появления в браузерах путём транспиляции и использования полифилов. Также эти инструменты гарантируют работу сайта в любом браузере независимо от того, имеется ли в нём поддержка той или иной фичи. Из примеров: Autoprefixer и postcss-preset-env для CSS, Babel для JavaScript. Но нужно понимать, что при этом размер кода увеличивается.

В итоге мы имеем сайт, который работает в любом браузере, но из-за этого загружается медленнее, чем мог бы. Напомню, что скорость загрузки и работы сайта напрямую влияет на количество и глубину просмотров. Что с этим можно сделать? На самом деле нам незачем транспилировать и полифилить абсолютно все фичи — достаточно это делать только с теми, которые не поддерживаются актуальными браузерами (или актуальными для аудитории вашего сайта). Например, промисы не поддерживаются только очень старыми версиями браузеров.

Browserslist Скопировать ссылку

Browserslist — это тот инструмент, с помощью которого можно описать целевые браузеры веб-приложения, используя простые выражения:

Этот пример файла .browserslistrc означает, что вам нужны: браузеры за последние два года, плюс браузеры у которых больше 1% пользователей, и все эти браузеры должны быть «живыми». Посмотреть в какие конкретные браузеры это разрезолвится можно на сайте browserl.ist, а более подробно узнать про синтаксис выражений можно на странице проекта.

Уже упомянутые Autoprefixer, postcss-preset-env и babel-preset-env под капотом используют Browserslist, и если в вашем проекте есть конфиг Browserslist, то код проекта будет собран под эти браузеры.

На этом этапе можно прийти к следующему выводу: чем новее браузеры, в которые мы целимся, тем меньше будет весить итоговый код сайта. При этом нужно не забывать, что в реальном мире не у всех пользователей самый новый браузер, и сайт должен быть доступен если не всем пользователям, то большинству из них. Как можно поступить, учитывая это условие?

Варианты таргетирования браузеров Скопировать ссылку

1. Более узкое таргетирование Скопировать ссылку

По умолчанию, если в проекте нет конфига, то Browserslist будет использовать defaults браузеры. Это выражение является сокращением для > 0.5%, last 2 versions, Firefox ESR, not dead . В целом, можно остановиться на использовании этого выражения, и со временем браузеры, подходящие под это выражение, станут поддерживать большинство актуальных фич.

Но можно целиться в более узкое количество браузеров: исключить старые и непопулярные, учитывать более-менее актуальные версии браузеров. Звучит просто, но на самом деле это не так. Необходимо сбалансировать конфиг Browserslist так, чтобы он, опять же, покрывал большую часть аудитории.

2. Анализ аудитории Скопировать ссылку

Если ваш сайт подразумевает использование только в определенных регионах, то можно попробовать использовать выражение вида > 5% in US , которое вернёт браузеры, подходящие по статистике использования в указанной стране.

Семейство Browserslist полно разными дополнительными инструментами, один из них — Browserslist-GA (также есть browserslist-adobe-analytics), который позволяет выгрузить из сервиса аналитики данные о браузерах, используемые посетителями вашего сайта. После этого становится возможным использовать эти данные в конфиге Browserslist и применять на них выражения:

К примеру, если обновлять эти данные при каждом деплое, то сайт будет всегда собираться под актуальные браузеры, используемые вашей аудиторией.

3. Условная загрузка ресурсов Скопировать ссылку

В марте 2019 Матиас Байненс из Google предложил добавить в браузеры дифференциальную загрузку скриптов (differential script loading, далее DSL):

До сих пор его предложение остается лишь предложением, и неизвестно, будет ли это в том или ином виде реализовано в браузерах. Но концепт понятен, и в семействе Browserslist есть инструменты, с помощью которых можно реализовать что-то подобное, один из них — browserslist-useragent. Этот инструмент позволяет проверить User-Agent браузера на соответствие вашему конфигу.

Browserslist-useragent Скопировать ссылку

В интернете уже есть несколько статей на эту тему, вот пример одной из них — «Smart Bundling: How To Serve Legacy Code Only To Legacy Browsers». Коротко пробежимся по реализации. Для начала необходимо настроить ваш процесс сборки для вывода, например, двух версий бандлов для новых и старых браузеров. Здесь вам поможет Browserslist с его возможностью объявления нескольких сред в конфигурационном файле:

Далее необходимо настроить сервер для отдачи нужного бандла под браузер пользователя:

Таким образом, сайт будет выдавать легковесный бандл пользователям с новыми браузерами, что приведёт к более быстрой загрузке страниц, сохраняя при этом доступность для остальных пользователей. Но, как можно было заметить, этот способ требует наличия собственного сервера со специальной логикой.

Module/nomodule Скопировать ссылку

С появлением в браузерах поддержки ES-модулей появился другой способ реализовать DSL, но уже на стороне клиента:

Этот паттерн имеет название module/nomodule, и его суть состоит в том, что старые браузеры без поддержки ES-модулей не будут обрабатывать скрипты с типом module , так как этот тип им неизвестен. А браузеры, которые поддерживают ES-модули, будут загружать скрипт с типом module и игнорировать скрипт с атрибутом nomodule . Браузеры с поддержкой ES-модулей, описываются следующим конфигом:

Главным плюсом паттерна module/nomodule является отсутствие необходимости в собственном сервере — всё работает полностью на клиентской части. Загрузку стилей в зависимости от браузера таким образом не сделать, но можно реализовать загрузку ресурсов с помощью JavaScript, используя проверку вида:

Из минусов: этот паттерн имеет кое-какие кроссбраузерные проблемы. Также уже начинают появляться фичи, которые имеют разный уровень поддержки, даже среди браузеров с поддержкой ES-модулей, например, optional chaining operator. С появлением новых фич этот вариант DSL потеряет свою актуальность.

Прочитать ещё больше про паттерн module/nomodule можно в статье «Modern Script Loading». Если вас заинтересовал этот вариант DSL и хочется попробовать внедрить его в свой проект, то вы можете воспользоваться плагином для Webpack: webpack-module-nomodule-plugin.

Browserslist-useragent-regexp Скопировать ссылку

Относительно недавно появился ещё один инструмент для Browserslist: browserslist-useragent-regexp. Этот инструмент позволяет получить из конфига регулярное выражение для проверки User-Agent браузера. Регулярные выражения работают в любой среде исполнения JavaScript, что даёт возможность проверять User-Agent браузера не только на стороне сервера, но и на стороне клиента. Таким образом, можно реализовать работающий в браузере DSL:

Можно добавить, что генерируемые регулярные выражения работают быстрее, чем функция matchesUA из browserslist-useragent, так что есть смысл использовать browserslist-useragent-regexp и на стороне сервера:

Все это выглядит очень здорово, но хочется иметь удобный способ встроить это в процесс сборки проекта… И такой способ есть!

Browserslist Differential Script Loading Скопировать ссылку

Bdsl-webpack-plugin это плагин для Webpack, работающий в паре с html-webpack-plugin и использующий browserslist-useragent-regexp, который помогает автоматизировать добавление в бандл дифференциальной загрузки. Вот пример конфига для Webpack с применением этого плагина:

В этом примере экспортируется несколько конфигов для вывода бандлов для каждой среды из конфига Browserslist. На выходе мы получим HTML-файл со встроенным DSL-скриптом:

Помимо загрузки скриптов есть поддержка загрузки стилей. Также имеется возможность использовать этот плагин на стороне сервера.

Но, к сожалению, есть и кое-какие нюансы, о которых нужно знать при использовании bdsl-webpack-plugin: так как загрузка скриптов и стилей инициализируется из JavaScript, то они загружаются асинхронно без блокировки рендеринга и т. д. К примеру, в случае скриптов — это означает невозможность использования атрибута defer , а для стилей — необходимость скрывать контент страницы до момента полной загрузки стилей. Про то, как можно обойти эти нюансы, и про другие возможности данного плагина можно прочитать в документации и в примерах использования.

Транспиляция зависимостей Скопировать ссылку

Из уже прочитанной части статьи мы узнали несколько способов того, как с помощью Browserslist можно уменьшить размер собственного кода сайта, но остаётся другая часть итогового бандла — зависимости. В веб-приложениях размер зависимостей в итоговом бандле может занимать весомую часть.

По умолчанию, процесс сборки должен избегать транспиляцию зависимостей, так как тогда сборка будет занимать много времени, да и сами зависимости, если их исходники используют неподдерживаемый синтаксис, обычно распространяются уже транспилированными. На практике же можно выделить три типа пакетов:

  1. c транспилированным кодом;
  2. c транспилированным и исходным кодом;
  3. c кодом на актуальном синтаксисе только для новых браузеров.

С первым типом, очевидно, ничего сделать не получится. Второй — нужно настроить бандлер для работы только с исходной версией кода в пакете. Третий тип — для работы кода даже в не очень актуальных браузерах всё равно необходимо транспилировать.

Так как общепринятого способа делать пакеты с несколькими версиями бандла нет, то опишу как я делаю такие пакеты: обычная транспилированная версия имеет расширение .js , и главный файл записывается в поле main файла package.json , а версия бандла без транспиляции имеет расширение .babel.js , и главный файл записывается в поле raw . Вот реальный пример — пакет Canvg. Но можно делать и по-другому, например, как это сделано в пакете Preact — исходники выделены в отдельную папку, а в package.json есть поле source .

Для того, чтобы Webpack мог работать с такими пакетами, нужно модифицировать секцию resolve :

Таким образом мы указываем Webpack как он должен искать файлы в пакетах, которые он должен использовать для сборки. Дальше нам остаётся настроить babel-loader:

Логика простая: просим игнорировать всё из node_modules , за исключением конкретных пакетов и файлов с определенным расширением.

Результаты Скопировать ссылку

Я замерил размеры бандла и скорости загрузки до и после применения дифференциальной загрузки вместе с транспиляцией зависимостей, на примере сайта DevFest Siberia 2019:

Размеры бандла

Без сжатия После сжатия
Без DSL 1,08 Мб 292 Кб
С плагином BDSL для Webpack 0,80 Мб 218 Кб

Среднее время загрузки, мс

Обычный интернет Обычный 4G Хороший 3G
Без DSL 1,511 4,240 8,696
С плагином BDSL для Webpack 1,594 3,409 8,561

Лучшее время загрузки, мс

Обычный интернет Обычный 4G Хороший 3G
Без DSL 1,266 3,366 8,349
С плагином BDSL для Webpack 1,143 3,142 6,673

В итоге получился прирост скорости загрузки и размер бандла уменьшился на ≈20%, читайте более подробный отчёт. Также вы можете самостоятельно провести подобные замеры — необходимый скрипт есть в репозитории bdsl-webpack-plugin.

Источник

I’ve got a very simple Create React App that I am trying to implement Cypress with. I initially had a different but related error that I was able to overcome. I’m not entirely sure why I would be given this type of error considering I do have the default «browserslist» key in my package.json file.

package.json

{
  "name": "cypress_test",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": "16.13.1",
    "npm": "8.1.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "cypress": "cypress open",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "dependencies": {
    "@cypress/react": "^5.12.4",
    "@cypress/webpack-dev-server": "^1.8.4",
    "@emotion/react": "^11.9.0",
    "@emotion/styled": "^11.8.1",
    "@fontsource/roboto": "^4.5.5",
    "@mui/icons-material": "^5.6.2",
    "@mui/material": "^5.6.3",
    "@mui/styled-engine-sc": "^5.6.1",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^12.1.5",
    "@testing-library/user-event": "^13.5.0",
    "browserslist": "^4.6.3",
    "cypress": "^9.6.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "5.0.1",
    "sass": "^1.51.0",
    "styled-components": "^5.3.5",
    "web-vitals": "^2.1.4"
  },
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@babel/preset-env": "^7.16.11",
    "@cypress/webpack-preprocessor": "^5.11.1",
    "babel-loader": "^8.2.5",
    "eslint-plugin-cypress": "^2.12.1",
    "html-webpack-plugin": "^4.5.2",
    "webpack": "^5.72.0"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "overrides": [
    {
      "extends": [
        "plugin:cypress/recommended"
      ],
      "files": [
        "cypress/**/*.js"
      ]
    }
  ],
  "resolutions": {
    "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"
  },
  "jest": {
    "coveragePathIgnorePatterns": [
      "<rootDir>/cypress/"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

When I initially installed CRA, it used the latest version of React 18, but apparently Cypress doesn’t have support for 18 yet so I downgraded React to 17. I’m wondering if there are some sort of package mismatches that I’m unaware of and aren’t receiving specific errors for. As a note though, the app runs fine in the browser. Here are the other related files:

/cypress/plugins/index.js:

const findWebpack = require('find-webpack')
const webpackPreprocessor = require('@cypress/webpack-preprocessor');
const injectDevServer = require('@cypress/react/plugins/react-scripts');

module.exports = (on, config) => {
  const webpackOptions = findWebpack.getWebpackOptions();

  if (!webpackOptions) {
    throw new Error('Could not find Webpack in this project');
  }

  const cleanOptions = { reactScripts: true };

  findWebpack.cleanForCypress(cleanOptions, webpackOptions);

  const options = {
    webpackOptions,
    watchOptions: {},
  };

  on('file:preprocessor', webpackPreprocessor(options));
  injectDevServer(on, config);
  return config;
};

App.spec.js:

import React from 'react';
import data from '../fixtures/data.json';
import App from '../../src/App.jsx';

describe('Test search functionality', () => {
  beforeEach(() => {
    cy.mount(<App />);
  });

  it('renders new fact when search is performed', () => {
    cy.visit('http://localhost:3001')
    // Type in search input
    cy.get('input').type('Test');

    // Click on search button
    cy.get('.submit-btn').click();

    // Intercept the request and return the mock data
    cy
      .intercept({
        method: 'GET',
        url: `${process.env.REACT_APP_API_ENDPOINT}/jokes/search?query=Test`
      }, {
        fixture: data.result[1]
      })
      .as('fetchFact');

    // cy.wait(['@fetchFact']);

    cy.get('p.copy').should('contain', data.result[1].value);
  })
});

cypress.json:

{
  "baseUrl": "http://localhost:3001",
  "videos": false,
  "component": {
    "testFiles": "**/*.cy.{js,ts,jsx,tsx}",
    "componentFolder": "src"
  }
}

Cypress error:


Error: No browserslist config found to handle the 'browserslist' target.
See https://github.com/browserslist/browserslist#queries for possible ways to provide a config.
The recommended way is to add a 'browserslist' key to your package.json and list supported browsers (resp. node.js versions).
You can also more options via the 'target' option: 'browserslist' / 'browserslist:env' / 'browserslist:query' / 'browserslist:path-to-config' / 'browserslist:path-to-config:env'
    at TARGETS.web (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/config/target.js:93:11)
    at getTargetProperties (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/config/target.js:296:19)
    at /Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/config/target.js:342:20
    at Array.map (<anonymous>)
    at getTargetsProperties (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/config/target.js:342:11)
    at applyWebpackOptionsDefaults (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/config/defaults.js:142:6)
    at createCompiler (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/webpack.js:77:2)
    at create (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/webpack.js:134:16)
    at webpack (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/webpack.js:158:32)
    at f (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/webpack/lib/index.js:63:16)
    at Object.handler (/Users/jimmiejackson/Documents/repositories/cypress_test/node_modules/@cypress/webpack-preprocessor/dist/index.js:148:24)
    at invoke (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_plugins.js:22:16)
    at /Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:45:14
    at tryCatcher (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/util.js:16:23)
    at Function.Promise.attempt.Promise.try (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/bluebird/js/release/method.js:39:29)
    at Object.wrapChildPromise (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:44:23)
    at Object.wrap (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/preprocessor.js:28:8)
    at execute (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_plugins.js:123:27)
    at EventEmitter.<anonymous> (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_plugins.js:213:5)
    at EventEmitter.emit (node:events:390:28)
    at process.<anonymous> (/Users/jimmiejackson/Library/Caches/Cypress/9.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:19:22)
    at process.emit (node:events:390:28)

@razvan-soare

Hello, i just used ‘npx create-react-app project-name’ and when im starting my project im getting this error:

/src/index.css (./node_modules/css-loader/dist/cjs.js??ref—6-oneOf-3-1!./node_modules/postcss-loader/src??postcss!./src/index.css)
BrowserslistError: Unknown browser query android all. Maybe you are using old Browserslist or made typo in query.
at Array.reduce ()
at Array.some ()
at Array.filter ()

Npm -v : 6.9.0
Node -v : v10.16.0

boyney123, afung95014, ahmetsemsettinozdemirden, akshayakrsh, pirey, ckristhoff, Ktulh, fantasticit, buckett, sfatihk, and 131 more reacted with thumbs up emoji
richardblocksure, olso, Loading-m, timurmamedov, yongholeeme, and tiandavis reacted with hooray emoji
akshayakrsh, ckristhoff, rinkisingh8181, ziudeso, Aspiga99, ValentinBlokhin, anthonypenna, protzi, ttotal, delias, and 20 more reacted with confused emoji
jofhatkea, jaycarey, akshayakrsh, ckristhoff, bhavikji, komiyak, chentingjuan, ziudeso, valcho, Katreque, and 28 more reacted with eyes emoji

@akshayakrsh

Same issue for the same versions of node and npm

@pirey

@bhavikji

Same issue for

Npm -v : 6.9.0
Node -v : v11.2.0

@epicbytes

yep — same issue. what we gonna do?

@ckristhoff

Same here.
Node 11.6.0
npm 6.9.0

@epicbytes

may be we just downgrade version of browserlist to 4.6.1?
in 4.6.2 they are fixed android queries
() Fix last x version and similar queries for Android (by Tony Ross).

@bhavikji

@epicbytes

@epicbytes don’t we need to eject CRA for that?

i dont know in this time, its just an idea)

@fantasticit

@SunJianMing

@akshayakrsh

If you want to pin browserslist version, pin it to 4.6.0.
4.6.2 = 4.6.1 + Readme changes
4.6.1 is the faulty one and was release 20days ago

@masak

We’re having the same issue here. Halp. 😞
Node v11.1.0
npm 6.1.0

@fireswork

@ckristhoff

If you want to pin browserslist version, pin it to 4.6.0.
4.6.2 = 4.6.1 + Readme changes
4.6.1 is the faulty one and was release 20days ago

Browserslist 4.6.0 still raises same error to me.

@b4h0-c4t

Same issue for :
npm ver. 6.9.0
node ver. 12.4.0

@axlwang1976

Same issue for :
npm ver. 6.9.0
node ver. 10.16.0

@yb

Same issue for :
npm ver. 6.9.0
node ver. 10.14.1


1 similar comment

@ulsha

Same issue for :
npm ver. 6.9.0
node ver. 10.14.1

@anthonypenna

Same issue:

node v 11.6.0
npm  v 6.5.0

@qwertybot

Same issue:

node v 10.16.0
npm v 6.9.0

@belio39

Same issue:
./src/index.css (./node_modules/css-loader??ref—6-oneOf-3-1!./node_modules/postcss-loader/src??postcss!./src/index.css)
BrowserslistError: Unknown browser query android all. Maybe you are using old Browserslist or made typo in query.
at Array.reduce ()
at Array.some ()
at Array.filter ()

@rinkisingh8181

Same issue

node v v10.13.0
npm v 6.4.1

@VincentLeonrdo

Same issue
npm 6.9.0
node v10.15.3

@jfchico

Same here
npm 6.4.1
node 8.12.0

@mohammadsobhy

same here
./src/index.css (./node_modules/react-scripts/node_modules/css-loader/dist/cjs.js??ref—6-oneOf-3-1!./node_modules/postcss-loader/src??postcss!./src/index.css)
BrowserslistError: Unknown browser query android all. Maybe you are using old Browserslist or made typo in query.
at Array.reduce ()
at Array.some ()
at Array.filter ()

@sfatihk

@alexandrsmirnov93

Same issue
npm 5.6.0
node 8.11.4

@hwiVeloper

same issue
node -v : v10.13.0
yarn -v : 1.16.0

@dev-zetta

@hileix

Same issue.
node -v: 8.11.4
yarn -v: 1.16.0

@jaywcjlove

@liSong5713 Temporary solution:

npm i browserslist@4.6.2
npm i caniuse-lite@1.0.30000974

@ycjcl868

temporary solution:

// package.json

  "resolutions": {
    "browserslist": "4.6.2",
    "caniuse-lite": "1.0.30000974"
  },

maybe you can use umijs, it is not affected.

jaywcjlove, ttys026, ycjcl868, zechau, darkboom, sorrycc, IanLunn, march08, jfranty, and warriorpostman reacted with thumbs up emoji
jaywcjlove, kuitos, and ycjcl868 reacted with hooray emoji
jaywcjlove, hoangthau, and ycjcl868 reacted with heart emoji

@etisdew

This looks like a repeat of what the angular team ran into some time ago. Came to report left with a solution. Thank you! @jaywcjlove @4Eric

@jonathantneal

It looks like caniuse or caniuse-lite may have published a potentially breaking change that rolled into postcss-preset-env, which then rolled into browserslist and threw this error or warning.

How it worked: More or less, caniuse stats are provided as an object of features, like { "css-all": {}, "css-any-link": {} }. Within each feature, stats are provided as an object of browsers, like { "android": {}, "edge": {} }. Within each browser, stats are provided as an object of versions or version ranges, like { 4.1": "n", "4.2-4.3": "n" }.

For example, one tree of features might look like { "css-all": { "android": { "4.1": "n", "4.2-4.3": "n" } } }.

What changed: Within the android browser object of versions, stats now provide a simplified "all" value.

This means one tree of features might look like { "css-all": { "android": { "all": "n" } } }.

That is how the android all query ends up getting generated, which browserslist does not support.

@mastoj

How i can update Browserlist inside create-react-app?

yarn upgrade or npm update

Didn’t help with update.

@umyar

How i can update Browserlist inside create-react-app?

yarn upgrade or npm update

Didn’t help with update.

Try it stuff:

// package.json

"devDependencies": {
    "browserslist": "4.6.2"
  },
"resolutions": {
    "browserslist": "4.6.2",
    "caniuse-lite": "1.0.30000974"
  },

There is some help.

@mastoj

How i can update Browserlist inside create-react-app?

yarn upgrade or npm update

Didn’t help with update.

Try it stuff:

// package.json

"devDependencies": {
    "browserslist": "4.6.2"
  },
"resolutions": {
    "browserslist": "4.6.2",
    "caniuse-lite": "1.0.30000974"
  },

There is some help.

@umyar, that didn’t help. Adding that actuall fails my build with the following message:

./src/App.css
BrowserslistError: Unknown browser query android all. Maybe you are using old Browserslist or made typo in query.
at Array.reduce ()
at Array.some ()
at Array.filter ()

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! explorer@0.1.0 build: react-scripts build
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the explorer@0.1.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/tomasjansson/.npm/_logs/2019-06-20T12_55_59_151Z-debug.log

@nicdex

@derek-adair

OH BOY! what a dumpster fire. This broke my 2.1.8 build. Can’t get those 8 hours back….

I 100% understand that the blame here is distributed, but I still am considering ejecting and/or migrating away from react-scripts entirely. I can’t help but wonder which one of the dependencies will break next. There are over 50 direct dependencies in package.json when i eject a fresh project, and node_modules has around 1000 folders in it.

Is this REALLY the way to use react?

Also the only thing that worked for me was settings browserslist:[] in package.json as @anhquande has mentioned.

@arifulbgt4

temporary solution:

// package.json

  "resolutions": {
    "browserslist": "4.6.2",
    "caniuse-lite": "1.0.30000974"
  },

maybe you can use umijs, it is not affected.

don’t work

@zechau

temporary solution:

// package.json

  "resolutions": {
    "browserslist": "4.6.2",
    "caniuse-lite": "1.0.30000974"
  },

maybe you can use umijs, it is not affected.

don’t work

remove browserslist item, only set caniuse-lite, it worked for me. plus, you should use yarn or install npm-force-resolutions.

@ycjcl868

@zechau

@etisdew

Any level of testing should have caught this, that is the contract cost of using dependancies. I hate to ask or look since I know what I’m getting isn’t what I’ll actually use when I care about the content I’m making, but do you need help developing better tests? This project is a nice relief for dealing with rolling changes in this bloated ecosystem. I’m confident if it needs to happen the community will come together to make sure this doesn’t happen again. It could have been any subdep changes that didn’t crash getting into your npm release.

@timurmamedov

@ai @jonathantneal builds seem to be succeeding now, since the latest update to caniuse-lite from 5 hours ago. Thank you.

@jonathantneal

@timurmamedov, yes, the all version appears to be removed in caniuse-db@1.0.30000976, which has been merged into caniuse-lite@1.0.30000976.

I used this rudimentary test to compare v1.0.30000975 and v1.0.30000976:

// present in 1.0.30000975, removed in 1.0.30000976
caniuse.feature(caniuse.features['prefers-color-scheme']).stats.android.all

Looking at the most recent commits to caniuse-lite, it appears this change was most likely made within caniuse-db, and not patched in caniuse-lite. This is great news for all projects affected by the all change to caniuse-db, including PostCSS Preset Env. 🎉

@etisdew, if I managed caniuse, which merely exports a collection of function-less JSON files, I might not be prepared to test for every way other projects have chosen to use my files. If you think more tests are needed, you must produce a sufficient JSON schema and submit the PR to caniuse-db — I believe that is the contract cost of complaining about open source. 😉

@kennethrivera

@ai @jonathantneal builds seem to be succeeding now, since the latest update to caniuse-lite from 5 hours ago. Thank you.

I just tried it on my projects and it seems the issue is resolved.

@etisdew

@jonathantneal I only feel empathy for the situation in which another repository is able to affect its downstream users. From a learning perspective what is the best way of managing and remediating dependancys and the possibility of dependency divergence? Rollback and lock until stable? Find/build alternatives?

My major concern is malicious injection through strategic compromise and the dangers of rolling dependencies. I know that in any sizable form they would be obvious and found in review but on the state of the culture, the idea of manually disabling an execution context from human input grows harder and harder in the context of an automated pipeline not using strict version semantics and open source suffering from non-viable donation models to give the time and attention where it deserves to be. Keep fighting the good fight, but I would be interested to learn more about how to handle these issues, isolating ones own project from them without going full hermit.

@ai

@etisdew we already discussing solution in postcss-preset-env repo. This repo is a wrong place for issue and discussing.

@derek-adair

@ai and all the other devs in create-react-app. THANK YOUUUU. I REALLY dont wanna eject, nor migrate, nor deal w/ the crap ya’ll deal with.

This also highlights the absurdity that we deal w/ in the first place. The dependency-heavy-environment that we navigate is a result of the browser devs (read as Google/Firefox/Apple as orgs, NOT the engineers) lack of standards and coherency.

As endusers its super easy to place blame, CRA, or browserslists, or postcss-preset-env. The reality is that NONE of these would have to exist if the browser wasn’t a giant flaming turd to develop on.

Thank you for propping up our shit env with your tools.


Now, what can we do to help avoid this?

EDIT: this=mitigating upstream dependencies breaking builds.

It TERRIFIES me that 1/900+ dependencies can potentially BREAK a production build.

@derek-adair

Question is definitely off topic… I am not even talking about this browserslists fiasco.

As a user of CRA, what kind of confidence can I have in this going forward that this wont just randomly break?

@etisdew

I apologize for OT and discussion, I have to second the comment thanking you and all developers in the pipeline we have to manage everyday. You save me hours and headaches. Open source is a beautiful thing aswell. Issue seems to be largely closed yes?

@derek-adair
We can certainly have confidence that anything wrong will incur the recognition required to solve the issue with our cumulative knowledge and efforts. Its the smaller packages we sit on the shoulders of that need us most though they wont get our attention until its called for and something is already wrong. I guess the answer is some sense of community.

@ai

Yeap, this issue can be closed here.

We in separated issue are finding solution to prevent problems on similar case in the future.

@ianschmitz

As a user of CRA, what kind of confidence can I have in this going forward that this wont just randomly break?

That’s what the package lock file prevents.

Closing this issue for now as it appears to be resolved. Will leave pinned for a couple days in case anyone is still running into this issue.

@derek-adair

That’s what the package lock file prevents.

This broke a stable project on CRA 2.1.8. With said lock file, I was getting a warning saying to update caniuse-lite and browserslist. I did not update or change any deps; I woke up to builds failing.

I’d be happy to provide yarn.lock if useful.

@bugzpodder

@derek-adair please file a new issue with commands your build ran and errors you saw. Also results to «yarn why caniuse-lite»

@lock
lock
bot

locked and limited conversation to collaborators

Jun 27, 2019

julien

Hi,

I’m working on this repository, which is contains React components and uses lerna and yarn workspaces. My tests pass locally with jest --coverage and they also pass on the master branch with GitHub actions, but for some reason they don’t pass on my pull request’s branch.

I’m always getting the error: Can’t find module ‘browserslist-config-clay’.

Here’s what I’ve tried so far:

  • Adding browserslist-config-clay as a devDependency in all packages
  • Removing all "browserslist" fields from all packages
  • Adding a "browserslist" field in the root-level package.json file, with the configuration «inlined»
  • Setting ignoreBrowserslistConfig to true in babel.config.js

I’m not sure this is a browserslist issue, but I thought I’d ask for suggestions here.

Sembiance

Started getting spam in my console logs when running eslint : Browserslist: caniuse-lite is outdated. Please run: npx browserslist@latest --update-db

Uhm, it’s pretty bad form to console.warn() all random end users that a package might be out of date. That’s not something you should be concerned about or warning about.

Especially since your ‘fix’ for the problem, this update-db code, feels like a major hack, detecting package managers and updating various lock files.

Just, NO.

Please remove this console.warn

lukkea

When
> browserslist@latest --update-db

is run it reports:
> Latest version: 1.0.30001260
> Installed version: none
> Removing old caniuse-lite from lock file
> Installing new caniuse-lite version
> $ yarn add -W caniuse-lite
> Cleaning package.json dependencies from caniuse-lite
> $ yarn remove -W caniuse-lite
> caniuse-lite has been successfully updated

and on the next line it reports:
> Browserslist: caniuse-lite is outdated. Please run:
> npx browserlist@latest --update-db

and the helpful message that npx browserlist@latest --update-db needs to be run continues to report on building/running

So, I’m figuring something needs to be installed somewhere.

Shoud browserlist be installed globally and/or caniuse-lite?

svennergr

Hey,

I noticed the change of colorette to nanocolors.
Since we are currently using Node v15.12.0 and nanocolors only supports «^8.0.0 || ^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0» this change of dependency is breaking our project.

This might be attended and I know, that we probably should upgrade our Node version, but just wanted to let you know about this.

sksankarraj

Browserslist:

Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating

Also Browserslist:

$ npx browserslist@latest --update-db
browserslist: Unknown arguments --update-db.

Usage:
  browserslist
  browserslist "QUERIES"
  browserslist --config="path/to/browserlist/file"
  browserslist --coverage "QUERIES"
  browserslist --coverage=US "QUERIES"
  browserslist --env="environment name defined in config"
  browserslist --stats="path/to/browserlist/stats/file"

How can i fix this issue.

lpoulter

npx browserslist "last 2 firefox versions"
outputs

npx browserslist "last 2 firefox versions"
Browserslist: caniuse-lite is outdated. Please run next command `npm update`
firefox 66
firefox 65

Then I run npm update but get the same message ...caniuse-lite is outdated

I then tried npx browserslist@latest --update-db but get the error Unknown arguments --update-db.

osdiab

If caniuse-lite is included as as transitive dependency of some other package like NextJS, I’m finding that when I run npx browserslist@latest --update-db then it fails with this error:

Command failed: yarn info caniuse-lite --json

I use Yarn 3.0.1. When I add caniuse-lite directly, then instead of that error, I get:

Cannot read property 'version' of undefined

For reference, yarn info caniuse-lite --json returns:

{"value":"caniuse-lite@npm:1.0.30001252","children":{"Version":"1.0.30001252"}}

And if this were to work without caniuse-lite being installed as a direct dependency, the yarn info command just needs the --recursive flag:

yarn info caniuse-lite --recursive --json

Yielding the same result as above.

It seems that the update-db command doesn’t work properly for Yarn Berry. I presume that even if I got past this, it would fail later due to #577 because it doesn’t use yarn up -R caniuse-lite as suggested there.

chanan

When I run the command: npx browserslist --update-db on windows I get: Cannot read property '1' of null

I made sure I updated to the latest: browserslist@4.14.2

bdukes

Description

When using Yarn v2, running npx browserslist@latest --update-db fails with the error Cannot read property '1' of null. Calling yarn run browserslist --update-db gives the error with some more details:

C:inetpubwwwrootblt.yarncachebrowserslist-npm-4.16.1-37866cfd90-56f51464c3.zipnode_modulesbrowserslistupdate-db.js:57
    if (match[1]) return match[1]
             ^

TypeError: Cannot read property '1' of null
    at getCurrentVersion (C:inetpubwwwrootblt.yarncachebrowserslist-npm-4.16.1-37866cfd90-56f51464c3.zipnode_modulesbrowserslistupdate-db.js:57:14)
    at updateDB (C:inetpubwwwrootblt.yarncachebrowserslist-npm-4.16.1-37866cfd90-56f51464c3.zipnode_modulesbrowserslistupdate-db.js:179:17)
    at Object.<anonymous> (C:inetpubwwwrootblt.yarncachebrowserslist-npm-4.16.1-37866cfd90-56f51464c3.zipnode_modulesbrowserslistcli.js:40:3)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.external_module_.Module._load (C:inetpubwwwrootblt.pnp.js:4980:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

package.json

«`json
{
«name»: «blt»,
«version»: «1.0.0»,
«main»: «index.js»,
«license»: «MIT»,
«dependencies»: {
«browserslist»: «^4.16.1»
}
}
«`

yarn.lock

«`yaml
# This file is generated by running «yarn install» inside your project.
# Manual changes might be lost — proceed with caution!

__metadata:
version: 4
cacheKey: 7

«blt@workspace:.»:
version: 0.0.0-use.local
resolution: «blt@workspace:.»
dependencies:
browserslist: ^4.16.1
languageName: unknown
linkType: soft

«browserslist@npm:^4.16.1»:
version: 4.16.1
resolution: «browserslist@npm:4.16.1»
dependencies:
caniuse-lite: ^1.0.30001173
colorette: ^1.2.1
electron-to-chromium: ^1.3.634
escalade: ^3.1.1
node-releases: ^1.1.69
bin:
browserslist: cli.js
checksum: 56f51464c3a3bd9b2aeb75ded1dc3fce5ad91bd6d84187aba812a78ba66b69bc97c2de25a1b7409daac3c0049e979bf0faa6cca4aacce0abcaf3107c250ce3fb
languageName: node
linkType: hard

«caniuse-lite@npm:^1.0.30001173»:
version: 1.0.30001177
resolution: «caniuse-lite@npm:1.0.30001177»
checksum: a43eafb57339093bf3343bc6f57cc21ff6779af44c9a26cc078c3b335db12598179d1333a1cbdd6fd25a2c08cfc550c78538b48fbb3e9c383f46460a1c32a1c0
languageName: node
linkType: hard

«colorette@npm:^1.2.1»:
version: 1.2.1
resolution: «colorette@npm:1.2.1»
checksum: 1cc21ad4b84777a424794f78b6bb6a44b614ae17dcea91762199339f8047598e6d981249eeef7ea588c99eaf062be8fcdcd4866c112998922ed854db6dde96f9
languageName: node
linkType: hard

«electron-to-chromium@npm:^1.3.634»:
version: 1.3.639
resolution: «electron-to-chromium@npm:1.3.639»
checksum: bf8b79e55d50ddcd55b029feb161a56250f3a8980b5c11b47055fd983b9306ad8c69c0501eef9317e0e03f8c5d69fecb97338c08df8c9456953f5741646794c1
languageName: node
linkType: hard

«escalade@npm:^3.1.1»:
version: 3.1.1
resolution: «escalade@npm:3.1.1»
checksum: 1e31ff50d66f47cd0dfffa702061127116ccf9886d1f54a802a7b3bc95b94cab0cbf5b145cc5ac199036df6fd9d1bb24af1fa1bfed87c94879e950fbee5f86d1
languageName: node
linkType: hard

«node-releases@npm:^1.1.69»:
version: 1.1.69
resolution: «node-releases@npm:1.1.69»
checksum: 240b2534f32c8bbdfefb1fb052d2b1c198f3a745141ce245c6c5241a6efefae22d356b66965e5250869013c3c4777050cdca78d39f3b46965166b5f3e02b08d7
languageName: node
linkType: hard

</details>

Here's a zip of the whole project (just `package.json` and various Yarn artifacts): [sscce.zip](https://github.com/browserslist/browserslist/files/5816478/sscce.zip)

rj-david

The latest version of Edge in Windows is 83

Edge in Android is 45. This is causing our system to tag Edge in Android as outdated.

Any suggestions for the rule to use in this case?

carlosjeurissen

As Naver Whale is a browser with a separate WebExtension store and it’s own webExtension adaptions and additions, it is very valuable to have it supported in browserslist.

See: https://whale.naver.com/en/

ElhadiRagabaat

Module build failed: BrowserslistError: Unknown browser query dead

cbdeveloper

I’m working on a project that needs to support IE11.

"browserslist": {
  "production": [
    ">0.2%",
    "ie 11",
    "not dead"
  ]
}

Given the fact that IE11 is such an old browser, does it even make sense to add anything else (more recent than IE11) to the browserslist config?

Couldn’t I just do this?

"browserslist": {
  "production": [
    "ie 11"
  ]
}

raldone01

I use rush to manage a big monorepo.
It has many subprojects.
Rush stores no individual lock file and instead keeps one big lock file in a central location.

While building with rush my subprojects output the following error:

Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

When I cd into a subproject and run pnpx browserslist@latest --update-db it crashes with the following error:
No lockfile found. Run "npm install", "yarn install" or "pnpm install"

DanielHeath

KishaniKandasamy

OS —
Windows 10

Hi,
After running this command also npx browserslist@latest —update-db , I’m getting the same error!
Can anyone Please help me to fix it?

jitendravyas

Mr-M1M3

Hello there, I want to use PostCSS autoprefixer with browserlist. But, will autoprefixer prefix my css properties ONLY for the browsers listed in .browserslistsrc? It’s not an issue, but I couldn’t find the answer anywhere. So, i had to do this. Plz, help me.

vinodtestautomation

@wdio/runner: BrowserslistError: Unknown browser query basedir=$(dirname "$(echo "$0" | sed -e 's. Maybe you are using old Browserslist or made typo in query

Screenshot_1

danylkaaa

Hello! I tried to use your snippet to get coverage when querying my custom stats.

// process.env.BROWSERSLIST_STATS === path.join(__dirname, './browserslist-stats.json');
const browsers = browserslist();
browserslist.coverage(browsers, 'my stats')

But I get this error

TypeError: Cannot read property 'op_mini' of undefined
    at normalizeStats (<...>/node_modules/browserslist/node.js:127:11)
    at Object.getStat (<...>/node_modules/browserslist/node.js:209:12)
    at Function.browserslist.coverage (<....>/node_modules/browserslist/index.js:580:27)

I found that it is caused by this 3f54ebd/index.js#L580 :

// node_modules/browserslist/index.js:580
var customStats = env.getStat(opts)

Which calls getStat() from browserslist/node.js

3f54ebd/node.js#L209

  getStat: function getStat (opts, data) {
    // some ops, but "data" argument is not overrided
    return normalizeStats(data, stats) // <- data is undefined
  }

And then browserlist tries to access the data members w/o checking that the data exists.

3f54ebd/node.js#L127

if (
      versions.length === 1 &&
      data[i] && // <- raises error
      data[i].versions.length === 1
    )

So, I considered to add extra check


function normalizeStats (data, stats) {
  if (!data) {
    data = {};
  }
  //...
}

Today we have a large number of different browsers and even more versions of each. Not a long time ago features were added infrequently, but now you can see them added in almost every release. As a result, different versions of browsers have different features’ support, not to mention a different level of vendor support.

Developers want to use new features, as they often simplify their lives. Using modern development tools, you can use features before they even get an official vendor support by transpiling and using polyfills. Additionally, these tools guarantee that a website will work in all browsers, regardless of a particular feature support. Examples: Autoprefixer and postcss-preset-env for CSS, Babel for JavaScript. But you need to understand that using these tools can increase the bundle’s size.

As a result, we have a website that works in any browser, but it loads slower. Let me remind you that the loading time and fast transitions directly affects UX and popularity. What can be done with it? In fact, we don’t need to transpile and polyfill absolutely every feature — it’s enough to do this only with those that are not supported by current browsers (or relevant to the audience of your website). For example, promises are supported by every browser, excluding the oldest ones.

Browserslist

Browserslist is a convenient tool for describing target browsers just by using simple queries like the following:

last 2 years
> 1%
not dead

Enter fullscreen mode

Exit fullscreen mode

This is an example of .browserslistrc file, that requires: browsers over the past two years, plus browsers with more than 1% of users, and all of these browsers must be «live». You can see specific browsers resolution on browserl.ist. Learn more about queries syntax on project page.

Already mentioned Autoprefixer, postcss-preset-env and babel-preset-env under the hood use Browserslist, and if your project has a Browserslist config, project code will be compiled for these browsers.

At this stage, we can come to the following conclusion: the newer browsers we are targeting, the less bundle size we get. At the same time, we should not forget that in the real world not every single user has the newest browser, and the website should be accessible for all users, or at least for the most of them. What can be done under these considerations?

Browser targeting variants

1. Limited targeting

By default, if there is no config in the project, Browserslist will use default browsers. This query is an alias for > 0.5%, last 2 versions, Firefox ESR, not dead. In general, you can stop on this query, and over the time, the browsers matching this query will start to support most of the current features.

But you can target a considerable number of browsers by following these rules: exclude legacy and unpopular ones, consider more or less relevant versions of browsers. Sounds simple, but actually it’s not. You need to carefully balance the Browserslist config to cover most of the audience.

2. Audience analysis

If your website implies only certain regions’ support, then you can try using a query like > 5% in US, which returns suitable browsers based on the usage statistics by specified country.

Browserslist family is full of various additional tools, one of them is Browserslist-GA (there is also browserslist-adobe-analytics), which allows you to export data from analytics service about your users’ browsers statistics. After that, it becomes possible to use this data in Browserslist config and make queries based on it:

> 0.5% in my stats

Enter fullscreen mode

Exit fullscreen mode

For example, if you can update this data on every deploy, then your website will always be built for current browsers used by your audience.

3. Differential resource loading

In March 2019 Matthias Binens from Google proposed to add differential script loading (further DSL) to browsers:

<script type="module"
        srcset="2018.mjs 2018, 2019.mjs 2019"
        src="2017.mjs"></script>
<script nomodule src="legacy.js"></script>

Enter fullscreen mode

Exit fullscreen mode

Until now, his proposal stays only a proposal, and it is unknown whether this will be implemented by vendors or not. But the concept is understandable, and Browserslist family has tools that you can use to implement something similar, one of them is browserslist-useragent. This tool allows you to check if the browser’s User-Agent fits your config.

Browserslist-useragent

There are already several articles on this topic, here is an example of one — «Smart Bundling: How To Serve Legacy Code Only To Legacy Browsers». We will briefly go over the implementation. First, you need to to configure your build process to output two versions of the bundles for the modern and legacy browsers, for example. Here, Browserslist will help you with it ability to declare several environments in a configuration file:

[modern]
last 2 versions
last 1 year
not safari 12.1

[legacy]
defaults

Enter fullscreen mode

Exit fullscreen mode

Next, you need to configure the server to send the right bundle to the user’s browser:

/* … */
import { matchesUA } from 'browserslist-useragent'
/* … */
app.get('/', (request, response) => {
    const userAgent = request.get('User-Agent')
    const isModernBrowser = matchesUA(userAgent, {
        env: 'modern',
        allowHigherVersions: true
    })
    const page = isModernBrowser
        ? renderModernPage(request)
        : renderLegacyPage(request)

    response.send(page)
})

Enter fullscreen mode

Exit fullscreen mode

Thus, the website will send a lightweight bundle to users with modern browsers, resulting in a faster loading time, while saving accessibility for other users. But, as you can see, this method requires your own server with special logic.

Module/nomodule

With browsers’ support of ES-modules, there is a way to implement DSL on client side:

<script type="module" src="index.modern.js"></script>
<script nomodule src="index.legacy.js"></script>

Enter fullscreen mode

Exit fullscreen mode

This pattern is called module/nomodule, and it’s based on the fact that legacy browsers without ES-modules’ support will not handle scripts with the type module, since this type is unknown to them. So browsers that support ES-modules will load scripts with the type module and ignore scripts with the nomodule attribute. Browsers with ES-modules’ support can be specified by the following config:

[esm]
edge >= 16
firefox >= 60
chrome >= 61
safari >= 11
opera >= 48

Enter fullscreen mode

Exit fullscreen mode

The biggest advantage of the module/nomodule pattern is that you don’t need to own a server — everything works completely on client side. Differential stylesheet loading cannot be done this way, but you can implement resource loading using JavaScript:

if ('noModule' in document.createElement('script')) {
    // Modern browsers
} else {
    // Legacy browsers
}

Enter fullscreen mode

Exit fullscreen mode

One of the disadvantages: this pattern has some cross-browser problems. Also, browsers supporting ES-modules already have new features with different levels of support, for example, optional chaining operator. With the addition of new features, this DSL variation will lose its relevance.

You can read more about the module/nomodule pattern in the article «Modern Script Loading». If you are interested in this DSL variant and would like to try it in your project, then you can use Webpack plugin: webpack-module-nomodule-plugin.

Browserslist-useragent-regexp

More recently, another tool was created for Browserslist: browserslist-useragent-regexp. This tool allows you to get a regular expression from config to check browser’s User-Agent. Regular expressions work in any JavaScript runtime, which makes it possible to check the browser’s User-Agent not only on server side, but also on client side. Thus, you can implement a working DSL in a browser:

// last 2 firefox versions
var modernBrowsers = /Firefox/(73|74).0.d+/
var script = document.createElement('script')

script.src = modernBrowsers.test(navigator.userAgent)
    ? 'index.modern.js'
    : 'index.legacy.js'

document.all[1].appendChild(script)

Enter fullscreen mode

Exit fullscreen mode

Another fact is that generated regexpes are faster than matchesUA function from browserslist-useragent, so it makes sense to use browserslist-useragent-regexp on server side too:

> matchesUA('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0', { browsers: ['Firefox > 53']})
first time: 21.604ms
> matchesUA('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0', { browsers: ['Firefox > 53']})
warm: 1.742ms

> /Firefox/(5[4-9]|6[0-6]).0.d+/.test('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0')
first time: 0.328ms
> /Firefox/(5[4-9]|6[0-6]).0.d+/.test('Mozilla/5.0 (Windows NT 10.0; rv:54.0) Gecko/20100101 Firefox/54.0')
warm: 0.011ms

Enter fullscreen mode

Exit fullscreen mode

All in all, this looks very cool, but there should be an easy way to integrate it into the project’s building process… And in fact there is!

Browserslist Differential Script Loading

Bdsl-webpack-plugin is a Webpack plugin paired with html-webpack-plugin and using browserslist-useragent-regexp, which helps automate DSL addition to the bundle. Here is an example Webpack config for this plugin usage:

const {
    BdslWebpackPlugin,
    getBrowserslistQueries,
    getBrowserslistEnvList
} = require('bdsl-webpack-plugin')

function createWebpackConfig(env) {
    return {
        name: env,
        /* … */
        module: {
            rules: [{
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    presets: [
                        ['@babel/preset-env', {
                            /* … */
                            targets: getBrowserslistQueries({ env })
                        }]
                    ],
                    plugins: [/* … */]
                }
            }]
        },
        plugins: [
            new HtmlWebpackPlugin(/* … */),
            new BdslWebpackPlugin({ env })
        ]
    };
}

module.exports = getBrowserslistEnvList().map(createWebpackConfig)

Enter fullscreen mode

Exit fullscreen mode

This example exports several configs to output bundles for each environment from Browserslist config. As an output, we get HTML file with built-in DSL script:

<!DOCTYPE html>
<html>
    <head>
        <title>Example</title>
        <script>function dsl(a,s,c,l,i){c=dsld.createElement('script');c.async=a[0];c.src=s;l=a.length;for(i=1;i<l;i++)c.setAttribute(a[i][0],a[i][1]);dslf.appendChild(c)}var dsld=document,dslf=dsld.createDocumentFragment(),dslu=navigator.userAgent,dsla=[[]];if(/Firefox/(73|74).0.d+/.test(dslu))dsl(dsla[0],"/index.modern.js")
else dsl(dsla[0],"/index.legacy.js");dsld.all[1].appendChild(dslf)</script>
    </head>
    <body></body>
</html>

Enter fullscreen mode

Exit fullscreen mode

In addition to scripts loading, there is support for styles loading. It is also possible to use this plugin on server side.

But, unfortunately, there are some nuances that you should know before starting to use bdsl-webpack-plugin: since scripts and styles loading is initialized by JavaScript, they are loaded asynchronously without render being blocked, and etc. For example, in case of the scripts — this means an inability to use defer attribute, and for the styles — the necessity to hide page content until styles are fully loaded. You can investigate how to get around these nuances, and other features of this plugin yourself, see documentation and usage examples.

Dependencies transpilation

Following the aforesaid part of the article, we’ve learned several ways of using Browserslist to reduce the size of the website’s own code, but the other part of the bundle is its dependencies. In web applications, the size of the dependencies in the final bundle can take up a significant part.

By default, the build process should avoid the transpilation of dependencies, otherwise the build will take a lot of time. Also dependencies, utilizing unsupported syntax, are usually distributed already transpiled. In practice, there are three types of packages:

  1. with transpiled code;
  2. with transpiled code and sources;
  3. with code with current syntax only for modern browsers.

With the first type, obviously, nothing can be done. The second — you need to configure the bundler to work only with the sources from the package. The third type — in order to make it work (even with not very relevant browsers) you still have to transpile it.

Since there is no common way to make packages with several versions of the bundle, I will describe how I suggest to approach this problem: the regular transpiled version has .js extension, the main file is written to the main field of package.json file, while, on the contrary, the version of the bundle without transpilation has .babel.js extension, and the main file is written in the raw field. Here is a real example — Canvg package. But you can do it another way, for example, here is how it’s done in Preact package — the sources are located in the separate folder, and package.json has a source field.

To make Webpack work with such packages, you need to modify resolve config section:

{
    /* … */
    resolve: {
        mainFields: [
            'raw',
            'source',
            'browser',
            'module',
            'main'
        ],
        extensions: [
            '.babel.js',
            '.js',
            '.jsx',
            '.json'
        ]
    }
    /* … */
}

Enter fullscreen mode

Exit fullscreen mode

This way, we tell Webpack how to lookup files in packages that are used at build time. Then we just need to configure babel-loader:

{
    /* … */
    test: /.js$/,
    exclude: _ => /node_modules/.test(_) && !/(node_modules/some-modern-package)|(.babel.js$)/.test(_),
    loader: 'babel-loader'
    /* … */
}

Enter fullscreen mode

Exit fullscreen mode

The logic is straightforward: we ask to ignore everything from node_modules, except specific packages and files with specific extensions.

Results

I’ve measured DevFest Siberia 2019 a website’s bundle size and loading time before and after applying differential loading together with dependencies transpilation:

Regular network Regular 4G Good 3G
Without DSL
Average loading time 1,511 ms 4,240 ms 8,696 ms
Fastest loading time 1,266 ms 3,366 ms 8,349 ms
Encoded size 292 kB
Decoded size 1.08 MB
bdsl-webpack-plugin, 3 environments (modern, actual, legacy)
Average loading time 1,594 ms 3,409 ms 8,561 ms
Fastest loading time 1,143 ms 3,142 ms 6,673 ms
Encoded size 218 kB
Decoded size 806 kB

The result is a decreased loading time and bundle size reduction by ≈20%, read more detailed report. You can also make measurements by yourself — you can find the required script in the bdsl-webpack-plugin repository.

Sources

  • Smart Bundling: How To Serve Legacy Code Only To Legacy Browsers, Shubham Kanodia
  • Modern Script Loading, Jason Miller

Editor

  • Vadim Makeev
  • Irina Pitaeva

Translation

  • Dan Onoshko
  • Anton Borisov
  • Kat Sas

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Error no bootable partition found
  • Error in render typeerror cannot convert undefined or null to object
  • Error no boot disk has been detected or the disk has failed что делать
  • Error in procedure galaxy next day label 16
  • Error no boot disk has been detected or the disk has failed как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии