Сборка TypeScript-проекта с помощью gulp, webpack и линтированием на ESLint

Table of contents

Introduction

Базовый проект для сборки TypeScript при помощи gulp в ES6 и webpack для сборки бандла из отдельных модулей. Для линтирования используется ESLint с настройками как для файлов JS, так и для файлов TS. В качестве примера создан интерфейс логирования с несколькими его реализациями.

Репозиторий с полным кодом проекта: https://github.com/superrosko/example-ts-basic.

Установка пакетов

Инициализация проекта:

npm init

После того, как мы инициировали node-проект и заполнили информацию о нем, нам необходимо установить следующие пакеты:

npm install --save-dev typescript gulp gulp-clean gulp-typescript gulp-eslint eslint eslint-config-google @typescript-eslint/eslint-plugin @typescript-eslint/parser webpack webpack-cli ts-loader clean-webpack-plugin @babel/cli @babel/core @babel/node @babel/preset-env

Настройка TS-проекта

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "src/web-bundle.ts"
  ],
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "allowJs": true,
    "checkJs": true,
    "declaration": true,
    "sourceMap": true,
    "outDir": "./dist/",
    "strict": true,
    "noImplicitAny": true
  }
}

В конфиге мы указываем директорию с исходниками и исключаем из сборки файлы web-бандла.

Настройка babel

Для настройки правил транспиляции создадим в корне проекта файл .babelrc со следующим содержимым:

{
    "presets": [
        "@babel/preset-env"
    ]
}

Настройка ESLint

Для настройки правил проверки создадим файл .eslintrc.json в корне проекта со следующим содержимым:

{
  "env": {
    "es6": true,
    "browser": true,
    "node": true,
    "es2017": true
  },
  "parserOptions": {
    "ecmaVersion": 8,
    "sourceType": "module"
  },
  "ignorePatterns": [
    "dist/**/*"
  ],
  "extends": [
    "eslint:recommended",
    "google"
  ],
  "rules": {
    "no-console": [
      "error",
      {
        "allow": [
          "warn",
          "error",
          "log"
        ]
      }
    ]
  },
  "overrides": [
    {
      "files": [
        "*.ts",
        "*.tsx"
      ],
      "extends": [
        "eslint:recommended",
        "google",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@typescript-eslint/recommended-requiring-type-checking"
      ],
      "plugins": [
        "@typescript-eslint"
      ],
      "parser": "@typescript-eslint/parser",
      "parserOptions": {
        "project": "tsconfig.json",
        "tsconfigRootDir": "."
      }
    }
  ]
}

В приведенном выше конфиге сформированы правила линтирования для файлов JS, а с помощью опции overrides добавлены правила линтирования и для файлов TS.

Настройка gulp

Запуск линтирования и часть сборки мы будем производить с помощью задач gulp, для этого создадим файл конфигурации gulpfile.babel.js в корне проекта со следующим содержимым:

import gulp from 'gulp';
import gulpClean from 'gulp-clean';
import eslint from 'gulp-eslint';
import ts from 'gulp-typescript';

const tsProject = ts.createProject('tsconfig.json');

/**
 * @type {{ts: [string, string]}}
 */
const paths = {
  ts: ['./src/**/*.ts', '!./src/web-bundle.ts'],
};

/**
 * Test TS lint
 * @return {*}
 */
function testTsLint() {
  return gulp.src(paths.ts).
      pipe(eslint()).
      pipe(eslint.format()).
      pipe(eslint.failAfterError());
}

/**
 * Clean dist folder
 * @return {*}
 */
function cleanDist() {
  return gulp.src('dist/*').
      pipe(gulpClean({force: true}));
}

/**
 * Build JS from TS
 * @return {*}
 */
function buildJS() {
  return tsProject.src().pipe(tsProject()).js.pipe(gulp.dest('dist'));
}

const tests = gulp.parallel(testTsLint);
exports.tests = tests;

const clean = gulp.series(cleanDist);
exports.clean = clean;

const build = gulp.series(tests, cleanDist, buildJS);
exports.build = build;

export default build;

Созданы всего 3 задачи:

  • cleanDist - для очистки предыдущих сборок.
  • buildJS - собственно сама сборка JS из TS.
  • testTsLint - запуск линтирования.

Настройка webpack

Запуск сборки web-бандла мы будем производить с помощью задач webpack, для этого создадим файл конфигурации webpack.config.js в корне проекта со следующим содержимым:

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin;

