Файл babel json что это
В своем предыдущем посте я писал о том зачем и почему нужно было сделать pybabel-hbs, экстрактор строк gettext из шаблонов handlebars.
Чуть позже появилась необходимость извлекать так же из json.
Так появился pybabel-json.
pip install pybabel-json либо на github
Там использовался лексер джаваскрипта встроенный в babel, но нюансы так же имелись, но пост не об этом, написанное там менее интересное, чем было в hbs плагине и вряд ли нуждается в заострении внимания.
Этот пост о том, как же в целом выглядит полный набор для локализации, от и до, что делать с данными из БД, либо из другого не совсем статичного места.
От и до включает в себя:
(должен заметить — что ни один пункт не является обязательным, все это достаточно легко подключается к любому приложению только частично и по необходимости)
— Babel. Набор утилит для локализации приложений.
— Grunt. Менеджер задач(task-ов),
— coffeescript. В представлении не нуждается, весь клиенстский код написан на coffee, и из него тоже нужно извлекать строки.
— handlebars — темплейты
— json — хранилища строк
— Jed. gettext клиент для js
— po2json. Утилита для перевода .po файлов в .json формат поддерживаемый Jed-ом
gettext — изначально набор утилит для локализации приложений, сегодня же я бы назвал gettext еще и общепринятым форматом. (не путать с единственным)
Минимальную суть можно описать так, есть строки на английском, которые проходят через некую функцию gettext и на выходе превращаются в строку на нужном языке, сохраняя правила языка касающиеся разного склонения для множественных чисел + возможность указать контекст и домэин.
Важно заметить, что именно строки, они же ключи, а не константа USER_WELCOME_MESSAGE где-то превращающаяся в текст.
Контекст нужен далеко не всем и в своих плагинах babel-а я его пока что не реализовывал, так как без надобности, пулл реквесты приветствуются
О домэине будет пара слов позже.
А вот ngettext — штука безусловно необходимая многим, если не всем.
И тут же о мифах.
Этот простой пример должен показать всем любителям языковых констант а-ля «USER_WELCOME_MESSAGE», которые потом отдаются на перевод, что все не так просто как кажется на первый взгляд.
За то, какая строка будет выбрана решают правила предопределенные и описанные в babel:
Например это для английского:
А это для русского:
Велик и Могуч :)
Не нужно бояться, в ручную этого писать для, например, японского не прийдется.
Почему? Все дело в яблоках.
Множественное число не всегда в единственном числе, а единственное число не всегда для единицы.
Английский в этом плане прост, русский же нет.
ngettext по умолчанию, как ключ ожидает именно английский язык. Более того, ngettext на вход принимает только два параметра — единственное число и множественное. А не массив множественных чисел.
Кстати, заодно покажу пример, того как выглядят .po файлы для английского и для русского
Т.е кол-во результирующих строк зависит от конфигурации языка. Может быть и есть язык, в котором этак десяток форм множественного числа…
Все те, у кого до сих пор 3 яблок должны быть мотивированы для того что бы начать
Тяжелая часть позади.
Осталось:
— Изменить в коде весь текст на вызовы gettext
— Натравить babel на код
— На основе полученного .pot файла сделать .po файл соответствующий каждому нужному языку.
Настройка Babel
Babel — это транспилятор, обладающий огромными возможностями. В частности, он умеет преобразовывать LESS в CSS, JSX в JS, TypeScript в JS. Мы будем использовать с ним лишь две конфигурации — react и env (их ещё называют «пресетами»). Babel можно настраивать по-разному, в частности, речь идёт о средствах командной строки, о специальном файле с настройками, о стандартном файле package.json . Нас устроит последний вариант. Добавим в package.json следующий раздел:
Благодаря этим настройкам Babel будет знать о том, какие пресеты ему нужно использовать. Теперь настроим Webpack на использование Babel.
Webpack
Webpack используется для компиляции JavaScript-модулей. Этот инструмент часто называют «бандлером» (от bundler) или «сборщиком модулей». После его установки работать с ним можно, используя интерфейс командной строки или его API. Если вы не знакомы с Webpack — рекомендуется почитать об основных принципах его работы и посмотреть его сравнение с другими сборщиками модулей. Вот как, на высоком уровне, выглядит то, что делает Webpack.
Работа Webpack
Webpack берёт всё, от чего зависит проект, и преобразует это в статические ресурсы, которые могут быть переданы клиенту. Упаковка приложений — это очень важно, так как большинство браузеров ограничивает возможности по одновременной загрузке ресурсов. Кроме того, это позволяет экономить трафик, отправляя клиенту лишь то, что ему нужно. В частности, Webpack использует внутренний кэш, благодаря чему модули загружаются на клиент лишь один раз, что, в итоге, приводит к ускорению загрузки сайтов.
Настройка Webpack
Webpack можно настраивать разными способами. В частности, настройки этого инструмента могут принимать вид аргументов командной строки или присутствовать в проекте в виде конфигурационного файла с именем webpack.config.js . В нём нужно описать и экспортировать объект, содержащий настройки. Мы начнём настройку этого файла с описания объекта, выглядящего так (мы будем рассматривать его по частям, а ниже приведём его полный код):
Свойство entry задаёт главный файл с исходным кодом проекта. Значение свойства mode указывает на тип окружения для компиляции (в нашем случае это окружение разработки — development ) и на то, куда нужно поместить скомпилированный файл.
А что собственно переводить?
Вопрос не так прост как кажется на первый взгляд:
Часть простая — шаблоны и код.
Django и flask — есть экстракторы из шаблонов
Python и javascript поддерживаются babel изначально
handlebars и json — пришлось сделать, ссылки в начале поста.
Для coffeescript — рецепт далее
Для всего остального — гугл в помощь
Еще раз, часть простая — код, для этого все строки нужно обернуть в вызовы gettext/ngettext в соответствии с форматом, который требует каждый из экстракторов. Как правило они так же предоставляют возможность переопределить какую функцию должны использовать
Например, у меня так:
trans и ntrans указан для джаваскрипта, а __ для питона, в котором эта функция используется для прозрачной передачи строки(об этом позже)
Т.е, все
print(«apple») нужно переделать в print(ngettext(«apple»))
А все
print(«I have %s apples») в print(ngettext(«I have %s apple»,«I have %s apples»,num_of_apples)%num_of_apples)
Тут должен заметить, чего и всем желаю, что никогда не использую и не рекомендую использовать неименнованные параметры.
В моем случае — только именнованые, то бишь выглядить это должно так:
Используется стандартный gettext, для flask и джанго есть свои обертки
JSON хранилище строк:
Оффтоп: Пояснение к этому формату в документации к pybabel-json
Думаю не сложно было заметить, что num_of_apples повторяется каждый вызов два раза.
Причина тому, что один раз он передается в качестве аргумента для ngettext, по которому решается какая строка используется, а второй раз в качестве параметра для строки, на ряду с другими возможными параметрами подставляемыми в эту строку.
— Как я уже говорил — это простая часть, завернуть существующий текст.
Далее нужно
1) Изменить все кнопки на которых надписи на кнопки с текстами. Все знают что кнопки с текстом это плохо. Но часто это приходится принять, так как так быстрее, а дизайнер хочет именно так :)
— С этим пунктом все должно быть ясно — нудно, но необходимо
С данными в БД ситуация похожая, в систему билда-пуша-деплоя, что бы то ни было (ведь что-то у вас есть)? на том же уровне, где будут комманды для сборки всего и вся babel-ом нужно перед этими самыми командами добавить скрипт который будет извлекать все нужные данные из БД и собирать подобный json, запущенный следом babel уже соберет данные.
Само собой — такие файлы следует добавить в .gitignore либо аналог чего-бы-там ни было, в общем, чтоб в source control не попадало
Все строки, которые получены подобным образом должны проходить через вызов gettext функции
Т.е если это в python, то gettext(), в js Jed либо прокси-методы приведенные ранее
Так же следует заметить, что порой хочется сделать в обратном порядке. Либо необходимо сделать в обратном порядке.
Т.е определить в коде что строка должна переводиться, но непосредственно сам перевод будет запущен в другом месте.
Приведу пример на python:
Если вы напишите подобный код, то вы рискуете получить созданную копию класса в английском исполнении, если класс создался при запуске сервера, либо например китайскую версию, если создание было динамическим но кешируемым при первом заходе
В таких случаях хочется отметить для перевода, но перевести в нужном месте
Нужное место это создание объекта, а не класса
т.е
Т.е — сборщик строк определит __ как строку для перевода, сама функция не делает ничего, а перевод будет запущен в нужное время.
Таким образом все в одном месте и выглядит красиво.
Это касается многих языков, в том числе coffeescript и джаваскрипт, если вы пишете под node.js.
Для браузера это менее актуально, так как даже в момент создания класса уже должно быть известно для какого языка создавать.
Но в любом случае — правильнее перевести в конструкторе, а не в момент создания класса.
Вроде бы обошел все известные мне возможности направления перевода, допустим все это сделано.
Итоги
В этом материале приведён обзор настройки Webpack и Babel для их использования в React-проектах. На самом деле, на той базе, которую мы сегодня разобрали, можно создавать гораздо более сложные конфигурации. Например, вместо CSS можно воспользоваться LESS, вместо обычного JS писать на TypeScript. При необходимости можно, например, настроить минификацию файлов и многое другое. Конечно, если сегодня состоялось ваше первое знакомство с процессом самостоятельной настройки React-проектов, вам может показаться, что всё это очень сложно и куда легче воспользоваться готовым шаблоном. Однако после того, как вы немного в этом разберётесь, вы поймёте, что некоторое увеличение сложности настроек даёт вам большую свободу, позволяя настраивать свои проекты именно так, как вам это нужно, не полагаясь полностью на некие «стандартные» решения и снизив свою зависимость от них.
Настройка сервера разработки
Теперь, в файле webpack.config.js , настроим сервер разработки:
Свойство contentBase объекта с настройками devServer указывает на папку, в которой расположены наши ресурсы и файл index.html . Свойство port позволяет задать порт, который будет прослушивать сервер. Свойство watchContentBase позволяет реализовать наблюдение за изменениями файлов в папке, задаваемой свойством contentBase .
Вот полный код файла webpack.config.js :
Теперь внесём в package.json , в раздел scripts , команду для запуска сервера разработки и команду для запуска сборки проекта:
Сейчас всё готово к тому, чтобы запустить сервер разработки следующей командой:
Для того чтобы собрать проект воспользуйтесь следующей командой:
Настройка Webpack на работу с Babel
Тут мы воспользуемся библиотекой babel-loader, которая позволит использовать Babel с Webpack. Фактически, речь идёт о том, что Babel сможет перехватывать и обрабатывать файлы до их обработки Webpack.
▍CSS-файлы
Добавим в массив rules объекта module настройки для обработки CSS-файлов:
Задачу обработки CSS-файлов мы будем решать средствами style-loader и css-loader. Свойство use может принимать массив объектов или строк. Загрузчики вызываются, начиная с последнего, поэтому наши файлы сначала будут обработаны с помощью css-loader. Мы настроили это средство, записав в свойство modules объекта options значение true . Благодаря этому CSS-стили будут применяться лишь к тем компонентам, в которые они импортированы. Css-loader разрешит команды импорта в CSS-файлах, после чего style-loader добавит то, что получится, в форме тега style , в разделе страницы:
Babel
Babel — это транспилятор, который, в основном, используется для преобразования конструкций, принятых в свежих версиях стандарта ECMAScript, в вид, понятный как современным, так и не самым новым браузерам и другим средам, в которых может выполняться JavaScript. Babel, кроме того, умеет преобразовывать в JavaScript и JSX-код, используя @babel/preset-react.
Именно благодаря Babel мы, при разработке React-приложений, можем пользоваться JSX. Например, вот код, в котором используется JSX:
Выглядит такой код аккуратно, он понятен, его легко читать и редактировать. Глядя на него, сразу можно понять, что он описывает компонент, возвращающий элемент , в котором содержится текст Hello world! , выделенный жирным шрифтом. А вот пример кода, делающего то же самое, в котором JSX не используется:
Преимущества первого примера перед вторым очевидны.
▍Статические ресурсы
Продолжим работу над объектом настроек module , описав в нём правила обработки статических ресурсов:
Если система встретит файл с расширением PNG, SVG, JPG или GIF, то для обработки такого файла будет использован file-loader. Обработка таких файлов важна для правильной подготовки и оптимизации проекта.
А что собственно переводить?
Вопрос не так прост как кажется на первый взгляд:
Часть простая — шаблоны и код.
Django и flask — есть экстракторы из шаблонов
Python и javascript поддерживаются babel изначально
handlebars и json — пришлось сделать, ссылки в начале поста.
Для coffeescript — рецепт далее
Для всего остального — гугл в помощь
Еще раз, часть простая — код, для этого все строки нужно обернуть в вызовы gettext/ngettext в соответствии с форматом, который требует каждый из экстракторов. Как правило они так же предоставляют возможность переопределить какую функцию должны использовать
Например, у меня так:
trans и ntrans указан для джаваскрипта, а __ для питона, в котором эта функция используется для прозрачной передачи строки(об этом позже)
Т.е, все
print(«apple») нужно переделать в print(ngettext(«apple»))
А все
print(«I have %s apples») в print(ngettext(«I have %s apple»,«I have %s apples»,num_of_apples)%num_of_apples)
Тут должен заметить, чего и всем желаю, что никогда не использую и не рекомендую использовать неименнованные параметры.
В моем случае — только именнованые, то бишь выглядить это должно так:
Используется стандартный gettext, для flask и джанго есть свои обертки
JSON хранилище строк:
Оффтоп: Пояснение к этому формату в документации к pybabel-json
Думаю не сложно было заметить, что num_of_apples повторяется каждый вызов два раза.
Причина тому, что один раз он передается в качестве аргумента для ngettext, по которому решается какая строка используется, а второй раз в качестве параметра для строки, на ряду с другими возможными параметрами подставляемыми в эту строку.
— Как я уже говорил — это простая часть, завернуть существующий текст.
Далее нужно
1) Изменить все кнопки на которых надписи на кнопки с текстами. Все знают что кнопки с текстом это плохо. Но часто это приходится принять, так как так быстрее, а дизайнер хочет именно так :)
— С этим пунктом все должно быть ясно — нудно, но необходимо
С данными в БД ситуация похожая, в систему билда-пуша-деплоя, что бы то ни было (ведь что-то у вас есть)? на том же уровне, где будут комманды для сборки всего и вся babel-ом нужно перед этими самыми командами добавить скрипт который будет извлекать все нужные данные из БД и собирать подобный json, запущенный следом babel уже соберет данные.
Само собой — такие файлы следует добавить в .gitignore либо аналог чего-бы-там ни было, в общем, чтоб в source control не попадало
Все строки, которые получены подобным образом должны проходить через вызов gettext функции
Т.е если это в python, то gettext(), в js Jed либо прокси-методы приведенные ранее
Так же следует заметить, что порой хочется сделать в обратном порядке. Либо необходимо сделать в обратном порядке.
Т.е определить в коде что строка должна переводиться, но непосредственно сам перевод будет запущен в другом месте.
Приведу пример на python:
Если вы напишите подобный код, то вы рискуете получить созданную копию класса в английском исполнении, если класс создался при запуске сервера, либо например китайскую версию, если создание было динамическим но кешируемым при первом заходе
В таких случаях хочется отметить для перевода, но перевести в нужном месте
Нужное место это создание объекта, а не класса
т.е
Т.е — сборщик строк определит __ как строку для перевода, сама функция не делает ничего, а перевод будет запущен в нужное время.
Таким образом все в одном месте и выглядит красиво.
Это касается многих языков, в том числе coffeescript и джаваскрипт, если вы пишете под node.js.
Для браузера это менее актуально, так как даже в момент создания класса уже должно быть известно для какого языка создавать.
Но в любом случае — правильнее перевести в конструкторе, а не в момент создания класса.
Вроде бы обошел все известные мне возможности направления перевода, допустим все это сделано.
Работа над проектом
Поместим в файл index.html нашего проекта, расположенный в папке dist , следующий код:
Обратите внимание на тег script , присутствующий в этом файле. Он указывает на файл main.js , который будет получен в ходе компиляции проекта. Элемент с идентификатором root мы будем использовать для вывода React-приложения.
Теперь установим пакеты react и react-dom:
Внесём в index.js следующий код:
Это — стандартный код для подобных файлов React-приложений. Тут мы подключаем библиотеки, подключаем файл компонента и выводим приложение в тег с идентификатором root .
Склеиваем все вместе
Теперь можно попытаться все это собрать, тут есть несколько простых шагов:
0) Создать пустой каталог оригинальных строк, чтоб не ругался в дальнейшем на отсутствие файла
1) Создать .po файлы целевых языков Это делается 1 раз и не должно включаться в билд. .po файлы это файлы содержащие как оригинальные строки, так и перевод к ним, для каждого языка.
2) Создать/обновить .pot файл — основное хранилище строк Это так же не должно включаться в билд, а нужно запускать когда необходимо получить новые .po файлы, которые будут отправлены на перевод.
Тут будет не лишним показать пример babel.cfg файла, это mapping файл, указывающий на то, чем и из каких файлов извлекать строки:
3) Прогнать все .po файлы через po2json, для получения .json, которых и примет Jed.
Вот это можно и нужно включить в build.
Чего нельзя делать — так это пускать в git, им там не место.
Можно конечно и намного проще, после установки po2json (npm install po2json) включить нечто подобное в build script:
Не вошедшие в поток мысли, но имеющие смысл заострить на них внимание моменты
На протяжении поста несколько раз обещал «об этом позже», но для позже подходящего места не нашлось.
Как например:
coffeescript не имеет собственного экстрактора, т.к при билде статики coffeescript компилируется(либо транслируется) в javascript.
Поэтому достаточно запустить сборку .js строк после перевода в джаваскрипт
В моем случае все даже немного не так, рядом с каждым файлов coffee лежит файл coffee_js, который создается с помощью grunt watch в момент редактирования (и перезапускает дев статику, но это тема для отдельного поста :) ), эти файлы само собой вне гита. Вот из них строки и вытаскиваются
— Еще было упоминание о домэинах.
Домэины в конечном итоге это разные файлы, messages.pot/messages.po = домэин messages
Можно создавать несколько домэинов, все домэины привязывать к Jed инстансу, либо создавать несколько разных Jed инстанцев и перенаправлять в них
Но для этого нужно расширять хелперы handlebars либо любую другую обертку… У меня такой необходимости еще не было никогда, а как правило предпочитаю не делать ничего лишнего заранее :)
— Небольшая сноска к тексу во вступительном блоке
Тут следует понимать, что в вызове ngettext необходимо писать именно «you have %(apples_count)d apples», а не «you have one apple»
Т.к в и в случае одного и в случае 21-ого конечная строка должна быть в первой форме — т.е «У вас %d яблоко»
Если что-то упустил, не описал и т.д. — пишите в комментах, дополню.
Цели разобрать детально каждую утилиту не стояло, цель была рассказать о существовании оных и о том, как именно и почему именно так они работают вместе.
Остальному найдется место в комментариях
There are quite a few tools in the Babel toolchain that try to make it easy for you to use Babel whether you're an "end-user" or building an integration of Babel itself. This will be a quick introduction to those tools and you can read more about them in the "Usage" section of the docs.
If you're using a framework, the work of configuring Babel might be different or actually already handled for you. Check out our interactive setup guide instead.
This guide will show you how to compile your JavaScript application code that uses ES2015+ syntax into code that works in current browsers. That will involve both transforming new syntax and polyfilling missing features.
The entire process to set this up involves:
Running these commands to install the packages:
Creating a config file named babel.config.json (requires v7.8.0 and above) in the root of your project with this content:
The browsers list above is just an arbitrary example. You will have to adapt it for the browsers you want to support. See here for more @babel/preset-env options.
Or babel.config.js if you are using an older Babel version
And running this command to compile all your code from the src directory to lib :
You can use the npm package runner that comes with npm@5.2.0 to shorten that command by replacing ./node_modules/.bin/babel with npx babel
Read on for a step-by-step explanation of how this works and an introduction to each of the tools used.
Basic usage with CLI
All the Babel modules you'll need are published as separate npm packages scoped under @babel (since version 7). This modular design allows for various tools each designed for a specific use case. Here we'll look at @babel/core and @babel/cli .
The core functionality of Babel resides at the @babel/core module. After installing it:
you can require it directly in your JavaScript program and use it like this:
As an end-user though, you'll probably want to install other tools that serve as an interface to @babel/core and integrate well with your development process. Even so, you might still want to check its documentation page to learn about the options, most of which can be set from the other tools as well.
@babel/cli is a tool that allows you to use babel from the terminal. Here's the installation command and a basic usage example:
This will parse all the JavaScript files in the src directory, apply any transformations we have told it to, and output each file to the lib directory. Since we haven't told it to apply any transformations yet, the output code will be identical to the input (exact code styling is not preserved). We can specify what transformations we want by passing them as options.
We used the --out-dir option above. You can view the rest of the options accepted by the cli tool by running it with --help . But the most important to us right now are --plugins and --presets .
Transformations come in the form of plugins, which are small JavaScript programs that instruct Babel on how to carry out transformations to the code. You can even write your own plugins to apply any transformations you want to your code. To transform ES2015+ syntax into ES5 we can rely on official plugins like @babel/plugin-transform-arrow-functions :
Now any arrow functions in our code will be transformed into ES5 compatible function expressions:
That's a good start! But we also have other ES2015+ features in our code that we want transformed. Instead of adding all the plugins we want one by one, we can use a "preset" which is just a pre-determined set of plugins.
Just like with plugins, you can create your own presets too to share any combination of plugins you need. For our use case here, there's an excellent preset named env .
Without any configuration, this preset will include all plugins to support modern JavaScript (ES2015, ES2016, etc.). But presets can take options too. Rather than passing both cli and preset options from the terminal, let's look at another way of passing options: configuration files.
There are a few different ways to use configuration files depending on your needs. Be sure to read our in-depth guide on how to configure Babel for more information.
For now, let's create a file called babel.config.json (requires v7.8.0 and above) with the following content:
Now the env preset will only load transformation plugins for features that are not available in our target browsers. We're all set for syntax. Let's look at polyfills next.
🚨 As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions):
The @babel/polyfill module includes core-js and a custom regenerator runtime to emulate a full ES2015+ environment.
This means you can use new built-ins like Promise or WeakMap , static methods like Array.from or Object.assign , instance methods like Array.prototype.includes , and generator functions (when used alongside the regenerator plugin). The polyfill adds to the global scope as well as native prototypes like String in order to do this.
For library/tool authors this may be too much. If you don't need the instance methods like Array.prototype.includes you can do without polluting the global scope altogether by using the transform runtime plugin instead of @babel/polyfill .
To go one step further, if you know exactly what features you need polyfills for, you can require them directly from core-js.
Since we're building an application we can just install @babel/polyfill :
Note the --save option instead of --save-dev as this is a polyfill that needs to run before your source code.
Now luckily for us, we're using the env preset which has a "useBuiltIns" option that when set to "usage" will practically apply the last optimization mentioned above where you only include the polyfills you need. With this new option the configuration changes like this:
Babel will now inspect all your code for features that are missing in your target environments and include only the required polyfills. For example this code:
would turn into this (because Edge 17 doesn't have Promise.prototype.finally ):
If we weren't using the env preset with the "useBuiltIns" option set to "usage" (defaults to "false") we would've had to require the full polyfill only once in our entry point before any other code.
Then import core-js (to polyfill ECMAScript features) and regenerator runtime (needed only if you are transpiling generators) first, in our entry file to emulate a full ES2015+ environment since @babel/polyfill has been deprecated:
We used @babel/cli to run Babel from the terminal, @babel/polyfill to polyfill all the new JavaScript features, and the env preset to only include the transformations and polyfills for the features that we use and that are missing in our target browsers.
For more information on setting up Babel with your build system, IDE, and more, check out our interactive setup guide.
Babel has two parallel config file formats, which can be used together, or independently.
Version | Changes |
---|---|
v7.8.0 | Support .babelrc.mjs and babel.config.mjs |
v7.7.0 | Support .babelrc.json , .babelrc.cjs , babel.config.json , babel.config.cjs |
- Project-wide configuration
- babel.config.json files, with the different extensions ( .js , .cjs , .mjs )
- .babelrc.json files, with the different extensions ( .babelrc , .js , .cjs , .mjs )
- package.json files with a "babel" key
New in Babel 7.x, Babel has a concept of a "root" directory, which defaults to the current working directory. For project-wide configuration, Babel will automatically search for a babel.config.json file, or an equivalent one using the supported extensions, in this root directory. Alternatively, users can use an explicit "configFile" value to override the default config file search behavior.
Because project-wide config files are separated from the physical location of the config file, they can be ideal for configuration that must apply broadly, even allowing plugins and presets to easily apply to files in node_modules or in symlinked packages, which were traditionally quite painful to configure in Babel 6.x.
The primary downside of this project-wide config is that, because it relies on the working directory, it can be more painful to use in monorepos if the working directory is not the monorepo root. See the monorepo documentation for examples of how to use config files in that context.
Project-wide configs can also be disabled by setting "configFile" to false .
Babel loads .babelrc.json files, or an equivalent one using the supported extensions, by searching up the directory structure starting from the "filename" being compiled (limited by the caveats below). This can be powerful because it allows you to create independent configurations for subsections of a package. File-relative configurations are also merged over top of project-wide config values, making them potentially useful for specific overrides, though that can also be accomplished through "overrides".
There are a few edge cases to consider when using a file-relative config:
- Searching will stop once a directory containing a package.json is found, so a relative config only applies within a single package.
- The "filename" being compiled must be inside of "babelrcRoots" packages, or else searching will be skipped entirely.
These caveats mean that:
- .babelrc.json files only apply to files within their own package
- .babelrc.json files in packages that aren't Babel's 'root' are ignored unless you opt in with "babelrcRoots".
See the monorepo documentation for more discussion on how to configure monorepos that have many packages. File-relative configs can also be disabled by setting "babelrc" to false .
6.x vs 7.x .babelrc loading
Users coming from Babel 6.x will likely trip up on these two edge cases, which are new in Babel 7.x. These two restrictions were added to address common footguns in Babel 6.x:
- .babelrc files applied to node_modules dependencies, often unexpectedly.
- .babelrc files failed to apply to symlinked node_modules when people expected them to behave like normal dependencies.
- .babelrc files in node_modules dependencies would be detected, even though the plugins and presets inside they were generally not installed, and may not even be valid in the version of Babel compiling the file.
These cases will primarily cause issues for users with a monorepo structure, because if you have
the config will now be entirely ignored, because it is across a package boundary.
One alternative would be to create a .babelrc in each sub-package that uses "extends" as
Unfortunately, this approach can be a bit repetitive, and depending on how Babel is being used, could require setting "babelrcRoots".
Given that, it may be more desirable to rename the .babelrc to be a project-wide "babel.config.json". As mentioned in the project-wide section above, this may then require explicitly setting "configFile" since Babel will not find the config file if the working directory isn't correct.
Supported file extensions
Babel can be configured using any file extension natively supported by Node.js: you can use .json , .js , .cjs and .mjs , both for babel.config.json and .babelrc.json files.
babel.config.json and .babelrc.json are parsed as JSON5 and should contain an object matching the options format that Babel accepts. They have been supported since v7.7.0 .
We recommend using this file type wherever possible: JS config files are handy if you have complex configuration that is conditional or otherwise computed at build time. However, the downside is that JS configs are less statically analyzable, and therefore have negative effects on cacheability, linting, IDE autocomplete, etc. Since babel.config.json and .babelrc.json are static JSON files, it allows other tools that use Babel such as bundlers to cache the results of Babel safely, which can be a huge build performance win.
babel.config.cjs and .babelrc.cjs allow you to define your configuration as CommonJS, using module.exports . They have been supported since v7.7.0 .
babel.config.mjs and .babelrc.mjs use native ECMAScript modules. They are supported by Node.js 13.2+ (or older versions via the --experimental-modules flag). Please remember that native ECMAScript modules are asynchronous (that's why import() always returns a promise!): for this reason, .mjs config files will throw when calling Babel synchronously. They have been supported since v7.8.0 .
babel.config.js and .babelrc.js behave like the .mjs equivalents when your package.json file contains the "type": "module" option, otherwise they are exactly the same as the .cjs files.
JavaScript configuration files can either export an object, or a function that when called will return the generated configuration. Function-returning configs are given a few special powers because they can access an API exposed by Babel itself. See Config Function API for more information.
For compatibility reasons, .babelrc is an alias for .babelrc.json .
Monorepo-structured repositories usually contain many packages, which means that they frequently run into the caveats mentioned in file-relative configuration and config file loading in general. This section is aimed at helping users understand how to approach monorepo configuration.
With monorepo setups, the core thing to understand is that Babel treats your working directory as its logical "root", which causes problems if you want to run Babel tools within a specific sub-package without having Babel apply to the repo as a whole.
Separately, it is also important to decide if you want to use .babelrc.json files or just a central babel.config.json . .babelrc.json files are not required for subfolder-specific configuration like they were in Babel 6, so often they are not needed in Babel 7, in favor of babel.config.json .
Root babel.config.json file
The first step in any monorepo structure should be to create a babel.config.json file in repository root. This establishes Babel's core concept of the base directory of your repository. Even if you want to use .babelrc.json files to configure each separate package, it is important to have as a place for repo-level options.
You can often place all of your repo configuration in the root babel.config.json . With "overrides", you can easily specify configuration that only applies to certain subfolders of your repository, which can often be easier to follow than creating many .babelrc.json files across the repo.
The first issue you'll likely run into is that by default, Babel expects to load babel.config.json files from the directory set as its "root", which means that if you create a babel.config.json , but run Babel inside an individual package, e.g.
the "root" Babel is using in that context is not your monorepo root, and it won't be able to find the babel.config.json file.
If all of your build scripts run relative to your repository root, things should already work, but if you are running your Babel compilation process from within a subpackage, you need to tell Babel where to look for the config. There are a few ways to do that, but the recommended way is the "rootMode" option with "upward" , which will make Babel search from the working directory upward looking for your babel.config.json file, and will use its location as the "root" value.
One helpful way to test if your config is being detected is to place a console.log() call inside of it if it is a babel.config.json JavaScript file: the log will execute the first time Babel loads it.
How you set this value varies by project, but here are a few examples:
Jest is often installed at the root of the monorepo and may not require configuration, but if it is installed per-package it can unfortunately be more complex to configure.
The main part is creating a custom jest transformer file that wraps babel-jest 's default behavior in order to set the option, e.g.
and with that saved somewhere, you'd then use that file in the place of babel-jest in your Jest options via the transform option:
so all JS files will be processed with your version of babel-jest with the option enabled.
There are tons of tools, but at the core of it is that they need the rootMode option enabled if the working directory is not already the monorepo root.
Subpackage .babelrc.json files
Similar to the way babel.config.json files are required to be in the "root", .babelrc.json files must be in the root package, by default. This means that, the same way the working directory affects babel.config.json loading, it also affects .babelrc.json loading.
Assuming you've already gotten your babel.config.json file loaded properly as discussed above, Babel will only process .babelrc.json files inside that root package (and not subpackages), so given for instance
compiling the packages/mod/index.js file will not load packages/mod/.babelrc.json because this .babelrc.json is within a sub-package, not the root package.
To enable processing of that .babelrc.json , you will want to use the "babelrcRoots" option from inside your babel.config.json file to do
so that Babel will consider all packages/* packages as allowed to load .babelrc.json files, along with the original repo root.
Config Function API
JS config files may export a function that will be passed config function API:
The api object exposes everything Babel itself exposes from its index module, along with config-file specific APIs:
The version string for the Babel version that is loading the config file.
JS configs are great because they can compute a config on the fly, but the downside there is that it makes caching harder. Babel wants to avoid re-executing the config function every time a file is compiled, because then it would also need to re-execute any plugin and preset functions referenced in that config.
To avoid this, Babel expects users of config functions to tell it how to manage caching within a config file.
Since the actual callback result is used to check if the cache entry is valid, it is recommended that:
- Callbacks should be small and side-effect free.
- Callbacks should return values with the smallest range possible. For example, the .using(() => process.env.NODE_ENV) usage above is not ideal because it would create an unknown number of cache entries depending on how many values of NODE_ENV are detected. It would be safer to do .using(() => process.env.NODE_ENV === "development") because then the cache entry can only ever be true or false .
Since NODE_ENV is a fairly common way to toggle behavior, Babel also includes an API function meant specifically for that. This API is used as a quick way to check the "envName" that Babel was loaded with, which takes NODE_ENV into account if no other overriding environment is set.
It has a few different forms:
- api.env("production") returns true if envName === "production" .
- api.env(["development", "test"]) returns true if ["development", "test"].includes(envName) .
- api.env() returns the current envName string.
- api.env(envName => envName.startsWith("test-")) returns true if the env starts with "test-".
Note: This function internally makes use of api.cache mentioned above to ensure that Babel is aware that this build depends on a specific envName . You should not use it alongside with api.cache.forever() or api.cache.never() .
This API is used as a way to access the caller data that has been passed to Babel. Since many instances of Babel may be running in the same process with different caller values, this API is designed to automatically configure api.cache , the same way api.env() does.
The caller value is available as the first parameter of the callback function. It is best used with something like
to toggle configuration behavior based on a specific environment.
While api.version can be useful in general, it's sometimes nice to just declare your version. This API exposes a simple way to do that with:
Babel can be configured! Many other tools have similar configs: ESLint ( .eslintrc ), Prettier ( .prettierrc ).
All Babel API options are allowed. However, if the option requires JavaScript, you may want to use a JavaScript configuration file.
What's your use case?
- You are using a monorepo?
- You want to compile node_modules ?
- You have a configuration that only applies to a single part of your project?
Create a file called babel.config.json with the following content at the root of your project (where the package.json is).
Check out the babel.config.json documentation to see more configuration options.
Create a file called .babelrc.json with the following content in your project.
Check out the .babelrc documentation to see more configuration options.
Alternatively, you can choose to specify your .babelrc.json config from within package.json using the babel key like so:
JavaScript configuration files
You can also write babel.config.json and .babelrc.json files using JavaScript:
You are allowed to access any Node.js APIs, for example a dynamic configuration based on the process environment:
You can read more about JavaScript configuration files in the dedicated documentation
Using the CLI ( @babel/cli )
Check out the babel-cli documentation to see more configuration options.
Using the API ( @babel/core )
Check out the babel-core documentation to see more configuration options.
Print effective configs
You can tell Babel to print effective configs on a given input path
BABEL_SHOW_CONFIG_FOR accepts both absolute and relative file paths. If it is a relative path, it will be resolved from cwd .
Once Babel processes the input file specified by BABEL_SHOW_CONFIG_FOR , Babel will print effective configs to the console. Here is an example output:
Babel will print effective config sources ordered by ascending priority. Using the example above, the priority is:
In other words, babel.config.json is overwritten by .babelrc , and .babelrc is overwritten by programmatic options.
For each config source, Babel prints applicable config items (e.g. overrides and env ) in the order of ascending priority. Generally each config sources has at least one config item -- the root content of configs. If you have configured overrides or env , Babel will not print them in the root, but will instead output a separate config item titled as .overrides[index] , where index is the position of the item. This helps determine whether the item is effective on the input and which configs it will override.
If your input is ignored by ignore or only , Babel will print that this file is ignored.
How Babel merges config items
Babel's configuration merging is relatively straightforward. Options will overwrite existing options when they are present and their value is not undefined . There are, however, a few special cases:
- For assumptions , parserOpts and generatorOpts , objects are merged, rather than replaced.
- For plugins and presets , they are replaced based on the identity of the plugin/preset object/function itself combined with the name of the entry.
Option (except plugin/preset) merging
As an example, consider a config with:
When NODE_ENV is test , the sourceType option will be replaced and the assumptions option will be merged. The effective config is:
As an example, consider a config with:
The overrides item will be merged on top of the top-level options. Importantly, the plugins array as a whole doesn't just replace the top-level one. The merging logic will see that "./plug" is the same plugin in both cases, and < thing: false, field2: true >will replace the original options, resulting in a config as
Since merging is based on identity + name, it is considered an error to use the same plugin with the same name twice in the same plugins / presets array. For example
is considered an error, because it's identical to plugins: ['./plug'] . Additionally, even
is considered an error, because the second one would just always replace the first one.
If you actually do want to instantiate two separate instances of a plugin, you must assign each one a name to disambiguate them. For example:
because each instance has been given a unique name and thus a unique identity.
Существует немало инструментов, позволяющих подготовить среду для React-разработки. Например, в наших материалах учебного курса по React используется средство create-react-app, позволяющее создать шаблонный проект, содержащий всё необходимое для разработки React-приложений. Автор статьи, перевод которой мы публикуем сегодня, хочет рассказать о том, как самостоятельно настроить окружение для разработки React-проектов с использованием Babel и Webpack. Эти инструменты используются и в проектах, создаваемых средствами create-react-app, и мы полагаем, что всем, кто изучает React-разработку, интересно будет познакомиться с ними и с методикой создания React-проектов на более глубоком уровне. А именно, речь пойдёт о том, как сконфигурировать Webpack таким образом, чтобы это средство использовало бы Babel для компиляции JSX-кода в JavaScript-код, и как настроить сервер, используемый при разработке React-проектов.
Предварительные требования
Для того чтобы настроить проект React-приложения, нам понадобятся следующие npm-модули.
- react — библиотека React.
- react-dom — библиотека, которая поможет нам использовать возможности React в браузере. /core — транспиляция JSX в JS. /preset-env — создание кода, подходящего для старых браузеров. /preset-react — настройка транспилятора для работы с React-кодом.
- babel-loader — настройка Webpack для работы с Babel.
- css-loader — настройка Webpack для работы с CSS.
- webpack — сборка модулей.
- webpack-cli — работа с Webpack из командной строки.
- style-loader — загрузка всего используемого CSS-кода в заголовке HTML-файла.
- webpack-dev-server — настройка сервера разработки.
Точкой входа в программу будет файл index.js , который содержится в папке src , находящейся в корневой директории проекта. Ниже показана структура этого проекта. Некоторые файлы и папки создаются автоматически, некоторые вам нужно будет создать самостоятельно.Структура проекта
Готовый проект, которым мы будем здесь заниматься, можно найти в этом репозитории.
Папка component будет содержать компоненты проекта (в нашем случае тут присутствует лишь один компонент). В папке dist , в файле main.js , будет находиться скомпилированный код, а index.html — это, как уже было сказано, главный HTML-файл нашего приложения.
▍JS-файлы
Вот правила, касающиеся работы с JS-файлами (этот код пойдёт в файл webpack.config.js ), они представляют собой одно из свойств объекта с настройками, экспортируемого этим файлом:
В свойстве rules представленного здесь объекта хранится массив правил, в соответствии с которыми должен быть обработан файл, заданный регулярным выражением, описанным в свойстве test . В данном случае правило будет применяться ко всем файлам с расширениями .m и .js , при этом файлы из папок node_modules и bower_components мы транспилировать не хотим. Далее, тут мы указываем, что мы хотим пользоваться babel-loader. После этого наши JS-файлы будут сначала обрабатываться средствами Babel, а потом упаковываться с помощью Webpack.Читайте также: