Как создать и настроить Angular проект с нуля


Короткое вступление


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


Подготовка окружения для разработки


Git


Первое что должно быть установлено на любом окружении для разработки это Git. Скачайте и установите Git на свою операционную систему.


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


// Для глобальной настройки (для всех проектов) используйте команды
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

// Для настройки конкретного проект перейдите в папку с проектом и выполните
$ git config user.name "John Doe"
$ git config user.email johndoe@example.com

NodeJs и NVM


Следующее что нужно настроить для работы с Angular это NodeJs. Лучший подход на данный момент это установить NodeJs с помощью Node Version Manager. Это даст возможность легко переключаться на любую версию и даже делать это автоматически для каждого проекта.


  1. На MacOs перед установкой необходимо создать файл .zshrc в домашней директории.

$ touch ~/.zshrc

  1. Выполните одну из следующих команд для установки или обновления NVM. Версия в команде может обновиться, поэтому проверьте последнюю версию тут.

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash
$ wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash

  1. Установите последнюю LTS версию NodeJs.

$ nvm install 'lts/*'

  1. Установите ее как default.

$ nvm use 'lts/*'
$ nvm alias default 'lts/*'

  1. На MacOs добавьте следующий код в ~/.zshrc файл для вызова nvm use автоматически когда переходите в директорию которая содержит .nvmrc файл со строкой указывающей nvm какую версию nodejs использовать. Для других операционных систем читайте мануал.

# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
  local node_version="$(nvm version)"
  local nvmrc_path="$(nvm_find_nvmrc)"

  if [ -n "$nvmrc_path" ]; then
    local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")

    if [ "$nvmrc_node_version" = "N/A" ]; then
      nvm install
    elif [ "$nvmrc_node_version" != "$node_version" ]; then
      nvm use
    fi
  elif [ "$node_version" != "$(nvm version default)" ]; then
    echo "Reverting to nvm default version"
    nvm use default
  fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc

Установка CLI и создание проекта


  1. Установите CLI используя менеджер пакетов npm.

$ npm install -g @angular/cli

  1. Перейдите в папку со своим проектом и создайте пустой Angular проект

$ cd ~/Projects
$ ng new --create-application false --new-project-root apps project-name

Мы создали пустой проект с настроенной директорией apps для новых приложений. Другими словами мы подготовили основу для монорепозитория.


  1. Создайте и сделайте коммит первого приложения в монорепозитории.

$ cd project-name
$ ng generate application --routing true --style scss website
$ git add .
$ git commit -m 'website application created'

Для создания дополнительных приложений просто повторите этот шаг с другим именем приложения вместо website.


Создание библиотек


Я обычно советую создавать библиотеки в папке libs. Для этого нам необходимо изменить одну строку в angular.json файле:


"newProjectRoot": "apps",

На следующую строку:


"newProjectRoot": "libs",

И выполнить команду:


$ ng generate library @group-name/lib-name

Мы добавили group-name для более наглядной группировки библиотек по домену или какому то другому принципу. Также если вы захотите опубликовать библиотеку например в npm, сначала необходимо создать организацию тут с названием соответствующим group-name. Это как namespace для группы связанных библиотек, например:


"@angular/common"
"@angular/compiler"
"@angular/core"
"@angular/forms"

Я более подробно опишу процесс публикации библиотек в npm в будущих статьях. А пока просто следуйте этому правилу.


И не забудьте вернуть angular.json к прежнему состоянию: "newProjectRoot": "apps"


Дополнительная конфигурация


NodeJs version


Добавьте .nvmrc файл с текстом lts/* в нем. Так же можно указать конкретную версию node, например 12.13.1. Эта версия будет автоматически установлена если вы следовали моим предыдущим инструкциям. Также можно активировать эту версию вручную с помощью команды $ nvm use.


$ touch .nvmrc
$ echo "lts/*" > .nvmrc

Строгие проверки на null и undefined


Добавьте следующее правило в tsconfig.json чтоб включить строгую проверку на null и undefined. Это защитит вас от распространённой ошибки чтения свойства у undefined или null переменной.


"compilerOptions": {
    "strictNullChecks": true,
}

Или лучше, если вы хотите больше строгих проверок включите все правила в компиляторе:


/* Strict Type-Checking Options */
"strict": true,                           /* Enable all strict type-checking options. */
// "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true,              /* Enable strict null checks. */
// "strictFunctionTypes": true,           /* Enable strict checking of function types. */
// "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

