Разработка приложений на Typescript с использованием Nx

от автора

В данной статье расскажу об использовании Nx для разработки веб приложений на Typescript.

Два года назад я написал статью на медиуме — Сборка Typescript приложения с помощью Webpack, где поделился своим решением сборки простого приложения на Typescript с помощью Webpack

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

Проблемы собственных решений

Больше трех лет назад я устройся на работу в один небольшой фин. тех стартап Angular разработчиком. И по мере выполнения разных задач, появилась потребность в некоем решении, которое удовлетворяло бы следующим требованиям:

  • Язык разработки Typescript;

  • Livereload при внесении изменений;

  • Есть «prod» сборка, которая оптимизирует исходный код. 

Одним словом все то, что есть в современных JS фреймворках. 

Я просмотрел несколько проектов на github’е, но ни одно решение мне не подошло, так как либо они уже не поддерживались, либо были слишком усложнены. Мне же нужно было что-то простое. 

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

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

Так как проекты жили параллельно, всегда приходилось мигрировать проекты, копируя куски из одного решения в другое. В один момент я даже подумывал о создании своего CLI, но сама мысль об этом явно кричала о том, что я делаю что-то не так. 

Примерно в тоже время, я начал использовать Nx для своих Angular проектов.

Информация о Nx

Nx это набор утилит для создания и управления монорепозиторием. С Nx можно ознакомиться в официальной документации.

Так как статья посвящена разработке typescript приложений, интересен следующий раздел — Nx и TypeScript.

В свое время, часть Angular Team создает свою компанию Nrwl, в которой начинают делать свой инструментарий для упрощения разработки приложений на Angular. Консультации компаний из списка Fortune 500 позволили расширить инструментарий и включить в него не только Angular, а также React, Vue и чистый Typescript.

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

Вот несколько особенностей Nx:

  • вся мощь typescript вместе с eslint;

  • монорепозиторий, который позволяет разрабатывать несколько приложений с единой кодовой базой;

  • генераторы — семейство консольных команд для упрощения создания новых файлов и решений, с возможностью создания собственных шаблонов и команд;

  • конфигурируемые сборки — например, версии для релиза с оптимизацией исходного кода;

  • миграции — при изменении и обновлении системных зависимостей, Nx самостоятельно поправит конфигурационные файлы и приведет все данные к требуемому виду;

  • возможность использовать все современные фреймворки React, Angular, Vue и Svetle.

Полный список можно посмотреть в официальной документации по Nx.

Другими словами, NX — набор утилит, который все делает за вас, и делает это хорошо, но в разумных пределах, конечно.

Предварительная настройка

Для работы с nx вам потребуется установленная NodeJS и один из менеджеров пакетов, таких как npm или yarn.

Ознакомиться с NodeJS можно на сайте — https://nodejs.org/en/docs.

Перед использованием nx, можно установить nx cli глобально:

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 
Запуск команды создания Nx workspace
Запуск команды создания Nx workspace

Введем название workspace — boobs:

Создание boobs Nx workspace
Создание boobs Nx workspace

При при создании workspace можно выбрать тип проекта (angular, react, node или typescript). Данная опция определяет какие зависимости будут включены в package.json.

В качестве проекта выберем ts:

Выбор типа проекта Nx
Выбор типа проекта Nx

Откажемся от использования облака:

Опция выбора использования Nx Cloud
Опция выбора использования Nx Cloud

Если вы не боитесь стать пупыркой, можете попробовать использовать облако.

Далее Nx будет устанавливать необходимые зависимости:

Nx установил зависимости, где сразу предложил пройти туториал 🙂

Перейдем в папку с созданным проектом:

cd boobs

Посмотрим, что создал Nx, выполнив команду ls:

Но так как из консоли ничего непонятно, откроем проект в любимой IDE:

JetBrains, к моему большому сожалению, не хочет подогнать предоставить мне бесплатную версию. Видимо скоро придется перейти на VSCode.

Формально 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 — набор правил запуска приложений и библиотек. Для базовой настройки это можно пока пропустить.

Файл workspace.json будет содержать пути конфигураций для библиотек и приложений:

{   "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

  • приложения — тоже что и библиотеки, только приложение имеет способы запуска отслеживания изменений.

Для примера создадим новое приложение — store:

nx generate @nrwl/js:app store

После создания приложения, глобально появилось несколько файлов:

  • .eslintrc.json — настройки линтера

  • jest.config.js, jest.preset.js — настройка для unit тестирования с помощью jest

Workspace.json добавился новый проект:

{   "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:

Резюме

В данной статье рассказал о таком инструменте как Nx и его использовании при разработке веб приложений. Сделал краткий обзор структуры приложений на Nx, а также привел команды для генерации библиотек и запуска приложений.

В заключении хочется сказать, что инструментарии для веб разработки очень сильно развиваются. Грустно видеть проекты, которые до сих пор используют gulp как основную систему сборки. Нет никаких претензий к gulp, grunt и webpack, просто уже давно много задач, которые решали сборщики — были решены и оптимизированы. И разработчику не нужно создавать и поддерживать свой инструментарий. Хорошим примером является Nx, который возьмет на себя весь front-ops и даст вам возможность заниматься тем, чем должен заниматься разработчик — разработкой приложений, а не бесконечной настройкой конфигов, и их изменением при обновлении vendors.


ссылка на оригинал статьи https://habr.com/ru/post/652453/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *