Node.js для создания инструментов разработки

Почему node.js из языка бэкенда превратился в язык разработки тулинга.

Время чтения: меньше 5 мин

Кратко

Секция статьи "Кратко"

Node.js — это платформа, которая позволяет запускать JavaScript вне браузера. Области её применения можно разделить на две категории: создание серверных веб-приложений и создание инструментов для разработки.

Вся современная разработка на JavaScript строится вокруг огромного числа инструментов — тулинга. Большинство этих инструментов используют Node.js.

Тулинг (калька английского слова tooling) — набор инструментов для разработки.

Практически для каждого этапа работы над JS-проектом потребуется инструмент, который использует Node.js. Вот тройка примеров:

  • Разработка требует установки зависимостей через менеджер зависимостей. Все популярные пакетные менеджеры написаны на JavaScript или его диалектах и используют Node.js;
  • Типичный проект требует этапа сборки кода перед отправкой его в браузер. Все популярные сборщики написаны на JavaScript или его диалектах и используют Node.js;
  • Абсолютное большинство проектов используют статический анализ для контроля качества кода. Все популярные статические анализаторы написаны на JavaScript или его диалектах и используют Node.js.

Это означает, что даже разработчикам интерфейсов важно понимать, как работает Node.js, и уметь минимально с ней взаимодействовать.

Как понять

Секция статьи "Как понять"

Обычно мы не запускаем инструменты напрямую с помощью Node.js, а используем для этого пакетный менеджер. Команды, которые используются в проекте, нужно определить в разделе scripts файла package.json:

        
          
          "scripts": {  "lint": "eslint \"./src/**/*.js\""}
          "scripts": {
  "lint": "eslint \"./src/**/*.js\""
}

        
        
          
        
      

Запускается скрипт так:

        
          
          npm run lint
          npm run lint

        
        
          
        
      

Но это просто удобная форма запуска команды:

        
          
          node ./node_modules/.bin/eslint "./src/**/*.js"
          node ./node_modules/.bin/eslint "./src/**/*.js"

        
        
          
        
      

В этой строке происходит сразу несколько вещей: с помощью Node.js мы запускаем файл eslint, который лежит в папке node_modules/.bin, и передаём ему как аргумент строку, по которой ESLint найдёт файлы для анализа.

Файл eslint — это программа, написанная на JavaScript. В ней не происходит никакой магии.

В таком скрипте много кода, но для понимания принципа работы Node.js-инструментов можно его упростить и посмотреть на принципиальную схему. Это не настоящий ESLint, а просто пример того, как может быть устроен скрипт на Node.js:

        
          
          const cliOptions = parseCommandLineArguments(process.args)const configOptions = parseConfigFile('.eslintrs')const options = mergeConfigs([cliOptions, configOptions])const files = findFiles(options)const errors = analyzeFiles(files, options)for (const error of errors) {  console.warn(error)}if (errors.length > 0) {  process.exit(1)} else {  process.exit(0)}
          const cliOptions = parseCommandLineArguments(process.args)
const configOptions = parseConfigFile('.eslintrs')
const options = mergeConfigs([cliOptions, configOptions])
const files = findFiles(options)
const errors = analyzeFiles(files, options)

for (const error of errors) {
  console.warn(error)
}

if (errors.length > 0) {
  process.exit(1)
} else {
  process.exit(0)
}

        
        
          
        
      

А теперь разберём его построчно. Получаем аргументы командной строки, чтобы найти файлы, которые следует анализировать:

        
          
          const cliOptions = parseCommandLineArguments(process.args)
          const cliOptions = parseCommandLineArguments(process.args)

        
        
          
        
      

Читаем файл конфигурации, чтобы решить, какие правила анализа нужно применить:

        
          
          const configOptions = parseConfigFile('.eslintrs')
          const configOptions = parseConfigFile('.eslintrs')

        
        
          
        
      

Собираем финальную конфигурацию:

        
          
          const options = mergeConfigs([cliOptions, configOptions])
          const options = mergeConfigs([cliOptions, configOptions])

        
        
          
        
      

Находим все файлы, удовлетворяющие условию:

        
          
          const files = findFiles(options)
          const files = findFiles(options)

        
        
          
        
      

Ищем нарушения правил в каждом файле и пишем о них в консоль:

        
          
          const errors = analyzeFiles(files, options)for (const error of errors) {  console.warn(error)}
          const errors = analyzeFiles(files, options)

for (const error of errors) {
  console.warn(error)
}

        
        
          
        
      

Если была найдена хоть одна ошибка, завершаем выполнение с кодом 1. Если ошибок не было, завершаем выполнение с кодом 0:

        
          
          if (errors.length > 0) {  process.exit(1)} else {  process.exit(0)}
          if (errors.length > 0) {
  process.exit(1)
} else {
  process.exit(0)
}

        
        
          
        
      

Любая консольная программа при завершении может сообщить операционной системе код результата. Принято возвращать 0 при успешном завершении и 1 при ошибке.

Концептуально, все Node.js инструменты работают похожим образом — читают конфигурацию из файла в проекте, разбирают аргументы командной строки, делают какую-то полезную работу и сообщают код результата операционной системе.

На практике

Секция статьи "На практике"

Игорь Камышев советует

Секция статьи "Игорь Камышев советует"

Версия Node.js

Секция статьи "Версия Node.js"

Node.js очень динамично развивается, новые версии выходят каждые пару месяцев, но не стоит спешить переводить свои проекты на них. Каждые два года выходит LTS-версия платформы, которая характеризуется стабильностью, поддержкой, постоянными исправлениями багов и уязвимостей.

LTS (long-term support) — долгосрочная поддержка, обещание разработчиков поддерживать версию программы дольше, чем другие версии.

Традиционно, LTS-статус получают только чётные версии Node.js. Их и следует использовать при разработке.

❌ Node.js 11 не будет поддерживаться, может быть нестабильной, не стоит её использовать.

✅ Node.js 14 будет поддерживаться разработчиками несколько лет, они будут исправлять баги и закрывать уязвимости, можно брать.

Нативные зависимости

Секция статьи "Нативные зависимости"

Не любую программу есть смысл писать на JavaScript. Например, sharp (пакет для обработки изображений) использует libvips — библиотеку, написанную на C. У такого подхода есть два преимущества:

  1. Можно использовать уже написанный, проверенный код на другом языке (например, C), не переписывать его на JavaScript;
  2. JavaScript — не самый быстрый язык и часто решение на более низкоуровневом языке будет работать сильно быстрее.

Обратная сторона такого подхода — не самая удобная работа с такими зависимостями. Если JavaScript работает гарантированно в любом окружении, которое поддерживает Node.js, то библиотеки написанные на других языках может быть сложно собрать для какой-то конкретной операционной системы, могут возникать проблемы с совместимостью, требоваться установка разных сторонних программ.