/* Additional Checks */
"noUnusedLocals": true,                   /* Report errors on unused locals. */
"noUnusedParameters": true,               /* Report errors on unused parameters. */
"noImplicitReturns": true,                /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true,       /* Report errors for fallthrough cases in switch statement. */

Для того, чтобы TypeScript был более строгим не только TS-файлах, но в Angular-шаблонах нужно включать опцию 'fullTemplateTypeCheck' в tsconfig.json:


{
    "compilerOptions": { ... },
    "angularCompilerOptions": {
        "fullTemplateTypeCheck": true
        ...
    }
}

После добавления этих правил попробуйте собрать проект и пофиксить все найденные ошибки.


$ npm run build

Строгие правила TsLint


Включите все tslint правила на самые строгие настройки, для этого просто замените в tslint.json


"extends": "tslint:recommended",

На


"extends": "tslint:all",

И добавьте следующие правила чтобы отключить некоторые лишние правила


"no-implicit-dependencies": false,
"no-submodule-imports": false,
"completed-docs": false,
"prefer-function-over-method": false,
"file-name-casing": [
  true,
  "kebab-case"
],
"no-object-literal-type-assertion": [
  true,
  {
    "allow-arguments": true
  }
],
"no-floating-promises": false,
"promise-function-async": false,
"no-unsafe-any": false,
"no-any": false,
"comment-format": [
  true,
  "check-space"
],
"newline-per-chained-call": false,
"typedef": [
  true,
  "call-signature",
  "arrow-call-signature",
  "parameter",
  "arrow-parameter",
  "property-declaration",
  "object-destructuring",
  "array-destructuring"
]

В процессе разработки отключайте или настраивайте те правила которые вам не нравятся. И не забудьте включить поддержку tslint в своей IDE.


После добавления этих правил попробуйте прогнать линтером проект и пофиксить все ошибки.


$ npm run lint

Lint SCSS с помощью stylelint


Добавьте stylelint для проверки качества вашего sсss кода


$ npm install stylelint stylelint-config-standard --save-dev
$ touch .stylelintrc

И настройте правила в .stylelintrc


{
  "extends": "stylelint-config-standard",
  "rules": {}
}

Добавьте следующие строки для запуска stylelint в package.json


"scripts": {
    "lint-all-scss": "stylelint \"**/*.scss\"",
    "lint-all-scss-fix": "npm run lint-all-scss -- --fix"
}

В процессе разработки отключите или настройте те правила которые вас не устраивают. И не забудьте включить поддержку stylelint в своей IDE


$ npm run lint-all-scss-fix

Prettier


Prettier используется для форматирования кода. Для некоторых строк кода Prettier выдает вам не тот результат который вам нравится, но не смотря на этопреимущества от его использования гораздо больше.


$ npm install --save-dev --save-exact prettier

Добавьте в проект файл конфигурации .prettierrc


{
  "printWidth": 120,
  "tabWidth": 2,
  "arrowParens": "always",
  "bracketSpacing": true,
  "endOfLine": "lf",
  "semi": true,
  "singleQuote": true,
  "quoteProps": "consistent",
  "trailingComma": "all"
}

И файл .prettierignore с игнорируемыми файлами и папками


karma.conf.js
protractor.conf.js
ng-package.json
package.json
tsconfig.lib.json
tsconfig.app.json
tsconfig.spec.json
tslint.json
tsconfig.json
browserslist
.gitkeep
favicon.ico

Add scripts for running prettier in package.json


"scripts": {
    "prettier": "prettier --write \"{apps,libs}/**/*\"",
    "prettier:check": "prettier --check \"{apps,libs}/**/*\""
},

После этого запустите prettier чтоб пофиксить все файлы в проекте


$ npm run prettier

HOOKS


Для того чтобы запускать линтеры, форматтеры или другие скрипты мы можем настроить git хуки. Мы будем использовать следующие пакеты для этого:


  • husky — устанавливает хуки
  • pretty-quick — запускает prettier только для измененных файлов

$ npm i husky pretty-quick -D

