@doyakovlev/arui-scripts v10.0.0-beta.1
ARUI-scripts
Приложение на нашем стеке без какой либо конфигурации билдеров.
Во многом пакет аналогичен react-scripts из create-react-app, разница заключается
в немного отличающихся настройках для babel, поддержки ts и возможности работы с серверным кодом.
Использование
Пакет требует версию nodejs 8+.
Установите
arui-scriptsв свой проект как dev зависимость
yarn add arui-scripts --devили
npm install arui-scripts --save-dev- Создайте необходимые файлы
src/index.{js,jsx,ts,tsx}- входная точка для клиентского приложения.src/server/index.{js,jsx,tsx}- входная точка для серверного приложения.node_modules/arui-feather/polyfills- полифилы для клиентского приложения.
При желании вы можете изменить эти пути с помощью настроек.
- Используйте команды из
arui-scripts!
Доступные команды
arui-scripts start- запускает WebpackDevServer для фронтенда и webpack в режиме--watchдля сервера.arui-scripts build- компилирует клиент и сервер для использования в productionarui-scripts docker-build- собирает docker контейнер c production билдом и загружает его в артифакториarui-scripts test- запускает jest тесты.arui-scripts archive-build- собирает архив с production билдомarui-scripts bundle-analyze- запускает webpack-bundle-analyzer для prod версии клиентского кода
Настройки
Несмотря на то, что все работает из коробки, вы можете захотеть поменять некоторые настройки сборщиков.
Сделать это можно в package.json, определив там свойство aruiScripts.
Доступные настройки:
dockerRegistry- адрес используемого docker registry, по умолчанию'', то есть используется публичный registrybaseDockerImage- имя базового образа, используемого для построения docker образа. По умолчаниюheymdall/alpine-node-nginx:12.16.1.serverEntry- точка входа для исходников сервера, по умолчаниюsrc/server/index.serverOutput- имя файла для компиляции сервера, по умолчаниюserver.js.clientPolyfillsEntry- точка входа для полифилов. Будет подключаться до основной точки входа. По умолчанию подтягивает полифилы изarui-feather, если он установлен.clientEntry- точка входа для клиентского приложения. По умолчаниюsrc/index.js.useServerHMR- использовать ли HotModuleReplacement для сервера. По умолчаниюfalse.clientServerPort- порт WebpackDevServer и nginx итогового контейнера. По умолчанию8080.serverPort- порт нодового сервера. Нужен для правильного проксирования запросов от дев сервера и nginx. По умолчанию3000.additionalBuildPath- массив путей, которые попадут в архив при использовании командыarchive-build. По умолчанию['config'].archiveName- имя архива, который будет создан при использовании командыarchive-build. По умолчаниюbuild.tar.keepPropTypes- еслиtrue, пакеты с prop-types не будут удалены из production билда.debug- режим отладки, в котором не выполняются некоторые нежелательные операции и выводится больше сообщений об ошибках, по умолчаниюfalse.useTscLoader- использовать ts-loader вместо babel-loader для обработки ts файлов. У babel-loader есть ряд ограничений. По умолчаниюfalse.componentsTheme- путь к css файлу с темой для core-components. Используется для настройки postcss-custom-properties.
В целях отладки все эти настройки можно переопределить не изменяя package.json Просто передайте необходимые настройки в environment переменной ARUI_SCRIPTS_CONFIG
ARUI_SCRIPTS_CONFIG="{\"serverPort\":3333}" yarn startТак же, читаются настройки jest (см. документацию)
и proxy (см. документацию).
Несколько entry point
Ключи serverEntry и clientEntry принимают не только строки, но и любые возможные в webpack варианты.
Например, package.json:
{
"aruiScripts": {
"clientEntry": { "mobile": "src/mobile/", "desktop": "src/desktop/" },
"serverEntry": ["src/server-prepare", "src/server"]
}
}Ко всем клиентским entryPoint так же будут добавлены clientPolyfillsEntry (если задан)
и, в dev режиме, необходимые для hot-module-reload файлы.
Переопределение настроек компиляторов
По умолчанию для компиляции используется только Babel, для переопределения конфига можете положить файл .babelrc в рут папку своего проекта.
TypeScript можно включить, положив tsconfig.json в корень проекта.
Пути до ассетов
Во время компиляции продакшн версии билда будет созданно два бандла vendor.[hash].js и main.[hash].js. Для
того, чтобы генерировать правильный html с подключением этих ассетов вы можете использовать файл webpack-assets.json
который будет автоматически положен в папку со скомпилированным кодом.
vendor.js будет содержать все используемые вами node_modules, за исключением модулей, в названии которых содержится arui.
main.js содержит все остальное.
Важно подключать ваши ассеты в правильном порядке, vendor.js должен подключаться ДО main.js.
Пример функции, которая сформирует отсортированные в правильном порядке массивы для js и css файлов:
function readAssetsManifest() {
// читаем манифест
const manifestPath = path.join(process.cwd(), '.build/webpack-assets.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
const js = [];
const css = [];
// vendor должен идти перед main
['vendor', 'main'].forEach((key) => {
if (!manifest[key]) { // в дев сборке vendor.js не формируется
return;
}
if (manifest[key].js) {
js.push(manifest[key].js);
}
if (manifest[key].css) {
css.push(manifest[key].css);
}
});
return {
js, css
};
}Использование hot-module-replacement
Клиент:
По умолчанию на клиенте будет подменяться только css. Для правильной работы с react вам надо добавить примерно такой код:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app';
import { AppContainer } from 'react-hot-loader';
ReactDOM.render(
<AppContainer><App /></AppContainer>,
document.getElementById('react-app')
);
if (module.hot) {
module.hot.accept('./app', () => {
const NextAppAssignments = require('./app').default;
ReactDOM.render(
<AppContainer><NextAppAssignments /></AppContainer>,
document.getElementById('react-app')
);
});
}При возникновении в консоли предупреждения React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work. убедитесь, что в webpack.client.dev не переопределяется настройка resolve.alias, в нём должна быть следующая запиcь:
// ...
resolve: {
alias: {
'react-dom': '@hot-loader/react-dom'
}
}Сервер
Серверная часть приложения по умолчанию будет просто перезапускаться после каждого изменения кода, для использования hot module replacement на сервере нужно сделать несколько вещей:
В package.json добавить:
{
"aruiScripts": { "useServerHMR": true }
}Ваша входная точка сервера должна выглядеть примерно так (на примере hapi):
server/index.js
import server from './server';
let currentServer = server;
async function startServer() {
try {
await currentServer.start();
} catch (error) {
console.error('Failed to start server', error);
process.exit(1);
}
}
startServer();
if (module.hot) {
module.hot.accept(['./server'], async () => {
try {
await currentServer.stop();
currentServer = server; // импорт из сервера заменится самостоятельно
await startServer();
} catch (error) {
console.log('Failed to update server. You probably need to restart application', error);
}
});
}server/server.js
import hapi from 'hapi';
const server = new hapi.Server();
server.ext('onPostStart', (_, done) => {
console.log(`Server is running: ${server.info.uri}`);
done();
});
(async () => {
server.connection({ port: 3000 });
// ...
// конфигурация вашего сервера
// ...
})();
export default server;Таким образом после изменения кода сервер не будет полностью пререзагружаться, что во многих случаях быстрее. В случае изменения входной точки сервера при использовании HMR вам надо будет перезапускать сервер вручную.
Тесты
Команда arui-scripts test внутри запускает jest с дополнительной конфигурацией.
Конфигурация включает в себя:
- Использование
jest-snapshot-serializer-class-name-to-stringдля правильной работы сcn - Замену всех импортов css файлов на пустые файлы
- Компиляцию .js/.jsx файлов используя babel
- Компиляцию .ts/.tsx файлов используя tsc
- Замену импортов остальных типов файлов на импорт строк с названием файла
По умолчанию под маску для поиска тестов попадают все файлы *test*.(js|jsx|ts|tsx), *spec*.((js|jsx|ts|tsx)), */__test__/*.(js|jsx|ts|tsx).
Вы можете переопределять любые настройки jest в package.json, документация.
Если какие либо из ваших инструментов (например VSСode или WebStorm) не могут запустить тесты поскольку не находят конфигурацию, вы можете так же указать arui-scripts как preset для jest.
Таким образом будет работать как запуск тестов через arui-script, так и любые сторонние инструменты, запускающие jest.
package.json
{
"jest": {
"preset": "arui-scripts"
}
}docker
Команда arui-scripts docker-build запускает компиляцию продакшн версии и сборку докер образа.
Образ основан на alpine-node-nginx.
Имя контейнера определяется как {configs.dockerRegistry}/{name}:{version}. Переменные name и version по умолчанию берутся из package.json,
но вы так же можете переопределить их из командной строки, например
arui-scripts docker-build name=container-name version=0.1-beta
Команда предполагает наличие установленных node_modules перед сборкой, в процессе работы же очищает дев зависимости используя yarn или npm.
yarn будет использоваться когда в рутовой папке проекта есть yarn.lock и yarn доступен в системе.
Итоговый контейнер будет содержать nginx и скрипт для запуска nginx одновременно с nodejs сервером.
В итоге, для корректного запуска вашего докер-контейнера вам надо будет выполнить
docker run -p 8080:8080 container-name:version ./start.shНа 8080 порту будет поднят nginx, который будет раздавать статику и проксировать все остальные запросы к nodejs.
Вы также можете переопределить полностью процесс сборки docker-образа, создав в корневой директории проекта Dockerfile содержащий необходимый набор инструкций. Пример Dockerfile.
archive
Команда arui-scripts archive-build запускает компиляцию продакшн версии и сборку архива со скомпилированным кодом.
Этот вариант может быть полезен если вы хотите деплоить ваше приложение через подключение архива в марафоне.
Команда предполагает наличие установленных node_modules перед сборкой, в процессе работы же очищает дев зависимости используя yarn или npm.
yarn будет использоваться когда в рутовой папке проекта есть yarn.lock и yarn доступен в системе.
Итоговый архив будет содержать в себе .build, node_modules, package.json и config папки вашего проекта.
Проксирование запросов до бэкенда.
В случае, если ваш фронт должен обращаться к API, отличному от вашего nodejs сервера, в дев режиме вы можете настроить проксирование запросов.
Сделать это можно используя свойство proxy в вашем package.json.
Например:
{
"proxy": {
"/corp-shared-ui": {
"target": "http://corpint4",
"headers": {
"host": "corpint4"
}
}
}
}Такая конфигурация будет проксировать запросы к http://localhost:8080/corp-shared-ui/ на http://corpint4/corp-shared-ui.
Подробнее о конфигурации прокси сервера можно почитать в документации Webpack.
Конфигурация typescript
Компиляция TS работает из коробки, если в корне проекта есть файл tsconfig.json.
За основу можно использовать дефолтный конфиг:
{
"extends": "./node_modules/arui-scripts/configs/tsconfig.json"
}По умолчанию TS будет компилироваться через babel, но у этого есть ряд ограничений:
- нельзя использовать namespace
- Нельзя использовать устаревший синтаксис import/export (
import foo = require(...),export = foo) - enum merging
Если вы используете что-то из вышеперичисленного - вы можете вернуться к использованию tsc для компиляции ts файлов
{
"aruiScripts": { "useTscLoader": true }
}Конфигурация nginx
Несмотря на то, что nginx имеет готовый конфиг с роутингом, иногда возникает необходимость добавлять свои роуты.
Для этого вы можете создать nginx.conf на уровне проекта со своими роутами. Пример конфига: arui-scripts/commands/docker-build/nginx.conf.template.js
Удаление proptypes
Так как в production режими proptypes не проверяются, их имеет смысл удалить из production сборки.
Сами объявления proptypes удаляются с помощью babel-plugin-transform-react-remove-prop-types.
Но импорты пакетов prop-types при этом не удаляются. Чтобы это реализовать, используется webpack.NormalModuleReplacementPlugin.
С помощью него заменяются на пустышку пакеты, попадающие под маску:
/^react-style-proptype$//^thrift-services\/proptypes/
Если, по какой то причине, вы не хотите такого поведения - вы можете отключить его, добавив в package.json:
{
"aruiScripts": {
"keepPropTypes": true
}
}require не js файлов в node_modules в node.js
Сборка серверной части устроена таким образом, что большая часть node_modules не вкомпиливается
в итоговый бандл, а загружается стандартным require node.js. Как правило - это нам и нужно.
Но в случае react-компонентов, мы зачастую запрашиваем кроме кода компонентов еще и .css, .png и другие файлы.
require node.js на таких местах ломается. Поэтому наши внутренние библиотеки компонентов все же вкомпиливаются
в итоговый бандл сервера. Это сделанно с помощью добавления их в исключение
плагина webpack-node-externals.
В случае, если вам необходима обработка не-js файлов из других внешних модулей - вы можете
воспользоваться механизмом overrides, описанным ниже.
По умолчанию же все не-js файлы из внешних модулей будут проигнорированы.
Тонкая настройка
Если вам не хватает гибкости при использовании arui-scripts, например вы хотите добавить свой плагин для вебпака -
вы можете воспользоваться механизмом overrides.
Для этого вам необходимо создать в корне вашего проекта файл arui-scripts.overrides.js, из которого вы сможете управлять
конфигурацией почти всех инструментов, используемых в arui-scripts.
Принцип работы тут следующий. Для всех конфигураций определен набор ключей, которые они будут искать в arui-scripts.overrides.js,
В случае если такой ключ найден и это функция - она будет вызвана, и в качестве аргумента ей будет передана существующая конфигурация.
Возвращать такая функция должна так же конфигурацию.
Например такое содержимое arui-scripts.overrides.js:
const path = require('path');
module.exports = {
webpack: (config) => {
config.resolve.alias = {
components: path.resolve(__dirname, 'src/components')
};
return config;
}
};С помощью этой конфигурации ко всем настройкам вебпака будет добавлен alias components.
На данный момент можно переопределять следующие конфигурации:
babel-client- конфигурацияbabelдля клиентского кода. Ключи:babel,babelClient.babel-server- конфигурацияbabelдля серверноого кода. Ключи:babel,babelServer.dev-server- конфигурацияwebpack-dev-server. Ключи:devServer.postcss- конфигурация дляpostcss. Ключи:postcss.configpostcss содержит массив с уже инициализированными плагинами, параметры которых уже зафиксированны. Если необходимо изменить параметры плагинов можно пересоздать конфиг, таким образом:const { createPostcssConfig, // функция для создания конфигурационного файла postcss postcssPlugins, // список плагинов postcssPluginsOptions, // коллекция конфигураций плагинов } = require('arui-scripts/configs/postcss.config');module.exports = { postcss: () => { const newOption = { ...postcssPluginsOptions, 'postcss-import': { ...postcssPluginsOptions'postcss-import', path: [...postcssPluginsOptions'postcss-import'.path, './src'], }, }; return createPostcssConfig(postcssPlugins, newOption); }, };
stats-options- конфигурация для webpack-stats. Ключи:stats.webpack.client.dev- конфигурация для клиентского webpack в dev режиме. Ключи:webpack,webpackClient,webpackDev,webpackClientDev.webpack.client.prod- конфигурация для клиентского webpack в prod режиме. Ключи:webpack,webpackClient,webpackProd,webpackClientProd.webpack.server.dev- конфигурация для серверного webpack в dev режиме. Ключи:webpack,webpackServer,webpackDev,webpackServerDev.webpack.server.prod- конфигурация для серверного webpack в prod режиме. Ключи:webpack,webpackServer,webpackProd,webpackServerProd.supporting-browsers- список поддерживаемых браузеров в формате browserslist. Ключи:browsers,supportingBrowsers
Для некоторых конфигураций определены несколько ключей, они будут применяться в том порядке, в котором они приведены в этом файле.
5 years ago