Предотвращение несоответствующего импорта и обеспечение иерархии проектов в Typescript - PullRequest
5 голосов
/ 16 апреля 2020

В проекте TS я бы хотел заблокировать следующее:

  • Файл из общей папки, импортируемый из папки projectA *
  • Файл из папки projectB, импортируемый из папки projectA

Мне бы хотелось, чтобы было разрешено следующее:

  • Файл из папки projectA, импортируемый из папки projectA
  • Файл из папки projectA, импортируемый из папка общая.

Мне известны ссылки. Однако, как я понимаю, они требуют сборки для проверки типов (если такое разделение выполняется, сначала нужно создать файлы d.ts), чего я бы предпочел избежать.

Какие варианты у меня есть? Можно ли добиться просто через отдельные файлы tsconfig для каждого из этих проектов / папок?

Ответы [ 2 ]

5 голосов
/ 28 апреля 2020

TLDR; Вы действительно должны просто использовать ссылки. Это как раз то, для чего они нужны.

Но давайте сначала обратимся к некоторым вашим конкретным c мыслям:

  1. Возможно ли достичь просто с помощью отдельных Файлы tsconfig для каждого из этих проектов / папок?

    Да, но это не очень гибко. Вы можете выделить common , установив для rootDir значение .. Затем вы получите ошибку '/path/to/projectA' is not under 'rootDir', если попытаетесь импортировать projectA в common . Но чтобы иметь возможность импортировать common в projectA , его rootDir должен быть более глобальным, но тогда это позволит вам импортировать projectB .

    Мало того, согласно документации Project References :

    Раньше с этой структурой было довольно неудобно работать, если вы использовали один файл tsconfig:

    • Для файлов реализации было возможно импортировать тестовые файлы
    • Невозможно построить test и src одновременно без появления src в имени выходной папки, которое вы, вероятно, не хотите
    • Для изменения только внутренностей в файлах реализации требуется проверка типов тесты снова, даже если это не будет новые ошибки
    • Изменение только необходимых тестов снова проверяет реализацию, даже если ничего не изменилось

    Вы можете использовать несколько файлов tsconfig для решать некоторые из этих проблем, , но появятся новые :

    • Нет встроенной современной проверки, таким образом, вы всегда запускаете tsc дважды
    • Invoking tsc в два раза больше накладных расходов времени запуска
    • tsc -w не может работать одновременно с несколькими файлами конфигурации
  2. Мне известны ссылки. Однако, как я понимаю, они требуют сборки для проверки типов (если такое разделение выполняется, сначала нужно создать файлы d.ts), чего я бы предпочел избегать.

    Что такое причина такого отвращения?

    • Если это первоначальные затраты на создание клона проекта fre sh, это будет более чем компенсировано улучшенным временем сборки (см. аргументы для ниже). Преимущества последнего для производительности разработчика намного перевесят затраты первого.

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

    • Если вы хотите иметь возможность перемещаться по fre sh клонировать в редакторе, поддерживающем тип и связь, например VS Code или WebStorm, без необходимости сборки, вы можете добиться этого, проверяя файлы .d.ts в управлении исходным кодом.


    Вот что конкретно написано в документах:

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

Аргумент для проекта Ссылки

Из документов:

  • вы можете значительно улучшить время сборки

    Долгожданный Особенность умных инкрементальных сборок для проектов TypeScript. В версии 3.0 вы можете использовать флаг --build с tsc. По сути, это новая точка входа для tsc, которая ведет себя больше как оркестратор сборки, чем простой компилятор.

    Запуск tsc --build (для краткости tsc -b) сделает следующее:

    • Найти все ссылочные проекты
    • Определить, являются ли они актуальными
    • Сборка устаревших проектов в правильном порядке

    Не беспокойтесь о порядке файлов, передаваемых в командной строке - tsc будет переупорядочивать их при необходимости так что зависимости всегда строятся первыми.

  • обеспечивает логическое разделение между компонентами

  • организовать ваш код в новые и лучшие способы.

В Ссылки на проекты do c есть еще несколько полезных преимуществ / функций.

