В данной статье расскажу об использовании Nx для разработки веб приложений на Typescript.
Два года назад я написал статью на медиуме - Сборка Typescript приложения с помощью Webpack, где поделился своим решением сборки простого приложения на Typescript с помощью Webpack.
И все бы ничего, если бы это не устарело. Все описанное работает и до сих пор, но уже есть более продвинутые решения, об одном из которых и пойдет дальнейший разговор.
Я был знаком с webpack и мне не составило труда набросать несколько модулей и собрать сборку, которая удовлетворяла выше описанным требованиям.
Решение отлично работало, за одним только исключением, что его трудно было обновлять. Из-за того, что это был просто шаблон приложения, который копировался и изменялся, то явного желания обновлять 5 - 10 созданных проектов не было.
Так как проекты жили параллельно, всегда приходилось мигрировать проекты, копируя куски из одного решения в другое. В один момент я даже подумывал о создании своего CLI, но сама мысль об этом явно кричала о том, что я делаю что-то не так.
Примерно в тоже время, я начал использовать Nx для своих Angular проектов.
Nx это набор утилит для создания и управления монорепозиторием. С Nx можно ознакомиться в официальной документации.
Так как статья посвящена разработке typescript приложений, интересен следующий раздел - Nx и TypeScript.
Вот несколько особенностей Nx:
yarn global add /cli
Это позволит запускать команды nx без менеджеров пакетов (yarn или npm):
nx g lib mylib
Иначе придется писать:
yarn nx g lib mylib
npx create-nx-workspace@latest
Если вы используете yarn, то тогда можете запустить следующую команду:
yarn create nx-workspace --package-manager=yarn
Запуск команды создания Nx workspace
Введем название workspace - boobs:
Создание boobs Nx workspace
При при создании workspace можно выбрать тип проекта (angular, react, node или typescript). Данная опция определяет какие зависимости будут включены в package.json.
В качестве проекта выберем ts:
Выбор типа проекта Nx
Откажемся от использования облака:
Опция выбора использования Nx Cloud
Nx установил зависимости, где сразу предложил пройти туториал
Перейдем в папку с созданным проектом:
cd boobs
Посмотрим, что создал Nx, выполнив команду ls:
Но так как из консоли ничего непонятно, откроем проект в любимой IDE:
boobs/
├── packages/
├── tools/
├── workspace.json
├── nx.json
├── package.json
└── tsconfig.base.json
Откроем package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {},
"private": true,
"dependencies": {},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/node": "16.11.7",
"prettier": "^2.5.1",
"typescript": "~4.5.2"
}
}
Как видно из содержания, Nx создал новый, пустой проект с минимумом зависимостей. Пакеты @nrwl понятны из названия, где либо подключают cli, либо js, либо осуществляют работу самого workspace.
Рассмотрим файл nx.json:
{
"extends": "@nrwl/workspace/presets/core.json",
"npmScope": "boobs",
"affected": {
"defaultBase": "main"
},
"cli": {
"defaultCollection": "@nrwl/workspace"
},
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"lint",
"test",
"e2e"
]
}
}
}
}
В данном случае имеем 3 секции:
{
"version": 2,
"projects": {}
}
Последний файл это tsconfig.base.json:
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": ["es2017", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {}
},
"exclude": ["node_modules", "tmp"]
}
Здесь все стандартно. Можно бампнуть es до последней версии.
Единственное, что бросается в глаза - это отсутствие eslint.
Добавим eslint в workspace:
yarn add --dev @nrwl/linter
yarn add --dev @nrwl/eslint-plugin-nx
nx generate @nrwl/js:app store
После создания приложения, глобально появилось несколько файлов:
{
"version": 2,
"projects": {
"store": "packages/store"
}
}
Также, из-за того, что в проекте не было jest, Nx любезно его установил, обновив package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {},
"private": true,
"dependencies": {
"tslib": "^2.0.0"
},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/eslint-plugin-nx": "^13.8.2",
"@nrwl/jest": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/linter": "^13.8.2",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/jest": "27.0.2",
"@types/node": "16.11.7",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"eslint": "~8.7.0",
"eslint-config-prettier": "8.1.0",
"jest": "27.2.3",
"prettier": "^2.5.1",
"ts-jest": "27.0.5",
"typescript": "~4.5.2"
}
}
nx serve store
Как видно livereload работает. Если изменять файлы в монорепозитории, то приложение будет меняться.
Если открыть созданный проект, то там следующая структура:
packages/store
├── jest.config.js
├── package.json
├── project.json
├── README.md
├── src
│ ├── app
│ │ ├── store.spec.ts
│ │ └── store.ts
│ └── index.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
Tsconfig файлы переопределяют правила глобальной конфигурации TS.
Например, tsconfig.spec.ts - создаст окружение для тестирования.
Project.json описывает конфигурацию приложения.
{
"root": "packages/store",
"sourceRoot": "packages/store/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/store",
"main": "packages/store/src/index.ts",
"tsConfig": "packages/store/tsconfig.app.json",
"assets": ["packages/store/*.md"]
}
},
"serve": {
"executor": "@nrwl/js:node",
"options": {
"buildTarget": "store:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/store/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/store"],
"options": {
"jestConfig": "packages/store/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
В данном случае видны правила для запуска и компиляции проекта, есть команды для проверки стилей (lint) и тестирования (test).
Само приложение включает в себя только один файл - store.ts:
export function store(): string {
return 'store';
}
Теперь создадим библиотеку api:
nx generate @nrwl/js:library api
Структура библиотеки:
├── jest.config.js
├── package.json
├── project.json
├── README.md
├── src
│ ├── index.ts
│ └── lib
│ ├── api.spec.ts
│ └── api.ts
├── tsconfig.json
├── tsconfig.lib.json
└── tsconfig.spec.json
Одним из немногих отличий конфигурации библиотеки от приложения является project.json:
{
"root": "packages/api",
"sourceRoot": "packages/api/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/api",
"main": "packages/api/src/index.ts",
"tsConfig": "packages/api/tsconfig.lib.json",
"assets": ["packages/api/*.md"]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/api/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/api"],
"options": {
"jestConfig": "packages/api/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
Единственное отличие - это наличие блока с serve:
{
"root": "packages/store",
"sourceRoot": "packages/store/src",
"projectType": "application",
"targets": {
...
"serve": {
"executor": "@nrwl/js:node",
"options": {
"buildTarget": "store:build"
}
},
...
}
}
Используем библиотеку api в приложении strore:
import { api } from "@boobs/api";
export function store(): string {
console.log(api());
return 'store';
}
Соберем проект:
nx run store:build --with-deps
Запустим проект:
nx serve store
Как видим на скриншоте выше, был вызван метод из библиотеки api, перед запуском приложения store.
Добавим пакет ноды в workspace:
yarn add -D @nrwl/node
Теперь создадим приложение с node:
nx g @nrwl/node:application node-store
Структура созданного приложения:
packages/node-store
├── jest.config.js
├── project.json
├── src
│ ├── app
│ ├── assets
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ └── main.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
Почти все тоже самое, но только с немного улучшенной структурой.
Как и все другие библиотеки и приложения, главным файлом является project.json:
{
"root": "packages/node-store",
"sourceRoot": "packages/node-store/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/node:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/node-store",
"main": "packages/node-store/src/main.ts",
"tsConfig": "packages/node-store/tsconfig.app.json",
"assets": ["packages/node-store/src/assets"]
},
"configurations": {
"production": {
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "packages/node-store/src/environments/environment.ts",
"with": "packages/node-store/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"executor": "@nrwl/node:execute",
"options": {
"buildTarget": "node-store:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/node-store/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/node-store"],
"options": {
"jestConfig": "packages/node-store/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
Как видно из примера, тут уже есть environment'ы, также есть различные виды сборок, такие как development и production.
yarn add -D @nrwl/express
Создадим express приложение:
nx g @nrwl/express:app express-store
При запуске команд, Nx проверяет конфигурации, и в случае с добавлением express он глобально добавил несколько зависимостей в package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"start": "nx serve store",
"build": "nx run store:build --with-deps"
},
"private": true,
"dependencies": {
"express": "4.17.2",
"tslib": "^2.0.0"
},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/eslint-plugin-nx": "^13.8.2",
"@nrwl/express": "^13.8.3",
"@nrwl/jest": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/linter": "^13.8.2",
"@nrwl/node": "^13.8.3",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/express": "4.17.13",
"@types/jest": "27.0.2",
"@types/node": "16.11.7",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"eslint": "~8.7.0",
"eslint-config-prettier": "8.1.0",
"jest": "27.2.3",
"prettier": "^2.5.1",
"ts-jest": "27.0.5",
"typescript": "~4.5.2"
}
}
Отсюда и ответ на вопрос, зачем в каждом из пакетов, сгенерированных Nx, есть собственный package.json.
Запустим проект:
nx serve express-store
Откроем браузер:
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import * as express from 'express';
const app = express();
app.get('/api', (req, res) => {
res.send({ message: 'Welcome to express-store!' });
});
const port = process.env.port || 3333;
const server = app.listen(port, () => {
console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);
Добавим пакет в workspace:
yarn add -D @nrwl/web
Пакет подключает около 300 пакетов для веб разработки.
Создадим web приложение:
nx g @nrwl/web:app web-store
Выберем SCSS:
Запустим приложение:
nx serve web-store
Откроем браузер - http://localhost:4200
Там все аналогично. Добавляете пакет, создаете приложение.
yarn add -D @nrwl/angular
nx g @nrwl/angular:app angular-store
Однако, стоит отметить, что лучше при создании workspace использовать пресеты с приложениями. Это позволит не делать много не нужной работы.
GitHub - Fafnur/boobs: NX samples
github.com
В заключении хочется сказать, что инструментарии для веб разработки очень сильно развиваются. Грустно видеть проекты, которые до сих пор используют gulp как основную систему сборки. Нет никаких претензий к gulp, grunt и webpack, просто уже давно много задач, которые решали сборщики - были решены и оптимизированы. И разработчику не нужно создавать и поддерживать свой инструментарий. Хорошим примером является Nx, который возьмет на себя весь front-ops и даст вам возможность заниматься тем, чем должен заниматься разработчик - разработкой приложений, а не бесконечной настройкой конфигов, и их изменением при обновлении vendors.
Два года назад я написал статью на медиуме - Сборка Typescript приложения с помощью Webpack, где поделился своим решением сборки простого приложения на Typescript с помощью Webpack.
И все бы ничего, если бы это не устарело. Все описанное работает и до сих пор, но уже есть более продвинутые решения, об одном из которых и пойдет дальнейший разговор.
Проблемы собственных решений
Больше трех лет назад я устройся на работу в один небольшой фин. тех стартап Angular разработчиком. И по мере выполнения разных задач, появилась потребность в некоем решении, которое удовлетворяло бы следующим требованиям:- Язык разработки Typescript;
- Livereload при внесении изменений;
- Есть "prod" сборка, которая оптимизирует исходный код.
Я просмотрел несколько проектов на github’е, но ни одно решение мне не подошло, так как либо они уже не поддерживались, либо были слишком усложнены. Мне же нужно было что-то простое.Одним словом все то, что есть в современных JS фреймворках.
Я был знаком с webpack и мне не составило труда набросать несколько модулей и собрать сборку, которая удовлетворяла выше описанным требованиям.
Решение отлично работало, за одним только исключением, что его трудно было обновлять. Из-за того, что это был просто шаблон приложения, который копировался и изменялся, то явного желания обновлять 5 - 10 созданных проектов не было.
Так как проекты жили параллельно, всегда приходилось мигрировать проекты, копируя куски из одного решения в другое. В один момент я даже подумывал о создании своего CLI, но сама мысль об этом явно кричала о том, что я делаю что-то не так.
Примерно в тоже время, я начал использовать Nx для своих Angular проектов.
Информация о Nx
Nx это набор утилит для создания и управления монорепозиторием. С Nx можно ознакомиться в официальной документации.
Так как статья посвящена разработке typescript приложений, интересен следующий раздел - Nx и TypeScript.
Если вы предпочитаете больше смотреть, чем читать, то команда Nx регулярно выпускает ролики с обзором и настройкой текущего инструментария. Например, ролик посвященный первой настройке приложения:В свое время, часть Angular Team создает свою компанию Nrwl, в которой начинают делать свой инструментарий для упрощения разработки приложений на Angular. Консультации компаний из списка Fortune 500 позволили расширить инструментарий и включить в него не только Angular, а также React, Vue и чистый Typescript.
Вот несколько особенностей Nx:
- вся мощь typescript вместе с eslint;
- монорепозиторий, который позволяет разрабатывать несколько приложений с единой кодовой базой;
- генераторы - семейство консольных команд для упрощения создания новых файлов и решений, с возможностью создания собственных шаблонов и команд;
- конфигурируемые сборки - например, версии для релиза с оптимизацией исходного кода;
- миграции - при изменении и обновлении системных зависимостей, Nx самостоятельно поправит конфигурационные файлы и приведет все данные к требуемому виду;
- возможность использовать все современные фреймворки React, Angular, Vue и Svetle.
Другими словами, NX - набор утилит, который все делает за вас, и делает это хорошо, но в разумных пределах, конечно.
Предварительная настройка
Для работы с nx вам потребуется установленная NodeJS и один из менеджеров пакетов, таких как npm или yarn.Перед использованием nx, можно установить nx cli глобально:Ознакомиться с NodeJS можно на сайте - https://nodejs.org/en/docs.
yarn global add /cli
Это позволит запускать команды nx без менеджеров пакетов (yarn или npm):
nx g lib mylib
Иначе придется писать:
yarn nx g lib mylib
Создание workspace для typescript приложения
Для того чтобы создать новый Nx workspace необходимо запустить команду:npx create-nx-workspace@latest
Если вы используете yarn, то тогда можете запустить следующую команду:
yarn create nx-workspace --package-manager=yarn
Введем название workspace - boobs:
При при создании workspace можно выбрать тип проекта (angular, react, node или typescript). Данная опция определяет какие зависимости будут включены в package.json.
В качестве проекта выберем ts:
Откажемся от использования облака:
Далее Nx будет устанавливать необходимые зависимости:Если вы не боитесь стать пупыркой, можете попробовать использовать облако.
Nx установил зависимости, где сразу предложил пройти туториал
Перейдем в папку с созданным проектом:
cd boobs
Посмотрим, что создал Nx, выполнив команду ls:
Но так как из консоли ничего непонятно, откроем проект в любимой IDE:
Формально Nx создаст следующую структуру:
boobs/
├── packages/
├── tools/
├── workspace.json
├── nx.json
├── package.json
└── tsconfig.base.json
Откроем package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {},
"private": true,
"dependencies": {},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/node": "16.11.7",
"prettier": "^2.5.1",
"typescript": "~4.5.2"
}
}
Как видно из содержания, Nx создал новый, пустой проект с минимумом зависимостей. Пакеты @nrwl понятны из названия, где либо подключают cli, либо js, либо осуществляют работу самого workspace.
Рассмотрим файл nx.json:
{
"extends": "@nrwl/workspace/presets/core.json",
"npmScope": "boobs",
"affected": {
"defaultBase": "main"
},
"cli": {
"defaultCollection": "@nrwl/workspace"
},
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"lint",
"test",
"e2e"
]
}
}
}
}
В данном случае имеем 3 секции:
- affected - ветка, с которой будут сравниваться изменения в монорепозитории. Это необходимо для того, чтобы понимать какие тесты запускать, и какие части проекта (приложения и библиотеки) были затронуты в результате последних правок.
- cli - настройка CLI. Обычно, там указываются дефолтные коллекции. В данном случае используем коллекции nx. Если вы фанатик yarn, можно добавить свойство "packageManager": "yarn", чтобы всегда yarn использовался.
- tasksRunnerOptions - набор правил запуска приложений и библиотек. Для базовой настройки это можно пока пропустить.
{
"version": 2,
"projects": {}
}
Последний файл это tsconfig.base.json:
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": ["es2017", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {}
},
"exclude": ["node_modules", "tmp"]
}
Здесь все стандартно. Можно бампнуть es до последней версии.
Единственное, что бросается в глаза - это отсутствие eslint.
Добавим eslint в workspace:
yarn add --dev @nrwl/linter
yarn add --dev @nrwl/eslint-plugin-nx
Данный шаг можно пропустить, так как при создании приложения, Nx сделает все за вас, и сам добавит eslint и jest.
Работа с NX workspace
В монорепе можно создавать как минимум два типа пакетов:- библиотеки - обычные пакеты, которые можно разместить в npmjs
- приложения - тоже что и библиотеки, только приложение имеет способы запуска отслеживания изменений.
nx generate @nrwl/js:app store
После создания приложения, глобально появилось несколько файлов:
- .eslintrc.json - настройки линтера
- jest.config.js, jest.preset.js - настройка для unit тестирования с помощью jest
{
"version": 2,
"projects": {
"store": "packages/store"
}
}
Также, из-за того, что в проекте не было jest, Nx любезно его установил, обновив package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {},
"private": true,
"dependencies": {
"tslib": "^2.0.0"
},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/eslint-plugin-nx": "^13.8.2",
"@nrwl/jest": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/linter": "^13.8.2",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/jest": "27.0.2",
"@types/node": "16.11.7",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"eslint": "~8.7.0",
"eslint-config-prettier": "8.1.0",
"jest": "27.2.3",
"prettier": "^2.5.1",
"ts-jest": "27.0.5",
"typescript": "~4.5.2"
}
}
Запустим проект:Конечно, не понятно почему нельзя было сразу добавить команду запуска приложения в package.json, ну да ладно.
nx serve store
Как видно livereload работает. Если изменять файлы в монорепозитории, то приложение будет меняться.
Если открыть созданный проект, то там следующая структура:
packages/store
├── jest.config.js
├── package.json
├── project.json
├── README.md
├── src
│ ├── app
│ │ ├── store.spec.ts
│ │ └── store.ts
│ └── index.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
Tsconfig файлы переопределяют правила глобальной конфигурации TS.
Например, tsconfig.spec.ts - создаст окружение для тестирования.
Project.json описывает конфигурацию приложения.
{
"root": "packages/store",
"sourceRoot": "packages/store/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/store",
"main": "packages/store/src/index.ts",
"tsConfig": "packages/store/tsconfig.app.json",
"assets": ["packages/store/*.md"]
}
},
"serve": {
"executor": "@nrwl/js:node",
"options": {
"buildTarget": "store:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/store/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/store"],
"options": {
"jestConfig": "packages/store/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
В данном случае видны правила для запуска и компиляции проекта, есть команды для проверки стилей (lint) и тестирования (test).
Само приложение включает в себя только один файл - store.ts:
export function store(): string {
return 'store';
}
Теперь создадим библиотеку api:
nx generate @nrwl/js:library api
Структура библиотеки:
├── jest.config.js
├── package.json
├── project.json
├── README.md
├── src
│ ├── index.ts
│ └── lib
│ ├── api.spec.ts
│ └── api.ts
├── tsconfig.json
├── tsconfig.lib.json
└── tsconfig.spec.json
Одним из немногих отличий конфигурации библиотеки от приложения является project.json:
{
"root": "packages/api",
"sourceRoot": "packages/api/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/api",
"main": "packages/api/src/index.ts",
"tsConfig": "packages/api/tsconfig.lib.json",
"assets": ["packages/api/*.md"]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/api/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/api"],
"options": {
"jestConfig": "packages/api/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
Единственное отличие - это наличие блока с serve:
{
"root": "packages/store",
"sourceRoot": "packages/store/src",
"projectType": "application",
"targets": {
...
"serve": {
"executor": "@nrwl/js:node",
"options": {
"buildTarget": "store:build"
}
},
...
}
}
Используем библиотеку api в приложении strore:
import { api } from "@boobs/api";
export function store(): string {
console.log(api());
return 'store';
}
Соберем проект:
nx run store:build --with-deps
Запустим проект:
nx serve store
Как видим на скриншоте выше, был вызван метод из библиотеки api, перед запуском приложения store.
Node и Nx
Если нужно пойти дальше и начать разрабатывать приложение на NodeJs, то у Nx есть отличный пакет - @nrwl/node. Подробнее о разработке на Node с NX.Добавим пакет ноды в workspace:
yarn add -D @nrwl/node
Теперь создадим приложение с node:
nx g @nrwl/node:application node-store
Структура созданного приложения:
packages/node-store
├── jest.config.js
├── project.json
├── src
│ ├── app
│ ├── assets
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ └── main.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
Почти все тоже самое, но только с немного улучшенной структурой.
Как и все другие библиотеки и приложения, главным файлом является project.json:
{
"root": "packages/node-store",
"sourceRoot": "packages/node-store/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/node:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/node-store",
"main": "packages/node-store/src/main.ts",
"tsConfig": "packages/node-store/tsconfig.app.json",
"assets": ["packages/node-store/src/assets"]
},
"configurations": {
"production": {
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "packages/node-store/src/environments/environment.ts",
"with": "packages/node-store/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"executor": "@nrwl/node:execute",
"options": {
"buildTarget": "node-store:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/node-store/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/packages/node-store"],
"options": {
"jestConfig": "packages/node-store/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
}
Как видно из примера, тут уже есть environment'ы, также есть различные виды сборок, такие как development и production.
Внимательный читатель заметит, что чем дальше идет усложнение структуры, то все больше и больше она начинает напоминать структуру проектов на Angular.
Этот enterprise из головы уже не выкинуть.
Express и Nx
Шагнем дальше и создадим проект с express. Добавим соответствующий пакет Nx:yarn add -D @nrwl/express
Создадим express приложение:
nx g @nrwl/express:app express-store
При запуске команд, Nx проверяет конфигурации, и в случае с добавлением express он глобально добавил несколько зависимостей в package.json:
{
"name": "boobs",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"start": "nx serve store",
"build": "nx run store:build --with-deps"
},
"private": true,
"dependencies": {
"express": "4.17.2",
"tslib": "^2.0.0"
},
"devDependencies": {
"@nrwl/cli": "13.8.2",
"@nrwl/eslint-plugin-nx": "^13.8.2",
"@nrwl/express": "^13.8.3",
"@nrwl/jest": "13.8.2",
"@nrwl/js": "13.8.2",
"@nrwl/linter": "^13.8.2",
"@nrwl/node": "^13.8.3",
"@nrwl/tao": "13.8.2",
"@nrwl/workspace": "13.8.2",
"@types/express": "4.17.13",
"@types/jest": "27.0.2",
"@types/node": "16.11.7",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"eslint": "~8.7.0",
"eslint-config-prettier": "8.1.0",
"jest": "27.2.3",
"prettier": "^2.5.1",
"ts-jest": "27.0.5",
"typescript": "~4.5.2"
}
}
Отсюда и ответ на вопрос, зачем в каждом из пакетов, сгенерированных Nx, есть собственный package.json.
Запустим проект:
nx serve express-store
Откроем браузер:
Если сравнивать проект на node и express, то они как не казалось бы удивительным, отличаются только наличием express в main.ts:Магия, ничего не скажешь.
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import * as express from 'express';
const app = express();
app.get('/api', (req, res) => {
res.send({ message: 'Welcome to express-store!' });
});
const port = process.env.port || 3333;
const server = app.listen(port, () => {
console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);
Web и Nx
Последним пакетом можно рассмотреть пакет для создания web приложений - @nrwl/webДобавим пакет в workspace:
yarn add -D @nrwl/web
Пакет подключает около 300 пакетов для веб разработки.
Создадим web приложение:
nx g @nrwl/web:app web-store
Выберем SCSS:
Запустим приложение:
nx serve web-store
Откроем браузер - http://localhost:4200
Следующим шагом идет создание уже полноценных веб приложений на Angular и React.Выше приведенное решение и является аналогом моего самописанного решения, которое можно теперь выкинуть на помойку.
Там все аналогично. Добавляете пакет, создаете приложение.
yarn add -D @nrwl/angular
nx g @nrwl/angular:app angular-store
Однако, стоит отметить, что лучше при создании workspace использовать пресеты с приложениями. Это позволит не делать много не нужной работы.
Исходники
Все описанное выше можно посмотреть в репозитории - https://github.com/Fafnur/boobs:GitHub - Fafnur/boobs: NX samples
github.com
Резюме
В данной статье рассказал о таком инструменте как Nx и его использовании при разработке веб приложений. Сделал краткий обзор структуры приложений на Nx, а также привел команды для генерации библиотек и запуска приложений.В заключении хочется сказать, что инструментарии для веб разработки очень сильно развиваются. Грустно видеть проекты, которые до сих пор используют gulp как основную систему сборки. Нет никаких претензий к gulp, grunt и webpack, просто уже давно много задач, которые решали сборщики - были решены и оптимизированы. И разработчику не нужно создавать и поддерживать свой инструментарий. Хорошим примером является Nx, который возьмет на себя весь front-ops и даст вам возможность заниматься тем, чем должен заниматься разработчик - разработкой приложений, а не бесконечной настройкой конфигов, и их изменением при обновлении vendors.
Разработка приложений на Typescript с использованием Nx
В данной статье расскажу об использовании Nx для разработки веб приложений на Typescript. Два года назад я написал статью на медиуме - Сборка Typescript приложения с помощью Webpack , где поделился...
habr.com