Следующий шаг написать скрипты для наших хуков. Я предпочитаю создать папку tools для shell скриптов. Мы настроим 3 хука:


  1. pre-commit — Хук который выполняется перед коммитом. В этом худе будем выполнять prettier на измененных файлах.
  2. commit-msg — Хук в котором можно изменить текст коммита, мы используем его для того чтоб добавить имя ветки к тексту коммита.
  3. pre-push — Хук который выполняется перед git push. Используем его для запуска линтеров.

Конфигурация Husky в package.json:


"husky": {
    "hooks": {
        "pre-commit": "pretty-quick --staged",
        "commit-msg": "node ./tools/commit-msg.js",
        "pre-push": "./tools/pre-push.sh"
    }
},

Скрипт ./tools/commit-msg.js


const fs = require('fs');
const { execSync } = require('child_process');

const message = fs.readFileSync(process.env.HUSKY_GIT_PARAMS, 'utf8').trim();
const currentBranch = getCurrentBranch();

fs.writeFileSync(process.env.HUSKY_GIT_PARAMS, `${currentBranch}: ${message}`);
process.exit(0);

function getCurrentBranch() {
  const branches = execSync('git branch', { encoding: 'utf8' });
  return branches
    .split('\n')
    .find((b) => b.charAt(0) === '*')
    .trim()
    .substring(2);
}

Скрипт ./tools/pre-push.sh


#!/usr/bin/env bash

npm run lint-all-scss || exit
npm run lint || exit

Также вы можете добавить запуск тестов и e2e к pre-push скрипту.


COMING SOON…


Эта статья первая в серии о настройке проекта Angular. В следующих статьях я планирую раскрыть следующие темы:


  • Настройка continuous integration с помощью Travis и Docker
  • Server side rendering
  • Переводы
  • Тесты
  • Управление состоянием и структурой модулей
  • Моно репозиторий с Nrwl.Nx

Подписывайтесь, задавайте вопросы. Также вы можете посмотреть пример описанный в этой статье в этом репозитории.




Также присоединяйтесь к нашему сообществу на Medium, Telegram или Twitter.

Комментарии (6)


  1. kubk
    21.12.2019 16:41

    Здорово, что пишете о необходимости строгих проверок, но тема строгости не раскрыта. Мало выставить строгие проверки, нужно ещё код писать по-другому — где-то дополнительно описывать типы, где-то добавить проверки для сужения типов. К примеру, с выключенным «strictNullChecks» такой код успешно скомпилируется:

    const foo: number = null;
    

    Тип объявлён как number, а присвоить туда можно null. Такой код — источник известной проблемы Null Pointer Exception. Тут нужно либо сменить тип на number | null либо проанализировать код и задаться вопросом — а можно ли здесь вообще обойтись без null? Если foo сразу инициализовать числом, то null окажется ненужным. Необходимость писать код по-другому касается и других строгих опций, в особенности «strictPropertyInitialization» и «noImplicitAny».

    Для того, чтобы TypeScript был более строгим не только TS-файлах, но в Angular-шаблонах нужно включать опцию 'fullTemplateTypeCheck' в tsconfig.json:

    {
        "compilerOptions": { ... },
        "angularCompilerOptions": {
            "fullTemplateTypeCheck": true
            ...
        }
    }

    Половина опций в вашем tsconfig.json лишняя, так как strict: true уже включает в себя «noImplicitAny», «noImplicitThis», «alwaysStrict», «strictBindCallApply», «strictNullChecks», «strictFunctionTypes» и «strictPropertyInitialization»: www.typescriptlang.org/docs/handbook/compiler-options.html

    Так и не понял зачем в статью о строгом Ангуляре включать настройку гита и баш-скрипты для NVM под макось.


    1. misticwonder Автор
      22.12.2019 17:45

      Спасибо за комментарий. Добавил в статью про fullTemplateTypeCheck.


  1. virtualtoy
    21.12.2019 19:03

    Используйте ESLint вместо TSLint https://github.com/palantir/tslint/issues/4534


    1. misticwonder Автор
      22.12.2019 17:38

      Пусть об этом позаботится команда Angular CLI. На данный момент я считаю что eslint еще не готов заменить все фичи tslint для typescript и шаблонов Angular.


  1. darkneo
    22.12.2019 15:57

    Это лучший мануал, что я видел, ведь только в нем описано то, чего так не достает всем остальным!


    husky — устанавливает руки

    Без этого нельзя разрабатывать на любом языке программирования!


    1. misticwonder Автор
      22.12.2019 17:36

      )) Забавно получилось. Спасибо что заметили, исправил.