Пример настройки

  • src/tsconfig.json

    Даже если у вас нет кода на root, этот tsconfig может быть там, где все общие настройки go (остальные наследуют от него), и это позволит просто tsc --build src собрать весь проект (и с помощью --force построить его с нуля).

    {
      "compilerOptions": {
        "rootDir": ".",
        "outDir": "../build",
        "composite": true
      },
      // this root project has no source of its own
      "files": [],
      // but building this project will build all of the following:
      "references": [  
        { "path": "./common" }
        { "path": "./projectA" }
        { "path": "./projectB" }
      ]
    }
    
    • src/common/tsconfig.json

      Поскольку common не имеет ссылок, импорт ограничен целями в его каталоге и npm_modules. Я полагаю, что вы могли бы даже ограничить последний, предоставив ему свой собственный package.json.

          {
           "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/common",
              "composite": true
            }
          }
      
    • src/projectA/tsconfig.json

      projectA может импортировать common из-за заявленного ссылка.

          {
            "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/projectA",
              "composite": true
            },
            "references": [
              { "path": "../common" }
            ]
          }
      
    • src/projectB/tsconfig.json

      projectB можно импортировать common AND projectA из-за объявленных ссылок.

          {
            "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/projectB",
              "composite": true
            },
            "references": [
              { "path": "../common" }
              { "path": "../projectA" }
            ]
          }
      

Builds

Это только некоторые примеры. Я использую сокращенные формы переключателей tsc ниже, например, -b вместо --build. Все команды, выполненные из репо root.

tsc -b src - строит все дерево.

tsc -p src/projectA/ компилирует только projectA.

tsc -b src/projectA/ создает projectA и все устаревшие зависимости.

tsc -b -w src - создает и просматривает все дерево.

tsc -b --clean src - удалить вывод для всего дерева.

tsc -b -f src - принудительно перестроить все дерево.

Используйте переключатель -d или -dry, чтобы получить предварительный просмотр действий tsc -b.

4 голосов
/ 27 апреля 2020

Я предлагаю использовать линтер для этой работы, не нужно настраивать шаг сборки или использовать Project References.

eslint-plugin-import - довольно популярный плагин ESLint, совместимый с TS и можешь делать что хочешь. После настройки typcript-eslint (если это еще не сделано) вы можете поиграться с этими правилами:

Попробуем использовать следующую структуру проекта:

|   .eslintrc.js
|   package.json
|   tsconfig.json
\---src
    +---common
    |       common.ts
    |       
    +---projectA
    |       a.ts
    |       
    \---projectB
            b.ts

.eslintr c. js:

module.exports = {
  extends: ["plugin:import/typescript"],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    sourceType: "module",
    project: "./tsconfig.json",
  },
  plugins: ["@typescript-eslint", "import"],
  rules: {
    "import/no-restricted-paths": [
      "error",
      {
        basePath: "./src",
        zones: [
          // disallow import from projectB in common
          { target: "./common", from: "./projectB" }, 
          // disallow import from projectB in projectA
          { target: "./projectA", from: "./projectB" },     
        ],
      },
    ],
    "import/no-relative-parent-imports": "error",
  },
};

Каждая зона состоит из target path и из path. Цель - это путь, по которому должен применяться ограниченный импорт. Путь from определяет папку, которую нельзя использовать при импорте.

Просмотр файла ./src/common/common.ts:

import { a } from "../projectA/a"; // works 
// Error: Unexpected path "../projectB/b" imported in restricted zone.
import { b } from "../projectB/b";

Правило import/no-relative-parent-imports также жалуется на оба импорта, как для a.ts:

Относительный импорт из родительских каталогов не разрешен. Пожалуйста, либо передайте то, что вы импортируете во время выполнения (внедрение зависимости), переместите common.ts в тот же каталог, что и ../projectA/a, или рассмотрите возможность создания ../projectA/a пакета.

Третье правило import/no-internal-modules не использовался, но я также перечислю его здесь, так как это может быть очень полезно для ограничения доступа к дочерним папкам / модулям и эмуляции (по крайней мере) какого-то внутреннего модификатора пакета в TS.

...