const buildConfig = {
  entry: {
    'web-bundle': './src/web-bundle.ts',
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
};

const outputExampleWebVoiceControl = Object.assign({}, buildConfig, {
  output: {
    filename: 'web-bundle.js',
    path: path.resolve(__dirname, 'examples/example-web'),
  },
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [
        path.resolve(__dirname,
            './examples/example-web/web-bundle.js'),
      ],
    }),
  ],
});

module.exports = [outputExampleWebVoiceControl];

Настройка npm

Задачи линтирования и сборки созданы, теперь добавим команды в package.json:

{
  "scripts": {
    "build:dev": "gulp build && webpack --mode=development",
    "build:prod": "gulp build && webpack --mode=production",
    "test": "gulp tests"
  }
}

Запуск сборки и линтирования

В директории ./src создаем тестовый проект. Интерфейс логирования ILog.ts:

interface ILog {
  log(message: string): boolean;
}

А также его реализацию для консоли ConsoleLog.ts:

/**
 * Class ConsoleLog
 */
class ConsoleLog implements ILog {
  /**
   * log
   * @param {string} message
   * @return {boolean}
   */
  public log(message: string): boolean {
    console.log(message);
    return true;
  }
}

export {ConsoleLog};

И реализацию для web TextAreaLog.ts:

/**
 * Class TextAreaLog
 */
class TextAreaLog implements ILog {
  private element?: HTMLTextAreaElement;

  /**
   * setTextAreaElement
   * @param {HTMLTextAreaElement} element
   */
  public setTextAreaElement(element: HTMLTextAreaElement): void {
    this.element = element;
  }

  /**
   * log
   * @param {string} message
   * @return {boolean}
   */
  public log(message: string): boolean {
    if (this.element !== undefined) {
      this.element.value = this.element.value + message + '\n';
      return true;
    }
    return false;
  }
}

export {TextAreaLog};

Для использования в web создадим бандл:

import {TextAreaLog} from './classes/TextAreaLog';
import {ConsoleLog} from './classes/ConsoleLog';

const logTextArea = new TextAreaLog();
const logConsole = new ConsoleLog();

declare global {
  interface Window {
    logTextArea: TextAreaLog;
    logConsole: ConsoleLog;
  }
}
window.logTextArea = logTextArea;
window.logConsole = logConsole;

Для использования вне бандла мы экспортируем необходимый функционал в window.

После запуска команды build:dev или build:prod в директории ./dist будут созданы сборки. Примеры использования сборок находятся в директории ./examples.

Консольный пример example-node-console:

const {ConsoleLog} = require('../../dist/classes/ConsoleLog');

const logConsole = new ConsoleLog();

logConsole.log('First message');
logConsole.log('Second message');

Для запуска используем команду:

node index.babel.js

Консольный пример example-babel-node-console с синтаксисом ES6:

import {ConsoleLog} from '../../dist/classes/ConsoleLog';

const logConsole = new ConsoleLog();

logConsole.log('First message');
logConsole.log('Second message');

Для запуска используем команду:

babel-node index.babel.js

Пример для web example-web:

<!DOCTYPE html>
<html lang="en">

<head>
    <title>TypeScript example basic!</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="style.css">
</head>

<body>
<div class="row">
    <div id="log">
        <label>
            <textarea id="textarea" rows="5" cols="33"></textarea>
        </label>
    </div>
</div>
<script src="web-bundle.js"></script>
<script src="log.js"></script>
</body>

</html>
window.logConsole.log('First message');
window.logConsole.log('Second message');

const textarea = document.getElementById('textarea');
window.logTextArea.setTextAreaElement(textarea);
window.logTextArea.log('First message');
window.logTextArea.log('Second message');

Файл web-bundle.js собирается из web-bundle.ts.

Для запуска используем команду в корне приложения, демонстрирующего пример работы с web:

http-server

Затем открываем предложенную ссылку в браузере.

Настройка линтирования в PHPStorm

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

File | Settings | Languages & Frameworks | JavaScript | Code Quality Tools | ESLint

Выберем Manual ESLint configuration и сохраним настройки. Теперь и PHPStorm будет подсвечивать несоблюдение правил линтирования при редактировании. А также в контекстном меню при клике по файлу js появится пункт Fix ESLint Problems, который будет исправлять файл в соответствии со стандартом линтирования.

Дата публикации :
Дата редактирования : 2020-11-12 00:40:45
Автор :

Cookies and IP addresses allow us to deliver and improve our web content, resolve technical errors, and provide you with a personalized experience. Our website uses cookies and collects your IP address for these purposes.