Typescript: Поддержка «средних» проектов

Созданный на 10 июн. 2015  ·  147Комментарии  ·  Источник: microsoft/TypeScript

Прослушивание @nycdotnet вдохновило меня на решение этой проблемы. Спасибо, Стив. (кстати, вы можете посмотреть его хорошее интервью здесь: http://www.dotnetrocks.com/default.aspx?showNum=1149)

Предложение здесь было впервые выдвинуто в доисторические времена (еще до # 11), когда динозавры ходили по выжженной земле. Хотя в этом предложении нет ничего нового, я считаю, что пора заняться этим вопросом. Собственное предложение Стива - № 3394.

Эта проблема

В настоящее время в TypeScript довольно легко начать и начать, и мы делаем это проще с каждым днем ​​(с помощью таких вещей, как # 2338 и работы над System.js). Это замечательно. Но по мере роста размера проекта возникает небольшое препятствие. В настоящее время у нас есть ментальная модель, которая выглядит примерно так:

  • Небольшие проекты: используйте tsconfig.json, сохраните большую часть исходного кода в текущем каталоге.
  • Крупногабаритные проекты: используйте кастомные сборки, размещайте исходный код там, где он вам нужен

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

Стив в своем интервью указывает, что это не совсем правильная модель мира, и я склонен с ним согласиться. Вместо этого есть три размера проекта:

  • Небольшие проекты: используйте tsconfig.json, сохраните большую часть исходного кода в текущем каталоге.
  • Проекты среднего размера: со стандартными сборками и общими компонентами.
  • Крупногабаритные проекты: используйте кастомные сборки, размещайте исходный код там, где он вам нужен

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

Предложение

Чтобы решить эту проблему, я предлагаю поддержать «средние» проекты. Эти проекты имеют стандартные шаги сборки, которые сегодня можно описать в tsconfig.json, за исключением того, что проект построен из нескольких компонентов. Гипотеза заключается в том, что на этом уровне существует довольно много проектов, которым такая поддержка могла бы помочь.

Цели

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

Нецелевые

Это предложение _не_ включает необязательную компиляцию или какие-либо действия, выходящие за рамки того, что компилятор выполняет сегодня. Это предложение также не распространяется на комплектование или упаковку, которые будут рассматриваться в отдельном предложении. Короче говоря, как указано в названии, это предложение охватывает только «средние» проекты, а не потребности крупных.

Дизайн

Для поддержки проектов среднего размера мы сосредотачиваемся на том, что один tsconfig.json ссылается на другой.

Пример сегодняшнего tsconfig.json :

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    },
    "files": [
        "core.ts",
        "sys.ts"
    ]
}

Предлагаемый раздел tsconfig.json "зависимости":

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    },
    "dependencies": [
        "../common", 
        "../util"
    ],
    "files": [
        "core.ts",
        "sys.ts"
    ]
}

Зависимости указывают на:

  • Каталог, в котором можно найти tsconfig.json
  • Tsconfig.json напрямую

Зависимости иерархические. Чтобы отредактировать весь проект, вам нужно открыть правильный каталог, содержащий корень tsconfig.json. Это означает, что зависимости не могут быть циклическими. Хотя в некоторых случаях может быть возможно обрабатывать циклические зависимости, в других случаях, а именно с типами, которые имеют циклические зависимости, может оказаться невозможным выполнить полное разрешение.

Как это работает

Сначала создаются зависимости в том порядке, в котором они перечислены в разделе «зависимости». Если не удается построить зависимость, компилятор завершит работу с ошибкой и не продолжит сборку остальной части проекта.

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

Если пользователь указывает подкаталог как зависимость, а также подразумевает его компиляцию, не предоставляя раздел «файлы», зависимость компилируется во время компиляции зависимости и также удаляется из компиляции текущего проекта.

Языковая служба может видеть каждую зависимость. Поскольку каждая зависимость будет управляться собственным файлом tsconfig.json, это может означать, что потребуется создать несколько экземпляров языковой службы. Конечным результатом будет скоординированная языковая служба, способная выполнять рефакторинг, навигацию по коду, находить все ссылки и т. Д. В зависимости.

Ограничения

Добавление каталога в качестве зависимости, у которого нет tsconfig.json, считается ошибкой.

Предполагается, что выходные данные зависимостей автономны и отделены от текущего проекта. Это означает, что вы не можете объединить выходной файл .js зависимости с текущим проектом через tsconfig.json. Внешние инструменты, конечно, могут предоставить эту функциональность.

Как упоминалось ранее, циклические зависимости считаются ошибкой. В простом случае:

А - Б
\ C

A - это «текущий проект» и зависит от двух зависимостей: B и C. Если B и C сами по себе не имеют зависимостей, этот случай тривиален. Если C зависит от B, B становится доступным для C. Это не считается циркулярным. Однако, если B зависит от A, это считается циклическим и будет ошибкой.

Если в этом примере B зависит от C, а C является самодостаточным, это не будет считаться циклом. В этом случае порядок компиляции будет C, B, A, что соответствует логике, которая у нас есть для /// ref.

Дополнительные оптимизации / улучшения

Если зависимость не должна быть перестроена, то ее этап сборки пропускается и повторно используется представление ".d.ts" из предыдущей сборки. Это может быть расширено для обработки, если при компиляции зависимостей были построены зависимости, которые позже появятся в списке «зависимости» текущего проекта (как это произошло в примере, приведенном в разделе «Ограничения»).

Вместо того, чтобы рассматривать каталоги, переданные как зависимости, которые не имеют tsconfig.json, как случаи ошибок, мы могли бы при желании применить к этой зависимости «файлы» по умолчанию и настройки текущего проекта.

Committed Monorepos & Cross-Project References Suggestion

Самый полезный комментарий

Работа над документами / публикациями в блоге ниже (будет отредактирована на основе отзывов)

Я бы посоветовал всем, кто следит за этой веткой, попробовать. Сейчас я работаю над сценарием монорепозитория, чтобы сгладить любые последние ошибки / функции, и скоро у меня появятся некоторые рекомендации по этому поводу.


Ссылки на проекты

Ссылки на проекты - это новая функция в TypeScript 3.0, которая позволяет вам разбивать ваши программы на TypeScript на более мелкие части.

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

Мы также представляем новый режим для tsc , флаг --build , который работает рука об руку со ссылками на проекты, чтобы обеспечить более быструю сборку TypeScript.

Пример проекта

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

/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json

Тестовые файлы импортируют файлы реализации и проводят некоторое тестирование:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

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

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

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

  • Встроенной проверки обновлений нет, поэтому вы всегда запускаете tsc дважды.
  • Вызов tsc дважды увеличивает накладные расходы времени на запуск
  • tsc -w не может работать одновременно с несколькими файлами конфигурации

Ссылки на проекты могут решить все эти и многие другие проблемы.

Что такое ссылка на проект?

tsconfig.json files имеют новое свойство верхнего уровня references . Это массив объектов, который определяет проекты для ссылки:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Свойство path каждой ссылки может указывать на каталог, содержащий файл tsconfig.json , или на сам файл конфигурации (который может иметь любое имя).

Когда вы ссылаетесь на проект, происходят новые вещи:

  • При импорте модулей из проекта, на который имеется ссылка, вместо этого будет загружен его выходной файл декларации ( .d.ts ).
  • Если указанный проект создает outFile , объявления выходного файла .d.ts будут видны в этом проекте.
  • В режиме сборки (см. Ниже) при необходимости автоматически создается проект, на который имеется ссылка.

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

composite

В ссылочных проектах должна быть включена новая настройка composite .
Этот параметр необходим для того, чтобы TypeScript мог быстро определить, где найти выходные данные проекта, на который имеется ссылка.
Включение флага composite меняет несколько вещей:

  • Параметр rootDir , если он не установлен явно, по умолчанию соответствует каталогу, содержащему файл tsconfig
  • Все файлы реализации должны соответствовать шаблону include или перечислены в массиве files . Если это ограничение нарушено, tsc сообщит вам, какие файлы не были указаны
  • declaration должен быть включен

declarationMaps

Мы также добавили поддержку карт источников объявлений .
Если вы включите --declarationMap , вы сможете использовать такие функции редактора, как «Перейти к определению» и «Переименовать», чтобы прозрачно перемещаться и редактировать код через границы проекта в поддерживаемых редакторах.

prepend с outFile

Вы также можете включить добавление вывода зависимости, используя параметр prepend в ссылке:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

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

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

  ^ ^ 
 /   \
B     C
 ^   ^
  \ /
   D

В этой ситуации важно не добавлять в начало каждой ссылки, потому что в результате вы получите две копии A в выводе D - это может привести к неожиданным результатам.

Предостережения для ссылок на проекты

Ссылки на проекты имеют несколько недостатков, о которых следует знать.

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

Кроме того, чтобы сохранить совместимость с существующими рабочими процессами сборки, tsc не будет автоматически строить зависимости, если не вызывается с помощью переключателя --build .
Давайте узнаем больше о --build .

Режим сборки для TypeScript

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

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

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

Вы можете указать tsc -b с несколькими путями к файлам конфигурации (например, tsc -b src test ).
Как и в случае с tsc -p , указывать само имя файла конфигурации не нужно, если он называется tsconfig.json .

tsc -b Командная строка

Вы можете указать любое количество конфигурационных файлов:

 > tsc -b                                # Build the tsconfig.json in the current directory
 > tsc -b src                            # Build src/tsconfig.json
 > tsc -b foo/release.tsconfig.json bar  # Build foo/release.tsconfig.json and bar/tsconfig.json

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

Также есть некоторые флаги, специфичные для tsc -b :

  • --verbose : распечатывает подробный журнал, чтобы объяснить, что происходит (может быть объединен с любым другим флагом)
  • --dry : показывает, что нужно сделать, но на самом деле ничего не строит
  • --clean : Удаляет выходные данные указанных проектов (может сочетаться с --dry )
  • --force : действовать так, как будто все проекты устарели
  • --watch : режим наблюдения (не может сочетаться с каким-либо флагом, кроме --verbose )

Предостережения

Обычно tsc будет выдавать выходные данные ( .js и .d.ts ) при наличии синтаксических ошибок или ошибок типа, если только noEmitOnError не включен.
Делать это в системе инкрементной сборки было бы очень плохо - если бы одна из ваших устаревших зависимостей имела новую ошибку, вы бы увидели ее только один раз, потому что последующая сборка пропустила бы сборку теперь актуального проекта.
По этой причине tsc -b действует так, как будто noEmitOnError включен для всех проектов.

Если вы проверяете какие-либо результаты сборки ( .js , .d.ts , .d.ts.map и т. Д.), Вам может потребоваться запустить сборку --force после определенного элемента управления версиями операции в зависимости от того, сохраняет ли ваш инструмент управления версиями карты времени между локальной копией и удаленной копией.

msbuild

Если у вас есть проект msbuild, вы можете включить режим сборки, добавив

    <TypeScriptBuildMode>true</TypeScriptBuildMode>

в ваш proj файл. Это включит автоматическую инкрементную сборку, а также очистку.

Обратите внимание, что, как и в случае с tsconfig.json / -p , существующие свойства проекта TypeScript не будут соблюдаться - всеми настройками следует управлять с помощью файла tsconfig.

Некоторые группы настроили рабочие процессы на основе msbuild, в которых файлы tsconfig имеют тот же неявный порядок графов, что и управляемые проекты, с которыми они связаны.
Если ваше решение похоже на это, вы можете продолжать использовать msbuild с tsc -p вместе со ссылками на проекты; они полностью совместимы.

Руководство

Общая структура

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

Еще одна хорошая практика - иметь файл «решения» tsconfig.json который просто содержит references для всех ваших проектов листовых узлов.
Это простая точка входа; например, в репозитории TypeScript мы просто запускаем tsc -b src для создания всех конечных точек, потому что мы перечисляем все подпроекты в src/tsconfig.json
Обратите внимание, что начиная с версии 3.0 пустой массив files больше не является ошибкой, если у вас есть хотя бы один reference в файле tsconfig.json .

Вы можете увидеть этот шаблон в репозитории TypeScript - см. Ключевые примеры src/tsconfig_base.json , src/tsconfig.json и src/tsc/tsconfig.json .

Структурирование относительных модулей

В общем, для перехода репо с использованием относительных модулей требуется не так уж много.
Просто поместите файл tsconfig.json в каждый подкаталог данной родительской папки и добавьте reference s к этим файлам конфигурации, чтобы они соответствовали предполагаемому распределению уровней программы.
Вам нужно будет либо установить outDir в явную подпапку выходной папки, либо установить rootDir в общий корень всех папок проекта.

Структурирование outFiles

Макет для компиляций с использованием outFile более гибкий, потому что относительные пути не имеют большого значения.
Следует иметь в виду, что обычно вы не хотите использовать prepend до «последнего» проекта - это сократит время сборки и уменьшит количество операций ввода-вывода, необходимых в любой данной сборке.
Сам репозиторий TypeScript может служить здесь хорошей ссылкой - у нас есть несколько «библиотечных» проектов и несколько проектов «конечных точек»; "конечные" проекты сохраняются как можно меньше и включают только те библиотеки, которые им нужны.

Структурирование для монорепозитория

TODO: Больше экспериментируйте и выясните это. У Раша и Лерны разные модели, которые подразумевают разные вещи с нашей стороны.

Все 147 Комментарий

О боже!

: +1:

Ага! Это имеет смысл для представленных вами вариантов использования. Предоставление инструментов, которые мы используем, с лучшим представлением о том, как предполагается использовать наш код, определенно является правильным решением. Каждый раз, когда я по ошибке F12 открываю файл .d.ts, мне хочется задушить котенка!

Джонатан,

Большое спасибо за любезный отзыв и за решение взяться за дело. TypeScript - такой замечательный инструмент, и эта функциональность поможет многим людям, которые хотят разбить на компоненты свои кодовые базы среднего размера, которые не могут оправдать неэффективность, требуемую огромными проектами, которые полагаются на строгое разделение обязанностей (например, порталы Azure или проект Monacos of мир с> 100kloc и множеством независимых команд). Другими словами, это действительно поможет «обычным людям». Кроме того, другие уже предлагали что-то для этого, например @NoelAbrahams (# 2180) и другие, поэтому я не могу утверждать здесь оригинальность. Это просто то, в чем я нуждался некоторое время.

Я считаю, что ваше предложение отличное. Единственный недостаток, который я вижу по сравнению с моим предложением (# 3394), которое я сейчас закрыл, - это отсутствие резервного механизма для ссылок.

Рассмотрим следующий реальный сценарий, который я подробно описал здесь: https://github.com/Microsoft/TypeScript/issues/3394#issuecomment -109359701

У меня есть проект TypeScript grunt-ts, который зависит от другого проекта csproj2ts. Вряд ли кто-либо, кто работает с grunt-ts, также захочет работать с csproj2ts, поскольку он имеет очень ограниченный набор функций. Однако для кого-то вроде меня - было бы здорово иметь возможность работать над обоими проектами одновременно и проводить рефакторинг / переходить к определению / находить все ссылки в них.

Когда я сделал свое предложение, я предложил, чтобы объект зависимостей был литералом объекта с именованными резервными вариантами. Версия, более соответствующая вашему предложению, будет:

"dependencies": {
   "csproj2ts": ["../csproj2ts","node_modules/csproj2ts/csproj2ts.d.ts"],
   "SomeRequiredLibrary": "../SomeRequiredLibraryWithNoFallback"
}

Чтобы упростить это все еще массив, я предлагаю следующую альтернативную реализацию будущего гипотетического dependencies сечения чернового-ц tsconfig.json файла:

"dependencies": [
   ["../csproj2ts","node_modules/csproj2ts/csproj2ts.d.ts"],
   "../SomeRequiredLibraryWithNoFallback"
]

Правило разрешения для каждого элемента типа массива в dependencies будет следующим: _first_ элемент, который находится в каждом, является тем, который используется, а остальные игнорируются. Элементы строкового типа обрабатываются так же, как указано в предложении Джонатана.

Это решение немного сложнее в реализации, однако оно дает разработчику (и авторам библиотеки) гораздо большую_ гибкость. Для разработчиков, которым не нужно разрабатывать csproj2ts (и, следовательно, у них нет файла ../csproj2ts/tsconfig.json ), зависимость будет просто файлом определения, который добавляется в контекст компиляции. Для разработчиков, у которых _do_ есть файл ../csproj2ts/tsconfig.json , предложение будет работать точно так, как вы описали выше.

В приведенном выше примере "../SomeRequiredLibraryWithNoFallback" должен быть там, как и в вашем существующем предложении, и его отсутствие будет ошибкой компилятора.

Большое вам спасибо за то, что обдумали это.

Здесь есть две проблемы, которые нам нужно разделить: есть сборка и есть поддержка языковых служб.

Я не думаю, что для сборки tsconfig - подходящее место для этого. это явно проблема системы сборки. с этим предложением компилятор машинописного текста должен работать в следующих сферах:

  • выяснение зависимости
  • актуальные проверки утверждений
  • управление конфигурацией (выпуск или отладка)

За все это явно несут ответственность системы сборки; это сложные проблемы, и уже есть инструменты, которые это делают, например MSBuild, grunt, gulp и т. д.
Как только tsconfig и tsc станут драйвером сборки, вы захотите, чтобы он использовал все ЦП для создания несвязанных поддеревьев или имел команды post и pre build для каждого проекта, а также, возможно, строил другие проекты. Опять же, я думаю, что существуют инструменты сборки, которые хороши в том, что они делают, и нам нет необходимости воссоздавать это.

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

"files" : [
    "file1.ts",
    {
        "path": "../projectB/out/projectB.d.ts",
         "sourceProject": "../projectB/"
     }
]

Где tsc будет смотреть только на «путь», но инструменты могут смотреть на другую информацию и стараться быть максимально полезными.

Я признаю существование проблемы, но не думаю, что объединение сборки и инструментов является правильным решением. tsconfig.json должен оставаться пакетом конфигурации (т.е. альтернативой json файлам ответов) и не становиться системой сборки. один tsconfig.json представляет собой один вызов tsc. tsc должен оставаться единым компилятором проекта.

Проекты MSBuild в VS являются примером использования системы сборки для создания функций IDE, и теперь пользователи не довольны ею, потому что она слишком велика.

Спасибо за ответ, Мохамед. Позвольте мне еще раз сказать, понимаю ли я:

  • Вы думаете, что задача координации многопроектных сборок должна оставаться прерогативой специальных инструментов сборки.
  • Вы думаете, что в этом предложении есть что-то для языковой службы TypeScript.
  • Вы думаете, что запуск tsc --project на этом tsconfig.json будет таким же, как запуск tsc file1.ts ../project/out/project.d.ts . Однако открытие такого проекта в VS или другом редакторе с языковым сервисом TypeScript позволит «перейти к определению», чтобы привести разработчика к _файлу фактического TypeScript_, в котором была определена функция (а не к определению в projectB.d.ts ).

Я имею это право?

Если так, я считаю, что это очень справедливо. В моем первоначальном предложении (https://github.com/Microsoft/TypeScript/issues/3394) я сказал, что моя идея была неполной, потому что она не включала шаг копирования выданных результатов, откуда они будут выводиться. в ссылочной библиотеке туда, где ссылочная библиотека ожидала бы их во время выполнения. Я думаю, вы говорите: «зачем идти на полпути к строительству, когда действительно нужна языковая поддержка».

Чтобы немного изменить данные в вашем примере, вы бы хотели поддержать что-то подобное?

"files" : [
    "file1.ts",
    {
        "path": "externalLibraries/projectB.d.ts",
         "sourceProject": "../projectB/"
     }
]

Предполагается, что текущий проект будет поставляться с определением для projectB, которое будет использоваться по умолчанию, но если фактический источник для projectB доступен, вместо него будет использоваться фактический источник.

@nycdotnet, вы

Звучит здорово!

Я согласен с @mhegazy, и на самом деле я думаю, что для TypeScript важно перестать думать о себе как о «компиляторе» и начать думать о себе как о «проверяющем типе» и «транспиляторе». Теперь есть поддержка однофайловой транспиляции. Я не вижу причин для создания скомпилированных файлов JavaScript, кроме как во время выполнения / сборки. Я также не понимаю, почему необходимо генерировать определения внешних ссылок во время проверки типов, когда доступен фактический источник машинописного текста.

За разрешение файлов и пакетов отвечает используемая вами система сборки (browserify, systemjs, webpack и т. используют. Это означает либо реализацию настраиваемого LanguageServicesHost для каждой системы сборки, либо предоставление инструмента для каждой из них, который генерирует правильные записи сопоставления в tsconfig.json. Любой из этих вариантов приемлем.

@nycdotnet Я думаю, что ваш вариант использования нескольких резервных путей лучше обрабатывать с помощью npm link ../csproj2ts ?

Я согласен с @mhegazy, что мы должны держать сборку отдельно от компилятора / транспилятора. Я бы хотел включить зависимость tsconfig -> tsconfig в раздел файлов, предполагая, что если в разделе не перечислены какие-либо файлы * .ts, он по-прежнему сканирует их. Например

"файлы": [
{
"путь": "externalLibraries / projectB.d.ts",
"sourceProject": "../projectB/"
}
]

По-прежнему будут включены все файлы ts в каталоге и подкаталоге, содержащем файл tsconfig.json.

@dbaeumer, вы хотите, чтобы это было в другом

@mhegazy - не обязательно другой раздел, хотя в конце он прояснит ситуацию. Все, чего я хочу избежать, это принуждение к перечислению всех файлов, если я использую зависимости tsconfig -> tsconfig. В приведенном выше примере я все же хотел бы не перечислять какие-либо файлы * .ts для передачи их в компилятор.

Я думаю, это очень нужно. Однако я не думаю, что мы можем избежать вопроса о сборке. Это не означает, что нам нужно реализовать систему сборки вместе с этим предложением, но мы должны были продумать некоторые хорошие рекомендации, которые работают в такой конфигурации, как предложенная. Сделать это будет нетривиально (и нам нужно решить эту проблему для Visual Studio).

В приведенном выше предложении (где есть ссылка как на .d.ts, так и на источник), определяет ли он, устарел ли .d.ts (т.е. его нужно перестроить)? Работают ли такие операции, как Refactor / Rename, во всех проектах (т. Е. Обновляет имя в исходном коде проекта, на который указывает ссылка, а не только в его файле .d.ts, который будет перезаписан при следующей сборке)? Приводит ли GoToDef к исходному коду в упомянутом проекте (а не к середине гигантского файла .d.ts для всего проекта)? Казалось бы, это подразумевает необходимость определить, а в некоторых случаях проанализировать источник упомянутых проектов, и в каком случае .d.ts так полезен?

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

Проблема в сценарии редактирования. вы не хотите просматривать сгенерированный файл во время редактирования. Предлагаемое мной решение - дать «подсказку» о том, откуда пришли сгенерированные .d.ts. Тогда языковая служба не будет загружать .d.ts, а вместо этого загрузит «проект» из пути подсказки. таким образом goto def приведет вас к файлу реализации вместо .d.ts, и аналогично ошибки будут работать без необходимости компиляции.

такие операции, как переименование, будут «распространяться» от одного проекта к другому, аналогично поиску ссылок.

Сегодня поиск файла tsconfig.json полностью зависит от хоста (IDE и т. Д.), Хотя TS затем предоставляет API для его чтения и анализа. Как бы вы себе это представляли, если бы было несколько файлов tsconfig.json, расположенных в иерархическом порядке? Будет ли хост по-прежнему отвечать за разрешение исходного файла, но не других, или он будет отвечать за разрешение всех tsconfigs?

Похоже, здесь есть компромисс между удобством / условностями и гибкостью.

Разве это не началось бы с возможности создавать файлы d.ts, как описано в # 2568 (или, по крайней мере, иметь относительный импорт)?

@spion, я не уверен, что вижу здесь зависимость. у вас может быть несколько выходов проекта, он не должен быть одним файлом восхищения. система сборки должна знать это и связывать их в качестве входных данных с зависимыми проектами.

@mhegazy Ой, извините. Если взглянуть на проблему еще раз, кажется, что она больше связана с языковым сервисом. Я прочитал следующее

  • Проекты среднего размера: со стандартными сборками и общими компонентами.

и автоматически предполагал, что это связано с улучшенной поддержкой npm / browserify (или webpack), где части проекта являются внешними модулями.

AFAIK еще нет возможности создать файл (ы) .d.ts для внешних модулей? Если это так, то единственный способ, которым языковая служба может связать проекты, импортирующие внешние модули, - это иметь что-то вроде этого в tsconfig.json:

{ 
  "provides": "external-module-name"
}

который будет информировать LS, когда проекты упоминаются в другом tsconfig.json

AFAIK еще нет возможности создать файл (ы) .d.ts для внешних модулей?

Я не думаю, что это правда. вызов tsc --m --d сгенерирует файл объявления, который сам является внешним модулем. логика разрешения попытается найти .ts, а если нет, то .d.ts с тем же именем,

@spion TypeScript может генерировать файлы d.ts для внешних модулей, как сказал @mhegazy , но это приводит к соотношению определений к исходным файлам 1: 1, что отличается от того, как обычно используются определения TypeScript библиотеки. Один из способов обойти это - библиотека TypeStrong: https://github.com/TypeStrong/dts-bundle

@mhegazy, извините, я имел в виду "внешние внешние модули", т.е. если я напишу external-module-name в TypeScript и импортирую один из его классов из другого модуля:

import {MyClass} from 'external-module-name'

нет способа заставить tsc сгенерировать соответствующий файл .d.ts, который объявляет 'external-module-name'

@nycdotnet Я знаю о dts-bundle и dts-generator, но все же, если языковая служба должна знать об источнике моего другого проекта, она также должна знать, какое имя модуля она предоставляет, чтобы иметь возможность правильно отслеживать импорт

Каков статус этой функции? кажется, это важный вариант для проектов среднего размера. Как настроить проект с исходным кодом в разных папках с определенной конфигурацией "requirejs"?

@llgcode, пожалуйста, посмотрите https://github.com/Microsoft/TypeScript/issues/5039 , это должно быть доступно в typescript@next .

Я действительно не понимаю, что такого сложного в написании gulpfile с задачами, которые компилируют подпроекты именно так, как вам это нужно для проектов среднего размера. Я даже делаю это в небольших проектах. Единственная причина, по которой я вообще использую tsconfig.json, - это VS Code.

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

@llgcode Ну, gulp.task() . Внутри вашей задачи вы можете взять поток входных файлов с gulp.src() а затем .pipe() через конвейер преобразований, таких как компиляция, конкатенация, минификация, исходные карты, копирование ресурсов ... делать все, что возможно с модулями Node и NPM.
Если вам необходимо скомпилировать несколько проектов, просто определите задачу, которая это делает. Если вы хотите использовать несколько tsconfig.json, gulp -typescript поддерживает это, или вы можете просто прочитать файлы json. Также возможны инкрементные сборки. Я не знаю, как структурирован ваш проект, есть ли они у вас в разных репозиториях и используются ли подмодули или что-то еще. Но gulp на 100% гибкий.

Хорошо, спасибо, похоже, отличный инструмент. если у меня есть требование с сопоставлением, например, require ("mylibs / lib"), и мои файлы находятся, например, в папке project / src / lib.js, то завершение не будет работать в атоме, и я не знаю, как typescript или gulp решат отображение / конфигурация выполняется с помощью "mylibs" и локального пути. поэтому я думаю, что эти новые пути к параметрам в # 5039 - хорошее решение этой проблемы.

@llgcode Что ж, с помощью https://www.npmjs.com/package/gulp-typescript#resolving -files.

Я просто думаю, что TypeScript многое здесь пытается сделать, это транспилятор, а не инструмент для сборки. Управление «зависимостями», как упомянуто в предложении, на самом деле является задачей менеджеров пакетов или систем контроля версий, а объединение их вместе - задача инструментов сборки.

@felixfbecker Я не согласен. У каждого известного мне компилятора (с проверкой типов) есть такая возможность. Например:
gcc -> включить файлы
java -> путь к классам и путь к исходному тексту
перейти -> GOPATH
python -> PYTHONPATH
компилятор / транспилятор должен знать, какие исходные файлы необходимо перенести, какие исходные файлы - это просто файлы include / lib.
Инструмент сборки, такой как gulp, необходим, чтобы знать, что делать при изменении файла.

Согласитесь с @llgcode . Кроме того, помимо представления в качестве компилятора, TypeScript также предоставляется как языковая служба, которая обеспечивает подсветку синтаксиса (фактически обнаружение) и функции завершения для IDE. И ЭТО также нужно пройти по дереву зависимостей.

@llgcode @unional Действительные баллы. Одна вещь, которая также может помочь, - заставить свойство files в tsconfig.json принимать глобусы, чтобы вы могли определить все файлы во всех папках, которые хотите включить. Но я понимаю, откуда вы пришли и почему для более крупных проектов может понадобиться несколько tsconfig.json.

AFAIK для CommonJS это уже поддерживается через node_modules и npm link ../path/to/other-project

Ссылка npm не работает, как только вы начинаете повторно использовать библиотеки в проектах. Если вы используете общую библиотеку между двумя отдельными проектами, (на примере rxjs) typescript сообщит вам, что «Observable не может быть назначен Observable». Это потому, что пути включения следуют за папками с символическими ссылками на две разные папки node_modules и несмотря на то, что это одна и та же библиотека. Обходные пути приводят к созданию задач gulp или локальных / частных репозиториев npm, в основном возвращаясь к варианту большого проекта.

@EricABC, вероятно, потому, что они используют объявления внешних внешних модулей, и в этом случае они также должны включать определения для недавно поддерживаемых файлов .d.ts на основе node_modules . В остальном проблем быть не должно, поскольку типы TS проверяются только структурно, поэтому не имеет значения, происходят ли они из разных модулей или имеют разные имена, если структуры совпадают.

Спасибо, @spion , предположил, что это файл, похоже, ты собираешься спасти меня от мучительной боли.

Одна вещь, которая также может помочь, - заставить свойство files в tsconfig.json принимать глобусы ...

Обсуждается свойство include

Вопросы и замечания:

  • dependencies должен разрешать полный путь к tsconfig.json, потому что tsc это позволяет
  • Зачем вводить новое ключевое слово ( dependencies ), если files уже существует и в порядке?
    Пример:
{
    "compilerOptions": {
        // ...
    },
    "files": [
        "../common/tsconfig.json", // <== takes the `files` part of the tsconfig.json
        "../common/tsconfig.util.json", // <==
        "core.ts",
        "sys.ts"
    ]
}
  • Что произойдет, если в зависимости tsconfig.json также будет указано compilerOptions ?


Пойдем дальше / wild :-) и, возможно, разрешим (в будущем) compilerOptions , exclude ... ссылаться на другой tsconfig.json:

// File app/tsconfig.json
{
    "compilerOptions": "../common/tsconfig.compilerOptions.json",
    "files": [
        "../common/tsconfig.json",
        "../common/tsconfig.util.json",
        "core.ts",
        "sys.ts"
    ],
    "exclude": "../common/exclude.json"
}

// File ../common/tsconfig.compilerOptions.json
{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    }
}

// File ../common/exclude.json
{
    "exclude": [
        "node_modules",
        "wwwroot"
    ]
}

// File ../common/tsconfig.util.json
{
    "files": [
        "foo.ts",
        "bar.ts"
    ]
}

У вас есть логика: files , compilerOptions , exclude ... может ссылаться на другие файлы tsconfig.json, и он будет "брать" только соответствующую часть ключевого слова из другого tsconfig. .json файл => простой и масштабируемый. Таким образом, вы можете разделить tsconfig.json на несколько файлов, если хотите, и использовать их повторно.

Читая часть вашего обсуждения, кажется, что наиболее актуальным является правильное определение этой «языковой службы» / определения goto. Отладчики JavaScript используют sourceMaps. Теперь, если tsc генерирует данные sourceMap не только в .js, но и в файлах .d.ts ...

Кроме того, я не вижу особой пользы в запуске сборки дочерних проектов из файла tsconfig.json. Если вам нужен этот базовый вид зависимостей времени сборки, сработает простой сценарий оболочки. С другой стороны, если вам нужно интеллектуальное инкрементное строительство, предложенный подход кажется слишком простым. Во многих сценариях tsc оказывается всего лишь одним из этапов сборки среди других. Насколько странно писать зависимости для tsc в tsconfig.json, но в каком-то другом файле для остальной части? Опять же, для простых вещей, когда tsc является единственным шагом сборки, который будет выполнять сценарий оболочки.

В любом случае, как насчет создания сопоставления источников в файлах .d.ts так же, как и в файлах .js?

мы просто используем модули node + ссылка npm, единственное, что не работает, это то, что модуль moduleResolution: node несовместим с модулями ES6, которые позволяют оптимизировать встраивание / встряхивание дерева (см. также # 11103)

Не отрываясь от темы, но в некотором смысле это действительно похоже на проблемы работы с несколькими проектами пакетов Node локально. Я не думаю, что это так просто, как просто использовать "ссылку npm". У них могут быть разные сценарии сборки, вам нужно будет запускать их все в правильном порядке, сложнее делать это постепенно, сложнее использовать режим просмотра, сложнее отлаживать вещи и труднее разрешать исходные карты. В зависимости от выбранного вами редактора это может быть еще сложнее.

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

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

Просто послушайте, что это было бы здорово для нашей повседневной работы!

Из-за характера веб-разработки у нас есть несколько проектов ts, каждый из которых содержит несколько файлов ts, скомпилированных в один файл js ( --outFile ). Это либо проекты, подобные приложениям (они выполняют определенную функцию или добавляют определенную функцию), либо проекты, подобные библиотекам (повторно используемый код для приложений). Часто мы работаем над несколькими ts-проектами одновременно, улучшая библиотеки, чтобы облегчить разработку приложений. Но я также не думаю, что у кого-то из моих коллег-разработчиков все наши ts-проекты в своей локальной среде когда-либо.

Чтобы улучшить наш рабочий процесс, мы предлагаем следующие варианты:

  • Закидываем все в проект 1 цс

    • Мы можем напрямую ссылаться на файлы .ts, что намного лучше, чем использование файлов d.ts.

    • Но нам нужен внешний инструмент для сопоставления и объединения наборов файлов, поскольку нам нужно ограничить количество js-файлов, запрашиваемых через межсетевые соединения, при этом сохраняя модульность приложений (которые зависят от функции или страницы).

    • Как уже упоминалось, не все эти ts-проекты требуются постоянно для всех, так что это сильно усложнит файловую систему. Кому-то, работающему над проектом X, могут понадобиться проекты A, B и D, а кому-то, работающему над Y, могут понадобиться A и C.

  • Держите проекты разделенными (наша текущая ситуация)

    • Нам нужно ссылаться на скомпилированные файлы d.ts из других ts-проектов, поскольку включение файла .ts напрямую добавит его к выходным данным. Было бы намного быстрее, если бы мы могли просто ввести f12 прямо в наш собственный исходный код.

    • И нам нужно добавить инструменты / скрипты для одновременной компиляции нескольких из этих проектов. В настоящее время мы либо запускаем команды tsc -d -w с нескольких терминалов, либо запускаем сценарий, который делает это для всех подкаталогов, где находится tsconfig.

    • Я понимаю, что в этом могут помочь другие инструменты (например, gulp) или, может быть, мы сможем что-то выяснить с файловыми глобусами в tsconfigs, однако это не поможет с первой проблемой файлов d.ts.

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

Если бы dependencies мог быть лучшим вариантом, это было бы потрясающе; функциональностьво все ts-файлы зависимости, но компилируйте вывод в соответствии с собственным tsconfig этой зависимости.

Есть ли обновления по этой теме. Как у нас может быть несколько проектов машинописного текста, которые компилируются независимо друг от друга? Как мы можем описать такую ​​зависимость в tsconfig.json?

Теперь это включено в дорожную карту в будущий раздел под названием «Поддержка ссылок на проекты». Итак, я предполагаю, что файл .tsconfig сможет связать другой файл .tsconfig в качестве зависимости.

Есть ли обновления по этой теме. Как мы можем иметь несколько проектов машинописного текста, которые компилируются независимо друг от друга? Как мы можем описать такую ​​зависимость в tsconfig.json?

Зависимость сборки должна быть закодирована в вашей системе сборки. такие системы сборки, как gulp, grunt, broccoli, msbuild, basal и т. д., созданы для обработки таких случаев.
Для информации о типе выходные данные одного проекта должны включать .d.ts, которые следует передавать в качестве входных данных для другого.

@mhegazy Наш проект работает так. У нас есть несколько пакетов в lerna monorepo, каждый пакет имеет свои собственные зависимости в package.json и те же зависимости в свойстве "types" в их tsconfig.json . Каждый проект компилируется с помощью --outFile (это старый проект, который еще не переместился в модули ES), а ключ "typings" package.json указывает на связанный .d.ts файл.

Мы используем gulp для сборки / просмотра.

По большей части это работает, но есть несколько проблем:

  • Из-за таких проблем, как # 15488 и # 15487, нам нужна явная ссылка, чтобы ссылки работали правильно.
  • При переходе к определению вы попадете в связанный файл .d.ts . В идеале это приведет вас к источнику в другом проекте.
  • Самый быстрый способ выполнить полную сборку - это lerna run build --sort (фактически tsc в каждом каталоге), что вызывает дополнительные накладные расходы, поскольку порождает один процесс компилятора TypeScript для каждого пакета, выполняя много повторяющейся работы. .

Я внимательно слежу за этой проблемой, поскольку мы также находимся в той же ситуации, которую описывали другие.
Несколько «проектов», каждый со своим файлом tsconfig.json.

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

Настоящая проблема - это поддержка IDE: при поиске ссылок они находятся только в пределах одного tsconfig.json . Хуже того, каскадные эффекты измененного файла не распространяются на проекты, потому что зависимые файлы за пределами области tsconfig.json не перекомпилируются. Это очень плохо для обслуживания наших проектов и иногда вызывает ошибки сборки, которые могли быть обнаружены в IDE.

It's happening

О БОЖЕ

Обновленный сценарий, в котором я бы хотел, чтобы это было, включает компоненты React. У нас есть репозиторий компонентов, содержащий модули JSX (атомы, молекулы и организмы), которые делают компоненты пользовательского интерфейса подходящими для всех приложений в нашей компании. Этот репозиторий компонентов используется всеми интерфейсными разработчиками, когда они работают над своими индивидуальными приложениями. Было бы ТАК ПРИЯТНО, если бы у меня был опыт работы с языковым сервисом TypeScript, который позволил бы мне редактировать пользовательский интерфейс моего конкретного приложения и «Перейти к определению» в репозиторий общих компонентов пользовательского интерфейса. Сегодня мы должны связать эти компоненты по отдельности и скопировать их. Это проблема «создания собственного проекта», которую я хотел бы исправить (для которой есть очень хорошая история в мире .NET с проектами в рамках решения).

Ссылки на проект: встроенная масштабируемость для TypeScript

Вступление

TypeScript масштабируется до проектов, состоящих из сотен тысяч строк, но изначально не поддерживает такой масштаб в качестве встроенного поведения. Команды разработали обходные пути различной эффективности, и не существует стандартизированного способа представления крупных проектов. Несмотря на то, что мы значительно улучшили производительность проверки типов со временем, все еще существуют жесткие ограничения с точки зрения того, насколько быстро может работать TS,
и такие ограничения, как 32-битное адресное пространство, которые не позволяют языковой службе «бесконечно» масштабироваться в интерактивных сценариях.

Интуитивно понятно, что изменение одной строки JSX во внешнем компоненте не должно требовать повторной проверки типа всего основного компонента бизнес-логики проекта на 500 000 LOC. Цель ссылок на разделения кода на более мелкие блоки. Позволяя инструментам работать с небольшими порциями работы за раз, мы можем улучшить скорость отклика и сократить цикл разработки ядра.

Мы полагаем, что в нашей текущей архитектуре осталось очень мало «бесплатных обедов» с точки зрения радикальных улучшений производительности или потребления памяти. Вместо этого это разделение является явным компромиссом, который увеличивает скорость за счет некоторой предварительной работы. Разработчикам придется потратить некоторое время на рассуждения о графике зависимостей своей системы, а некоторые интерактивные функции (например, переименование между проектами) могут быть недоступны до тех пор, пока мы не доработаем инструменты.

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

Сценарии

Следует рассмотреть три основных сценария.

Относительные модули

Некоторые проекты широко используют относительный импорт . Этот импорт однозначно разрешается в другой файл на диске. Обычно можно найти такие пути, как ../../core/utils/otherMod , хотя в этих репозиториях обычно предпочтительны более плоские структуры каталогов.

Пример

Вот пример из проекта Perseus Khan Academy:

Взято из https://github.com/Khan/perseus/blob/master/src/components/graph.jsx

const Util = require("../util.js");
const GraphUtils = require("../util/graph-utils.js");
const {interactiveSizes} = require("../styles/constants.js");
const SvgImage = require("../components/svg-image.jsx");

Наблюдения

Хотя структура каталогов подразумевает структуру проекта, она не обязательно является окончательной. В приведенном выше примере Khan Academy will может сделать вывод, что util , styles и components , вероятно, будут их собственным проектом. Но также возможно, что эти каталоги довольно малы и на самом деле будут сгруппированы в одну единицу сборки.

Моно-репо

Монорепозиторий состоит из ряда модулей, которые импортируются по не относительным путям. Импорт из подмодулей (например, import * as C from 'core/thing ) может быть обычным явлением. Обычно, но не всегда, каждый корневой модуль фактически публикуется в NPM.

Пример

Адаптировано из https://github.com/angular/angular/blob/master/packages/forms/src/validators.ts

import {InjectionToken, ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {map} from 'rxjs/operator/map';
import {AbstractControl, FormControl} from './model';

Наблюдения

Единица деления не обязательно является ведущей частью имени модуля. Например, rxjs фактически компилирует свои подчасти ( observable , operator ) отдельно, как и любой пакет с ограниченной областью видимости (например, @angular/core ).

Outfile

TypeScript может объединять свои входные файлы в один выходной файл JavaScript. Справочные директивы или упорядочение файлов в tsconfig.json создают детерминированный порядок вывода для результирующего файла. Это редко используется для новых проектов, но все еще преобладает среди старых кодовых баз (включая сам TypeScript).

Пример

https://github.com/Microsoft/TypeScript/blob/master/src/compiler/tsc.ts

/// <reference path="program.ts"/>
/// <reference path="watch.ts"/>
/// <reference path="commandLineParser.ts"/>

https://github.com/Microsoft/TypeScript/blob/master/src/harness/unittests/customTransforms.ts

/// <reference path="..\..\compiler\emitter.ts" />
/// <reference path="..\harness.ts" />

Наблюдения

Некоторые решения, использующие эту конфигурацию, будут загружать каждый outFile через отдельный тег script (или эквивалент), но другие (например, сам TypeScript) требуют объединения предыдущих файлов, поскольку они создают монолитные выходные данные. .

Ссылки на проект: новая единица изоляции

Некоторые критические наблюдения из взаимодействия с реальными проектами:

  • TypeScript обычно «быстр» (<5-10 с) при проверке проектов с менее чем 50 000 LOC кода реализации (не-.d.ts).
  • Файлы .d.ts, особенно под skipLibCheck , почти "бесплатны" с точки зрения проверки типов и стоимости памяти.
  • Практически все программное обеспечение можно разделить на компоненты размером менее 50 000 LOC.
  • Почти все крупные проекты уже предусматривают некоторую структуризацию своих файлов по каталогам таким образом, чтобы создавать подкомпоненты среднего размера.
  • Большинство изменений происходит в компонентах конечных или почти конечных узлов, которые не требуют повторной проверки или повторной выдачи всего решения.

Если сложить все вместе, то если бы можно было одновременно проверять только один фрагмент кода реализации объемом 50 000 LOC, в интерактивном сценарии почти не было бы «медленных» взаимодействий, и у нас почти никогда не было бы нехватки памяти.

Мы представляем новую концепцию, ссылку на не проверяется; вместо этого мы просто загружаем его вывод .d.ts из детерминированного местоположения.

Синтаксис

Новый вариант references (TODO: Bikeshed!) Добавлен к tsconfig.json :

{
  "extends": "../tsproject.json",
  "compilerOptions": {
    "outDir": "../bin",
    "references": [
      { "path": "../otherProject" }
    ]
  }
}

Массив references определяет набор других проектов для ссылки из этого проекта.
Каждый references объект path указывает на файл tsconfig.json или папку, содержащую файл tsconfig.json .
Другие варианты могут быть добавлены к этому объекту, когда мы обнаружим их потребности.

Семантика

Ссылки на проект меняют следующее поведение:

  • Когда разрешение модуля разрешается в файл .ts в подкаталоге rootDir проекта, оно вместо этого преобразуется в файл .d.ts в outDir этого проекта.

    • Если это разрешение не удается, мы, вероятно, сможем обнаружить его и выдать более разумную ошибку, например Referenced project "../otherProject" is not built а не просто «файл не найден».

  • Ничего другого (TODO: пока?)

Ограничения на выполнение упомянутых проектов

Чтобы значительно улучшить производительность сборки, мы должны обязательно ограничить поведение TypeScript, когда он видит ссылку на проект.

В частности, должно быть верно следующее:

  • Никогда не читайте и не анализируйте входные TS-файлы указанного проекта.
  • С диска следует читать только tsconfig.json проекта, на который имеется ссылка.
  • Актуальная проверка не должна требовать нарушения вышеуказанных ограничений.

Чтобы выполнить эти обещания, нам необходимо наложить некоторые ограничения на проекты, на которые вы ссылаетесь.

  • declaration автоматически устанавливается в true . Попытка изменить этот параметр является ошибкой.
  • rootDir умолчанию "." (каталог, содержащий файл tsconfig.json ), а не выводится из набора входных файлов
  • Если предоставляется массив files , он должен содержать имена всех входных файлов.

    • Исключение: файлы, включенные как часть ссылок на типы (например, в node_modules/@types ), указывать не нужно.

  • Любой проект, на который имеется ссылка, должен сам иметь массив references (который может быть пустым).

Почему "declaration": true ?

Ссылки на проекты повышают скорость сборки за счет использования файлов объявлений (.d.ts) вместо файлов их реализации (.ts).
Поэтому, естественно, для любого проекта, на который имеется ссылка, должен быть включен параметр declaration .
Это подразумевается "project": true

Зачем менять rootDir ?

rootDir определяет, как входные файлы сопоставляются с именами выходных файлов. По умолчанию TypeScript вычисляет общий исходный каталог полного графа входных файлов. Например, набор входных файлов ["src/a.ts", "src/b.ts"] создаст выходные файлы ["a.js", "b.js"] ,
но набор входных файлов ["src/a.ts", "b.ts"] создаст выходные файлы ["src/a.js", "b.js"] .

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

Нет округлости

Естественно, проекты не могут образовывать граф с какой-либо округлостью. (TODO: Какие проблемы это на самом деле вызывает, кроме кошмаров построения оркестровки?) Если это произойдет, вы увидите сообщение об ошибке, указывающее на сформированный круговой путь:

TS6187: Project references may not form a circular graph. Cycle detected:
    C:/github/project-references-demo/core/tsconfig.json ->
    C:/github/project-references-demo/zoo/tsconfig.json ->
    C:/github/project-references-demo/animals/tsconfig.json ->
    C:/github/project-references-demo/core/tsconfig.json

tsbuild

Это предложение намеренно расплывчато относительно того, как оно будет использоваться в «реальной» системе сборки. Очень немногие проекты масштабируются за «быструю» границу 50 000 LOC без введения чего-либо, кроме tsc для компиляции кода .ts.

Пользовательский сценарий «Вы не можете собрать foo потому что bar еще не построен» - это очевидная задача типа «Иди и принеси эту», о которой компьютер должен позаботиться, скорее чем психологическая нагрузка на разработчиков.

Мы ожидаем, что такие инструменты, как gulp , webpack и т. Д. (Или их соответствующие плагины TS) будут понимать ссылки на проекты и правильно обрабатывать эти зависимости сборки, включая проверку актуальности.

Чтобы убедиться, что это возможно , мы предоставим эталонную реализацию для инструмента оркестровки сборки TypeScript, которая демонстрирует следующее поведение:

  • Быстрая актуальная проверка
  • Упорядочивание графов проекта
  • Распараллеливание сборки
  • (TODO: другие?)

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

ДЕЛАТЬ

Разделы, которые необходимо заполнить, чтобы полностью заполнить это предложение

  • Как бы вы перенесли существующий проект

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

  • Влияние на baseUrl

    • Делает внедрение сложным, но практически не влияет на конечного пользователя

  • Краткое обсуждение проектов раскроя (TL; DR необходимо разрешить)
  • Набросайте сценарий разделения «слишком большого» проекта на более мелкие без нарушения потребителей.
  • Разберитесь в сценарии Лерны

    • Доступная точка данных (N = 1) говорит, что им это не понадобится, потому что их сборка уже эффективно структурирована таким образом.

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

  • Нужен ли нам параметр dtsEmitOnly для людей, которые отправляют свой JS по конвейеру, например, через webpack / babel / rollup?

    • Возможно, references + noEmit подразумевает это

Фантастика!

Разберитесь в сценарии Лерны

  • Доступная точка данных (N = 1) говорит, что им это не понадобится, потому что их сборка уже эффективно структурирована таким образом.

Относится ли «это» к предложению или реализации эталонной сборки? Хотя вы можете использовать lerna для сборки (это делает моя команда), это сложно и будет намного эффективнее, если TS (или инструмент, созданный на основе этого предложения) позаботится о себе сам.

Раздел TODO - это TODO для всего предложения.

Отлично!

Любой проект, на который имеется ссылка, должен сам иметь массив ссылок (который может быть пустым).

Это действительно необходимо? Разве в таком пакете не было бы файлов .d.ts ?
(В этом случае это может быть даже не обязательно, если есть tsconfig.json ?)

Мой вариант использования: рассмотрим (например, сторонний) проект, который не использует outDir , поэтому .ts , .js и .d.ts будут рядом с друг друга, и TS в настоящее время пытается скомпилировать .ts вместо использования .d.ts .

Причина, по которой я не использую outDir для меня, состоит в том, чтобы легче разрешить импорт в стиле import "package/subthing" , который в противном случае должен был бы быть, например, import "package/dist/subthing" с outDir: "dist" .
И иметь возможность использовать либо пакет NPM, либо его исходный репозиторий напрямую (например, с npm link ).

(Было бы полезно, если бы в package.json указать каталог в main , но, увы ...)

Нужна ли нам настройка dtsEmitOnly для людей, которые передают свой JS по конвейеру, например, через webpack / babel / rollup?

Абсолютно! На данный момент это большая недостающая часть. В настоящее время вы можете получить один файл d.ts при использовании outFile , но когда вы переключаетесь на модули и используете сборщик, вы теряете его. Было бы замечательно иметь возможность создать один файл d.ts для точки входа модуля (с export as namespace MyLib ). Я знаю, что это могут делать внешние инструменты, но было бы здорово, если бы они были интегрированы в эмиттер и языковые сервисы.

Это действительно необходимо? Разве не было бы достаточно, если бы в таком пакете были файлы .d.ts?

Нам нужно что-то в целевом tsconfig, которое сообщает нам, где ожидать выходные файлы. В предыдущей версии этого предложения было «Вы должны явно указать rootDir », что было довольно громоздко (вам приходилось писать "rootDir": "." в каждом tsconfig). Поскольку мы хотим изменить различные модели поведения в этом мире, было бы разумнее просто сказать, что вы получаете «проектное» поведение, если у вас есть массив ссылок и это будет то, что отключено, вместо того, чтобы указывать кучу флаги, которые необходимо указать явно.

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

В идеальном мире упомянутый проект не требует предварительной сборки. т.е. TypeScript выполняет «сборку» указанного проекта и поддерживает эквивалент «d.ts» в памяти языковой службы. это позволит изменениям, внесенным в «исходный» проект, отображаться в «зависимом» проекте без необходимости перекомпоновки.

Нам нужно что-то в целевом tsconfig, которое сообщает нам, где ожидать выходные файлы.

Это верно только тогда, когда используется outDir , не так ли?

Например: если у меня есть tsconfig, который:

  • НЕ использует outDir (но, конечно, имеет declaration: true ), тогда нам не нужны ни rootDir , ни references
  • есть outDir , тогда вам нужно будет установить references и / или rootDirdeclaration: true )

Причина, по которой я спрашиваю, заключается в том, что я мог затем включить «режим проекта» для любого пакета TS, просто сославшись на него, то есть он находится под моим контролем.

В этом случае также было бы хорошо, если бы он также работал, как только он найдет файл .d.ts, который он ищет (т.е. не будет жаловаться, если нет файлов .ts или файлов tsconfig). Потому что это сделает возможным другой случай «замены» версии NPM (которая может иметь только файлы .d.ts) исходной версией, когда это необходимо.

Например, рассмотрим пакеты NPM MyApp и SomeLib.
SomeLib мог иметь tsconfig: declaration: true .

Репозиторий вроде:

package.json
tsconfig.json
index.ts
sub.ts

Скомпилировано, это становится:

package.json
tsconfig.json
index.ts
index.d.ts
index.js
sub.ts
sub.d.ts
sub.js

Эта структура позволяет, например,

// somewhere in MyApp
import something from "SomeLib/sub";

В опубликованном пакете NPM я в настоящее время всегда должен удалять файлы .ts, иначе все исходники будут перекомпилированы TS, если MyApp использует SomeLib:

Итак, в NPM это становится:

package.json
index.d.ts
index.js
sub.d.ts
sub.js

Теперь, если я помещу references: ["SomeLib"] в tsconfig MyApp, было бы хорошо, если бы он работал «как есть» как для версии NPM, так и для исходной версии SomeLib , то есть не будет жаловаться, например, на отсутствие tsconfig, до тех пор, пока он найдет sub.d.ts в нужном месте.


Связанный, но другой вопрос:

Теперь я понимаю, что ЕСЛИ автор SomeLib помещает references в свой tsconfig, это позволит публиковать пакеты NPM С файлами .ts в будущем. Но тогда, я полагаю, TS по-прежнему будет всегда перекомпилировать их, если какой-либо зависимый пакет явно не помещает references: ["SomeLib"] в свой tsconfig.

Или есть намерение также, что references в MyLib автоматически также вводит «границу проекта», когда это только import (т.е. не references )?

IIRC, одна из первоначальных идей заключалась в том, что если модуль, например, был расположен через node_modules , тогда файлы .d.ts были бы предпочтительнее, чем файлы .ts , но позже это было изменено обратно , потому что эвристика ("через node_modules ") в целом была слишком проблематичной. Возможно ли, что наличие явной «границы проекта» решит эту проблему (например, projectRoot: true вместо или в дополнение к наличию references )?

В случае с lerna я надеялся на более простое решение.

  1. В отдельных каталогах пакетов пакеты не должны знать ничего о структуре monorepo. Отдельные json-файлы tsconfig не должны содержать ссылок.

    • это позволяет вам разделять отдельные пакеты на отдельные репозитории и просто клонировать их с помощью основного инструмента репо, например ProseMirror: https://github.com/ProseMirror/prosemirror

  2. В корневом репозитории «рабочей области» (который может содержать весь код, но также может просто клонировать другие репозитории) есть ссылки в его tsconfig.json

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

Меня беспокоит то, что как только вы добавляете ссылку с использованием относительного нисходящего пути "../xx" к отдельным файлам конфигурации проекта, они больше не могут использоваться в качестве автономных модулей - они должны находиться в определенной структуре рабочего пространства.

Добавление новой концепции «рабочего пространства» tsconfig.json решает эту проблему. Таким образом, если вы, например, "git clone" для отдельного пакета, установив его зависимости обычным способом (например, используя npm или yarn), вы сможете работать с ним отдельно, поскольку скомпилированные зависимости внесут свои файлы определений. Если вы клонируете всю рабочую область и запускаете команду для ввода всех пакетов, конфигурация рабочей области позволит вам перемещаться по всем источникам.

Обратите внимание, что рабочее пространство tsconfig.json также идеально сочетается с рабочим пространством Yarn package.json https://yarnpkg.com/lang/en/docs/workspaces/

Я сделал небольшое доказательство концепции здесь

https://github.com/spion/typescript-workspace-plugin

Просто добавьте плагин во все ваши tsconfig.json файлы отдельных репозиториев.

{
  "plugins": [{"name": "typescript-workspace-plugin"}]
}

Затем в файле верхнего уровня package.json рядом с записью yarn "workspaces" добавьте запись "workspace-sources":

{
  "workspaces": ["packages/*"],
  "workspace-sources": {
    "*": ["packages/*/src"]
  }
}

Это поле работает точно так же, как поле «paths» в tsconfig.json, но влияет только на языковую службу отдельных проектов, указывая им на источники пакетов. Восстанавливает правильную функциональность «перейти к определению / типу» и тому подобное.

Это верно только при использовании outDir, не так ли?

Верный. Мы предположили, что почти каждый, у кого есть большой проект, использует outDir . Мне было бы интересно услышать о проектах, которые не

Теперь, если я помещу ссылки: ["SomeLib"] в tsconfig MyApp, было бы хорошо, если бы он работал "как есть" как для версии NPM, так и для исходной версии SomeLib.

Большой поклонник, мне очень нравится эта идея. Мне нужно подумать, действительно ли это требуется или нет.

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

Или также предполагается, что ссылки в MyLib автоматически также вводят «границу проекта», когда просто импортируют его (т.е. не ссылаются на него)?

Поговорили с references никогда не "видит" файл .ts за пределами папки проекта - это включает файлы в каталогах exclude d. Одно только это изменение заставляет сценарий lerna работать («работа» означает «ссылки на модули всегда разрешаются в .d.ts») из коробки, а также другие.

Мне нужно больше взглянуть на модель «рабочего пространства».

Это верно только при использовании outDir, не так ли?

Верный. Мы предположили, что почти все, у кого есть большой проект, используют outDir. Мне было бы интересно услышать о проектах, которые не

У нас есть 67 проектов TS в решении Visual Studio, которые скомпилированы без outdir и задач postbuild grunttasks для создания структуры выходного каталога (а также uglify и другой постобработки).

В большинстве проектов есть такой tsconfig.json

 "include": [
    "../baseProj/Lib/jquery.d.ts",
    "../baseProj/baseProj.d.ts"
  ]

Мне потребовалось некоторое время, чтобы прочитать предложение ссылок и исправить - пользователям AFAICT lerna и yarn workspace не нужны какие-либо функциональные возможности рабочего пространства, предлагаемые здесь:

  1. У нас уже есть граф зависимостей, основанный на package.json, поэтому мы знаем порядок запуска сборки. Фактически, в lerna есть общая команда запуска, которая может запускать это по порядку, и нетрудно написать инструмент, который также добавляет параллелизм, где это применимо. skipLibCheck должно незначительно повлиять на производительность, но я это не проверял.
  2. Lerna и Yarn уже создают символические ссылки на другие модули в соответствующем местоположении node_modules. В результате все иждивенцы могут перейти к package.json другого модуля, прочитать поле типов / типов и найти указанный файл определения типа module.d.ts.

Чего у нас нет и что предоставляет плагин, который я написал, - это способ загрузки всех источников одновременно. Когда мне нужно внести изменения в два или более модулей одновременно, я не хочу, чтобы «перейти к определению» и «перейти к определению типа» отправляли меня в файл .d.ts. Я хочу, чтобы он отправил меня в исходное местоположение исходного кода, чтобы, возможно, я мог его отредактировать. В противном случае я бы просто загрузил отдельный каталог проекта, и символические ссылки node_modules, созданные lerna / yarn, просто работали бы.

То же самое и для нас. Вместо Lerna мы используем Rush для вычисления нашего графа зависимостей, но результат тот же. Когда мы создаем проекты, tsc - это лишь одна из многих задач, которые необходимо выполнить. Параметры нашего компилятора рассчитываются с помощью более крупной системы сборки, и мы переходим к модели, в которой

Чего у нас нет и что предоставляет плагин, который я написал, - это способ загрузки всех источников одновременно. Когда мне нужно внести изменения в два или более модулей одновременно, я не хочу, чтобы «перейти к определению» и «перейти к определению типа» отправляли меня в файл .d.ts. Я хочу, чтобы он отправил меня в исходное местоположение исходного кода, чтобы, возможно, я мог его отредактировать.

+1 это было бы круто.

Если мы мечтаем о лучшей поддержке нескольких проектов, моим первым запросом была бы услуга компилятора, что-то вроде того, как работает VS Code IntelliSense. Наши сборки были бы значительно быстрее, если бы Rush мог вызывать tsc 100 раз без необходимости 100 раз раскручивать движок компилятора. Компилятор - один из самых дорогих этапов сборки. В монорепозитории время сборки действительно важно.

@iclanton @ nickpape-msft @ qz2017

Да, пожалуйста!

Я думаю, что одним из наиболее полезных результатов системы проектов было бы, если бы
'перейти к определению' перешел к исходному файлу вместо файла d.ts и
Поиск "найти все ссылки" в дереве ссылок проекта.

Предположительно, это также разблокирует рефакторинг типа «глобальное переименование».

В четверг, 9 ноября 2017 г., в 21:30 Сальваторе Превити [email protected]
написал:

Да, пожалуйста!

-
Вы получаете это, потому что подписаны на эту ветку.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/3469#issuecomment-343356868 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANX6d19Zz7TCd_GsP7Kzb-9XJAisG6Hks5s07VXgaJpZM4E-oPT
.

Поговорили с

Хорошо, и в таком случае зачем вообще указывать какие-то конкретные ссылки?
Оказывается, достаточно иметь флаг (например, projectRoot: true ).
Например, какая тогда разница между references: ["foo"] и просто references: [] ?
Потому что, если я import "foo" или import "bar" , оба будут игнорировать любые файлы .ts .

Итак, в этом случае предложение становится:

Учитывая этот tsconfig.json (TODO bikeshed на projectRoot ):

{
  "extends": "../tsproject.json",
  "compilerOptions": {
    "projectRoot": true
   }
}

Когда tsc затем нужно разрешить что-то за пределами папки проекта (включая файлы в исключенных каталогах), он будет смотреть только на .d.ts files (в качестве альтернативы он может просто _prefer_ .d.ts файлы и вернуться к tsconfig и / или .ts если он только это видит).

Это делает разрешение во время компиляции быстрым и простым.
Он работает для монорепозиционных ссылок (например, import "../foo" ) и ссылок на основе пакетов (например, import "foo" ).
Он работает для пакетов NPM и представления их исходного кода.
И это устраняет необходимость в разрешении машинного оборудования tsconfig.json во время компиляции, хотя сообщение об ошибке, если он не может найти .d.ts будет менее полезным.

Это звучит слишком хорошо, чтобы быть правдой, если это действительно так просто, поэтому я, вероятно, упускаю из виду что-то важное :)


Как отмечают другие, по-прежнему очень важно, чтобы IntelliSense продолжал работать с файлами .ts.

Итак, если затем делается запрос «перейти к определению», «найти ссылки» и т. Д., Он должен использовать некоторое обратное сопоставление, чтобы найти соответствующий файл .ts из файла .d.ts который он использовал, поэтому далеко.

Это сопоставление может быть выполнено, например, с помощью:

  • используя встроенную строку комментария, например //# sourceURL = ../src/foo.ts в .d.ts

    • можно использовать более сложную исходную карту для сопоставления от "свернутого" .d.ts обратно к исходному .ts

  • разрешение файла .js и использование его исходной карты для поиска файлов `.ts

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

@pgonzal @michaelaird https://github.com/spion/typescript-workspace-plugin делает только это - он восстанавливает переход к определению и находит все ссылки для проекта рабочей области multi-tsconfig yarn / lerna / etc.

Интересно ... Интересно, сможем ли мы сделать это с помощью Rush. Посмотрим.

К вашему сведению, еще один простой инструмент, который мы создали в рамках этих усилий, был wsrun

Подобно lerna run , он запускает команду для всех пакетов в рабочей области.

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

Замечу, oao - еще один инструмент монорепозитория для пряжи. Недавно он также добавил поддержку «топологического» порядка команд.

Я просто хотел бы опубликовать здесь слово «рефакторинг», поскольку это важная цель для нас, хотя, вероятно, не связанная с текущим предложением (https://github.com/Microsoft/TypeScript/issues/3469#issuecomment-341317069) и не упоминается в этом выпуске очень часто.

Наш вариант использования - монорепозиторий с несколькими проектами TS, его можно в основном сократить до common-library плюс несколько приложений: app1 и app2 . При работе в среде IDE их следует рассматривать как один проект, например, рефакторинг переименования должен работать во всех трех модулях, но app1 и app2 также являются двумя отдельными целями сборки. Хотя я согласен с тем, что сборка - это, как правило, отдельная проблема, правда в том, что наши приложения довольно маленькие, и выполнение чего-то вроде cd app1 && tsc было бы для нас совершенно нормально.

Если TypeScript найдет хороший способ поддержать это, это будет здорово.

Для кросс-проектного рефакторинга / ссылок monorepo я обнаружил, что эта настройка работает для меня, если вы работаете в vscode:

корень tsconfig:

"compilerOptions": {
   "baseUrl": ".",
   // global types are different per project
   "types": [],
   "paths": {
      "lib": ["packages/lib/src"],
      "xyz1": ["packages/xyz1/src"],
      "xyz2": ["packages/xyz2/src"],
   }
},
"include": ["./stub.ts"], // empty file with export {} to stop vscode complaining about no input files
"exclude": ["node_modules"]

пакеты / xyz1 / tsconfig.json

{
  "extends": "../../tsconfig",
   "compilerOptions": {
      "types": ["node"],
   },
   "include": ["src/**/*"]
}

пакеты / xyz2 / tsconfig.json

{
  "extends": "../../tsconfig",
   "compilerOptions": {
      "types": ["webpack-env"]
   },
  "include": ["src/**/*"]
} 

пакеты / lib / tsconfig.json

{
   "extends": "../../tsconfig",
    "compilerOptions": { ... },
    "include": ["src/**/*"],
    // special file to load referenced projects when inside in lib package, without it they won't be 
    // visible until you open some file in these projects 
    "files": ["./references.ts"],
}

пакеты / lib / tsconfig-build.json

{
   "extends": "./tsconfig",
    // exclude referenced projects when building
   "files": []
}

пакеты / lib / links.ts

import "xyz1";
import "xyz2";

export {};

Вам нужно правильное свойство main в package.json пакета и может быть types даже для пакетов без библиотеки, например:

  "main": "src/main.tsx",
  "types": "src/main.tsx",

Таким образом, рефакторинг / переименование чего-либо в lib также приведет к рефакторингу ссылок в xyz1 и xyz2 . Таким образом, проекты могут иметь разные глобальные / целевые библиотеки.

В Gradle это просто называют - композитная сборка .

Кстати, могу ли я указать мне, чтобы я начал, если я хочу внести свой вклад в компилятор TypeScript? Я клонировал репо, и это не мелочь (я использую для чтения источник: angular, ionic, express, когда я не могу получить его из документации или нахожусь вдали от Интернета ...) Мне действительно нужна помощь от одного указать мне путь, по которому нужно идти, пожалуйста.

Спасибо!
Светлое будущее TypeScript.

У меня есть прототип, который я бы хотел, чтобы люди попробовали, если они используют относительные пути к модулям. На https://github.com/RyanCavanaugh/project-references-demo есть образец репозитория, который описывает базовый сценарий и показывает, как он работает.

Для локальной попытки:

git clone https://github.com/RyanCavanaugh/TypeScript
git checkout pr-lkg
npm install
npm run build
npm link

Затем в другой папке:

npm link typescript

Я сохраню тег pr-lkg указывающий на последний рабочий коммит, когда все будет меняться. Далее мои задачи:

  • [x] Добавляйте более точные сообщения об ошибках, когда зависимые проекты не создаются
  • [] Применить поведение перенаправления к нерелевантным путям модулей
  • [] Предоставьте API для использования инструментов сборки

@RyanCavanaugh Я не могу создать его из-за таких ошибок, как отсутствие модуля del или Error: Cannot find module 'C:\github\TypeScript\built\local\tsc.js' (может быть, ваш локальный путь?), Но в целом структура выглядит великолепно.

Это только tsc или он также будет учитывать рефакторинг проекта в VSCode с языковым сервером?

... также отсутствует тег pr-lkg .

Тег есть (это не ветка). См. Https://github.com/RyanCavanaugh/TypeScript/tree/pr-lkg

references в tsconfig.json - это подписка для каждой зависимости, разве не имеет смысла применять его ко всему, что разрешается за пределами rootDir ?

Я представляю себе что-то вроде свойства sandbox которое могло бы выглядеть так:

# tsconfig.json
{
  "compilerOptions": {
    "outDir": "lib",
    "sandbox": "."
  },
  "include": ["src/index.ts"]
}

Песочница также установит то же значение для rootDir . Вместо явного указания путей к каталогам, содержащим tsconfig.json , будет применяться обычное разрешение модуля, и вы можете искать в дереве FS автоматически, чтобы найти tsconfig.json .

# package.json
{
  "name": "animals",
  "module": "src",
  "typings": "lib",
  "dependencies": {
    "core": "*"
  }
}

Сюда:

  • Не нужно поддерживать два синхронизированных списка ( references и dependencies ).
  • Пакет ничего не знает о файловой системе за пределами своей собственной.
  • Используйте стратегию разрешения узловых модулей вместо настраиваемых путей.

@RyanCavanaugh , насколько я понимаю, я могу работать с этими изменениями только локально и не смогу отправлять такие проекты на тестирование, например, на travis-ci.org. Верно?

Заметки с сегодняшней встречи с @billti / @mhegazy


  • Поиск ссылок / Переименование должно работать, по крайней мере, для сценариев, в которых необходимый контекст компиляции для определения закрытия проектов умещается в памяти.
  • При переименовании всего решения вы найдете файл «решения», вернетесь в обратном направлении, чтобы найти проекты, на которые ссылаются, а затем загрузите в
  • tsbuild должен обрабатывать режим -w
  • Решения могут не иметь подпроектов, происходящих за пределами папки решения.
  • Нужна четкая документация, например, для gulp, webpack

Боковая панель: это переименование сегодня не работает

function f() {
  if (Math.random() > 0.5) {
    return { foo: 10 };
  } else {
    return { foo: 20 };
}
// rename foo here doesn't rename *both* instances in the function body
f().foo;

Спасибо Райану, Мохамеду и Биллу. Получение сценария поиска ссылок / переименования, работающего в разных проектах, было одним из основных вариантов использования, которые я имел в виду, когда подавал первоначальную жалобу на то, что TypeScript не поддерживает проекты среднего размера. Средние проекты бывают модульными, но небольшими. Предложение и работа, которые я видел здесь, больше похожи на игру в масштабируемость. Это очень важно для долгосрочного здоровья TypeScript, но в основном это полезно для крупных проектов, а не для средних. То, что я слышу в этом комментарии от Райана, больше похоже на то, что необходимо для улучшения эргономики разработки проектов среднего размера на TypeScript.

Как всегда, большое спасибо всей команде TypeScript за ваши усилия! Вы делаете отличную работу.

Есть трюк с рабочим пространством lerna / yarn, который значительно облегчит вам жизнь.
Укажите записи main и types в вашем package.json подпроектов на ваш src / index. ts , и сценарий "Найти ссылки / переименовать" будет просто работать.
И вы сможете скомпилировать весь свой проект с помощью одного запущенного tsc.
Вы можете сделать это для некоторых своих пакетов, вы можете сделать это для всех. ваш звонок.

Есть некоторые недостатки и подводные камни (если у вас есть расширение в одном пакете или импорт каких-либо глобальных символов, это загрязнит всю вашу программу), но в целом это работает очень хорошо.
Когда вы хотите опубликовать в NPM, вы просто устанавливаете основные & типы на соответствующие значения (как часть вашей сборки или около того)

С приведенной выше настройкой я получил более или менее все ожидаемые функции

вот трюк с рабочим пространством lerna / yarn, который значительно облегчит вам жизнь.
Направьте записи main и types в вашем package.json подпроектов в файл src / index.ts, и сценарий «Найти ссылки / переименовать» будет просто работать.

По моему опыту, проблема с этой настройкой заключается в том, что TypeScript начнет обрабатывать ts-файлы _external_ пакетов, как если бы они были _sources_ пакета, который их требует, а не как внешние библиотеки. Это вызывает ряд проблем.

  • Внешние пакеты компилируются несколько раз, каждый раз с использованием tsconfig пакета _requiring_. Если требуемые пакеты имеют разные tsconfigs (например, разные библиотеки), это может привести к появлению ложных ошибок компиляции в требуемом пакете до тех пор, пока он не будет скомпилирован снова.

  • Требуемые пакеты также компилируются медленнее, поскольку включают в себя больше файлов, чем необходимо.

  • rootDir всех пакетов становится каталогом верхнего уровня, потенциально позволяя прямое включение любого файла TS из любого пакета, вместо простого включения из index . Если разработчики не будут осторожны, они могут обойти требуемый API пакета. Кроме того, если процесс сборки не является надежным, требуемые пакеты могут в конечном итоге содержать дублированный код из требуемого пакета, который должен был быть внешним.

В наших проектах мы исключили зависимость от файлов TS из-за недостатков. Все зависимости между пакетами находятся в файлах index.d.ts , поэтому компилятор рассматривает их как внешние, и все в порядке.

Конечно, в зависимости от .d.ts имеет проблему, требующую многоэтапной сборки (невозможно из коробки с такими инструментами, как webpack) и проблему плохого взаимодействия с IDE (переименование, ссылки, не пересекающие границы пакета ).

Я согласен с некоторыми другими мнениями: машинописный текст - это компилятор, а не система сборки. Нам нужны наши инструменты для поддержки лучших сборок с несколькими проектами. Я знаю, что в сообществе есть несколько вариантов, чтобы начать это делать. Например, в C # есть компилятор с именем Roslyn и инструмент сборки с именем MSBuild.

Обсуждение сегодня с @mhegazy о том, как заставить работать переименование с минимальными

Худший невырожденный случай переименования выглядит так:

// alpha.ts
const v = { a: 1 };
export function f() { return v; }
export function g() { return v; }

// alpha.d.ts (generated)
export function f(): { a: number };
export function g(): { a: number };

// beta.ts (in another project)
import { f } from '../etc/alpha';
f().a;

// gamma.ts (in yet another project)
import { g } from '../etc/alpha';
g().a;

Ключевое наблюдение состоит в том, что невозможно знать, что переименование f().a должно переименовывать g().a если вы не видите alpha.ts чтобы коррелировать два.

Примерный план реализации:

  • Имейте "богатое" и "экономное" представление файлов .d.ts в памяти SourceFile. "Бережливые" файлы читаются с диска; "богатые" образуются в результате генерации файлов .d.ts в памяти.
  • Исходные файлы "Rich" .d.ts имеют ссылки из своих узлов идентификаторов на исходное имя [имена] в исходном исходном файле.
  • Во время переименования мы сначала, как обычно, переходим к определению рассматриваемого символа. Если это происходит из файла .d.ts, на который есть ссылка на проект, мы загружаем его реализацией и создаем для него «Rich» файл .d.ts.

    • Примечание: этот процесс является итеративным, то есть может потребоваться проследить несколько уровней назад, пока он не попадет в реальный файл реализации (тот, который

  • Теперь используйте "многофункциональные" указатели, чтобы выяснить, какие другие идентификаторы в .d.ts проекта происходят от того же идентификатора исходного файла.
  • В каждом последующем проекте выполните сканирование текста на предмет переименованного идентификатора и посмотрите, соответствует ли его результат "go-to-def" любому из местоположений "начат с того же символа" в .d.ts
  • Выполните переименование в файле реализации

@RyanCavanaugh перейдет к определению / найдет, что все ссылки будут работать с этой моделью?

Заметки из более раннего обсуждения с Андерсом и Мохамедом большого количества открытых вопросов

  • Применяется ли prepend к .d.ts ? да
  • Что нам делать с @internal в ветке собачьего корма? Нам нужно сохранить внутренние объявления в локальных файлах .d.ts, но мы не хотим, чтобы они отображались в выходных версиях.

    • Удалить --stripInternal

    • Но не осуждайте его (пока ...?)

    • Райан пишет remove-internal tool (готово)

    • built -> Процесс LKG удаляет объявления @internal

  • Что произойдет, если вы измените файл .d.ts, например, с @types ?

    • Необходимо вручную принудительно выполнить перестройку, если вы хотите увидеть возможные новые ошибки ☹️

    • Могу со временем записать файл «файлы, которые я просмотрел.txt» в выходную папку, если это станет действительно проблематичным.

  • noEmitOnError обязателен? да.

    • Иначе при перестроении будут скрыты ошибки!

  • referenceTarget -> composable ✨ 🚲 🏡 ✨
  • А как насчет проектов листовых узлов, которые не хотят, чтобы объявления генерировались, но хотят быстро перестроить?

    • tsbuild или эквивалент могут проверить их соответствие требованиям composable имеющим отношения к восходящему потоку.

  • Циркулярные ссылки, (как) они работают?

    • По умолчанию нет

    • Вы можете указать, например, { path: "../blah", circular: true } если хотите это сделать.

    • Если вы это сделаете, вы должны убедиться, что ваша сборка детерминирована и всегда достигает фиксированной точки (возможно, это не так ?!)

    • Переназначение циклического: истинного импорта является необязательным (но приоритетным) переназначением

Разное

  • У @weswigham есть альтернативная идея переименования, которую нам нужно обсудить с @mhegazy

Я уже проиграл. В основном я просто хотел сохранить интерпретацию исходных карт вне компилятора (разделение ответственности за отдельные инструменты), но пока вас не было, я все равно работал над его добавлением (потому что, очевидно, желательно плавное определение перехода).

@RyanCavanaugh
Следует ли переименовать / найти все ссылки в проектах, на которые ссылаются, после слияния # 23944? Также должны ли мы использовать composite: true и projectReferences: [] в случае, если нужны только языковые сервисы (но не tsbuild)?

Следует ли переименовать / найти все ссылки в проектах, на которые ссылаются, после слияния # 23944?

еще нет. но мы работаем над этим дальше.

Также следует ли использовать композитные: true и projectReferences: [] в случае, если нужны только языковые сервисы (но не tsbuild)?

не уверен, что понял вопрос .. что значит "языковая служба", а не "сборка"?

не уверен, что понял вопрос .. что значит "языковая служба", а не "сборка"?

Меня интересует только поддержка редактора (переименовать / найти все ссылки / и т. Д.) В нескольких проектах в монорепозитории, а не в новом инструменте сборки (он же build mode ) (# 22997), поскольку я использую babel для моей подборки.

Это должно сработать. build - это дополнительная функция, вам не нужно ее использовать, если вы не хотите .. аналогично тому, как, например, tsc не требуется для вашего языкового обслуживания в VSCode.

Тем не менее, вам, вероятно, придется использовать объявления и карты объявлений, чтобы создавать метаданные, необходимые для работы перекрестных ссылок проекта.

Я не уверен, правильно ли я понимаю все аспекты предложения, но можно ли, чтобы отдельные проекты ссылались на другие не по пути, а по имени? В проекте рабочей области должен быть способ указать каждый путь к проекту, аналогично рабочим пространствам пряжи с помощью глобусов, или, возможно, перечислить каждое отдельное имя проекта:

В основном вместо:

"dependencies": [
    "../common", 
    "../util"
],

Можем ли мы иметь

"dependencies": [
    "common", 
    "util"
],

и есть рабочая область tsconfig.json

"workspace": {
  "common": "packages/common",
  "util": "packages/util"
}

Или, еще лучше, синтаксис путей:

"workspace": {
  "*":"packages/*"
}

Преимущества:

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

Или, по крайней мере, можем ли мы иметь имена, не являющиеся путями (те, которые не начинаются с './' или '../'), зарезервированные для будущего использования ...

Я не уверен, насколько это связано, но Yarn 1.7 недавно представил концепцию «сфокусированных рабочих пространств», см. Это сообщение в блоге .

Кто-нибудь здесь достаточно знаком как с рабочими пространствами, так и с работой, которую @RyanCavanaugh выполняет над ссылками на проекты TypeScript / режимом сборки, чтобы, возможно,

Я бы очень хотел получить последнюю информацию о ходе работы над этой функцией. Мы планируем перевести Aurelia vNext на монорепозиторий в следующем месяце или около того. Наша новая версия на 100% состоит из TypeScript, и мы хотели бы использовать официальную систему проектов TS, а не Lerna, если сможем. Мы также рады быть первопроходцами / тестерами новых функций :)

Поддержка ядра и goto def с использованием исходной карты была добавлена ​​в последней версии (TS 2.9). tsc --b для поддержки сборки уже существует и привязан к TS 3.0. Кодовая база машинописного текста перешла на его использование. В настоящее время мы тестируем эту поддержку, используя репозиторий машинописных текстов.

Что еще необходимо сделать на этом этапе: 1. найти все ссылки / переименовать для работы в сценариях с несколькими проектами. 2. Адресация обновления файлов .d.ts в фоновом режиме в редакторе и 3. --watch поддержка для сценариев с несколькими проектами. также много-много тестирования.

Этот билет продается уже 3 года. По-прежнему 3/6 выдающихся пунктов?

@claudeduguay Это фундаментальное изменение в том, какие проекты поддерживает TypeScript, время праздновать, не так ли? Я очень этому рад!

@mhegazy Это отличные новости. Я очень рад слышать, что команда TS также занимается этим над своим собственным проектом. С нетерпением жду, когда последние несколько вещей будут завершены и что это будет опция для Aurelia :) Как только появится некоторая документация по его настройке, мы переместим наш vNext в новую систему проектов. Не могу дождаться!

@mhegazy Не могли бы вы пролить свет на то, как все это будет работать с файлами package.json и проектами, основанными на модулях ES2015? Например, в Aurelia vNext у нас есть такие пакеты, как @aurelia/kernel , @aurelia/runtime , @aurelia/jit и т. Д. Это имена модулей, которые будут использоваться в import заявления на протяжении различных проектов. Как компилятор TS поймет, что имена этих модулей соответствуют различным папкам, на которые есть ссылки? Будет ли он подбирать файлы package.json из каждой указанной папки? Чем это будет отличаться от рабочих пространств Lerna или Yarn? Мой первоначальный взгляд на приведенные выше ссылки заставляет меня думать, что мне нужно использовать проекты TS (сборка) в сочетании с Lerna (депонирование и публикация), чтобы получить рабочее решение, но я не вижу, как TS будет правильно строить если он не может интегрироваться с package.json и node_modules. Источник репо TS сильно отличается от вашего среднего проекта Lerna (на самом деле ничего подобного), поэтому я начинаю задаваться вопросом, сможет ли он удовлетворить наши потребности. Любая дополнительная информация, которую вы можете предоставить, и особенно рабочая демонстрационная установка решения, аналогичная той, что я описал здесь, была бы очень полезна. Спасибо!

Я разделяю те же вопросы, что и @EisenbergEffect. В частности, я также надеюсь, что это будет хорошо работать с монорепозиторием, управляемым lerna .

Два сценария монорепозитория для рассмотрения

Начнем с установки, в которой вы связали все с помощью lerna:

/packages
  /a
    /node_modules
      /b -> symlink to b with package.json "types" pointing to dist/index.d.ts
  /b
    /dist
      /index.d.ts -> built output of entry point declaration file

Главное, что мы хотим здесь сделать, - это перестроить b мы построили. a iff a устарело. Поэтому мы добавляли "references": [ { "path": "../b" } ] к a tsconfig.json и запускали tsc --build в a чтобы получить правильные сборки восходящего потока b . В этом мире ссылки на проекты просто служат представлением графа построения и позволяют производить более разумные перестроения приращения. В идеале lerna и TS могли бы сотрудничать здесь и отображать зависимости package.json в tsconfig.json где это необходимо.

Другой сценарий (вероятно, менее распространенный) был бы, если бы вы не создавали символическую ссылку, но все же хотели бы «действовать так, как если бы» вы работали с живым набором пакетов. Вы можете сделать это сегодня с помощью довольно утомительного картирования путей, и некоторые люди это делают. Ссылки на проекты здесь также могут помочь с упорядочением сборки, но было бы очень желательно иметь поддержку свойства в референтном файле tsconfig для автоматического создания сопоставления пути всякий раз, когда на него ссылаются; например, если бы у вас был

{
  "compilerOptions": { "outDir": "bin" },
  "package": "@RyanCavanaugh/coolstuff"
}

затем добавление "references": [{ "path": "../cool" }] автоматически добавит отображение пути из @RyanCavanaugh/coolstuff -> ../cool/bin/ . Мы еще не добавили этого, но можем изучить его, если это окажется более распространенным сценарием.

В идеале lerna и TS могли бы сотрудничать здесь и отражать зависимости package.json в tsconfig.json, где это необходимо.

Вместо того, чтобы полагаться на внешние инструменты, мы могли бы предпочесть читать ваш package.json (при условии, что он находится рядом с вашим tsconfig) в качестве потенциальных ссылок, если установлен composite: true (проверьте, есть ли в каждом разрешенном пакете tsconfig.json , если он есть, считать его сборным узлом проекта и повторяться). Поскольку все символически привязано к месту, нам даже не нужно изменять разрешение (сильно? Любое?) Для обработки рабочего пространства. Lerna на самом деле не настраивает какие-либо специфичные для ts (или специфичные для сборки) вещи, как afaik, он просто символически связывает все на свои места и управляет версией. Это было бы оптимизацией по сравнению с тем, что мы делаем сегодня, а именно: загружаем файлы ts (поскольку мы предпочитаем их объявлениям) и перекомпилируем все, независимо от устаревания.

@RyanCavanaugh Звучит довольно интересно. Есть идеи, будет ли это работать со стратегией символических ссылок Rush ? Вкратце, Rush создает синтетический пакет common / temp / package.json, который содержит надмножество всех зависимостей для всех пакетов в репо. Затем мы используем pnpm для выполнения одной операции установки этого синтетического пакета. (PNPM использует символические ссылки для создания ориентированного ациклического графа вместо древовидной структуры NPM, которая исключает дублирование экземпляров библиотеки). Затем Rush создает папку node_modules для каждого проекта в репо, common / temp . Результат полностью совместим с TypeScript и стандартным алгоритмом разрешения NodeJS. Это очень быстро, потому что для всего репозитория существует один файл сжатой упаковки и одно уравнение управления версиями, при этом каждый пакет по-прежнему может указывать свои собственные зависимости.

В tsconfig.json для этой модели мы не вкладываем ничего особенного. Если для функции определения goto требуется какая-то особая конфигурация TypeScript, в идеале мы хотели бы создать ее автоматически во время установки, а не хранить в Git.

@pgonzal Спасибо за ссылку на Rush! Я этого еще не видел. Я проверю это сегодня вечером.

@RyanCavanaugh Спасибо за объяснение. Ваш первый сценарий с lerna ближе всего к тому, что было у нас. Вот наше репозиторий UX с TS и lerna в качестве примера того, что мы хотели бы использовать поддержку нового проекта на https://github.com/aurelia/ux.

@weswigham Похоже, то, что вы описываете, также подходит для нашего сценария. Пример репо выше.

Просто обратите внимание, что в случае рабочих пространств yarn модули не связаны символическими ссылками в каждом отдельном каталоге пакета, вместо этого они связаны символическими ссылками в рабочем пространстве верхнего уровня node_modules .

Кстати, поэтому я думаю, что ссылки, которые не начинаются с точки ('./' или '../'), следует зарезервировать на будущее. Надеюсь, в конечном итоге это будут «именованные ссылки», обрабатываемые с помощью стратегии разрешения активного модуля, а не относительные пути.

@spion мы просто будем использовать для этого имя свойства, отличное от path , если необходимо (например, "references": [ { "module": "@foo/baz" } ] ); Я не хочу вносить путаницу, когда "bar" и "./bar" означают одно и то же в files но другое в references

Работа над документами / публикациями в блоге ниже (будет отредактирована на основе отзывов)

Я бы посоветовал всем, кто следит за этой веткой, попробовать. Сейчас я работаю над сценарием монорепозитория, чтобы сгладить любые последние ошибки / функции, и скоро у меня появятся некоторые рекомендации по этому поводу.


Ссылки на проекты

Ссылки на проекты - это новая функция в TypeScript 3.0, которая позволяет вам разбивать ваши программы на TypeScript на более мелкие части.

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

Мы также представляем новый режим для tsc , флаг --build , который работает рука об руку со ссылками на проекты, чтобы обеспечить более быструю сборку TypeScript.

Пример проекта

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

/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json

Тестовые файлы импортируют файлы реализации и проводят некоторое тестирование:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

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

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

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

  • Встроенной проверки обновлений нет, поэтому вы всегда запускаете tsc дважды.
  • Вызов tsc дважды увеличивает накладные расходы времени на запуск
  • tsc -w не может работать одновременно с несколькими файлами конфигурации

Ссылки на проекты могут решить все эти и многие другие проблемы.

Что такое ссылка на проект?

tsconfig.json files имеют новое свойство верхнего уровня references . Это массив объектов, который определяет проекты для ссылки:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Свойство path каждой ссылки может указывать на каталог, содержащий файл tsconfig.json , или на сам файл конфигурации (который может иметь любое имя).

Когда вы ссылаетесь на проект, происходят новые вещи:

  • При импорте модулей из проекта, на который имеется ссылка, вместо этого будет загружен его выходной файл декларации ( .d.ts ).
  • Если указанный проект создает outFile , объявления выходного файла .d.ts будут видны в этом проекте.
  • В режиме сборки (см. Ниже) при необходимости автоматически создается проект, на который имеется ссылка.

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

composite

В ссылочных проектах должна быть включена новая настройка composite .
Этот параметр необходим для того, чтобы TypeScript мог быстро определить, где найти выходные данные проекта, на который имеется ссылка.
Включение флага composite меняет несколько вещей:

  • Параметр rootDir , если он не установлен явно, по умолчанию соответствует каталогу, содержащему файл tsconfig
  • Все файлы реализации должны соответствовать шаблону include или перечислены в массиве files . Если это ограничение нарушено, tsc сообщит вам, какие файлы не были указаны
  • declaration должен быть включен

declarationMaps

Мы также добавили поддержку карт источников объявлений .
Если вы включите --declarationMap , вы сможете использовать такие функции редактора, как «Перейти к определению» и «Переименовать», чтобы прозрачно перемещаться и редактировать код через границы проекта в поддерживаемых редакторах.

prepend с outFile

Вы также можете включить добавление вывода зависимости, используя параметр prepend в ссылке:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

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

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

  ^ ^ 
 /   \
B     C
 ^   ^
  \ /
   D

В этой ситуации важно не добавлять в начало каждой ссылки, потому что в результате вы получите две копии A в выводе D - это может привести к неожиданным результатам.

Предостережения для ссылок на проекты

Ссылки на проекты имеют несколько недостатков, о которых следует знать.

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

Кроме того, чтобы сохранить совместимость с существующими рабочими процессами сборки, tsc не будет автоматически строить зависимости, если не вызывается с помощью переключателя --build .
Давайте узнаем больше о --build .

Режим сборки для TypeScript

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

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

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

Вы можете указать tsc -b с несколькими путями к файлам конфигурации (например, tsc -b src test ).
Как и в случае с tsc -p , указывать само имя файла конфигурации не нужно, если он называется tsconfig.json .

tsc -b Командная строка

Вы можете указать любое количество конфигурационных файлов:

 > tsc -b                                # Build the tsconfig.json in the current directory
 > tsc -b src                            # Build src/tsconfig.json
 > tsc -b foo/release.tsconfig.json bar  # Build foo/release.tsconfig.json and bar/tsconfig.json

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

Также есть некоторые флаги, специфичные для tsc -b :

  • --verbose : распечатывает подробный журнал, чтобы объяснить, что происходит (может быть объединен с любым другим флагом)
  • --dry : показывает, что нужно сделать, но на самом деле ничего не строит
  • --clean : Удаляет выходные данные указанных проектов (может сочетаться с --dry )
  • --force : действовать так, как будто все проекты устарели
  • --watch : режим наблюдения (не может сочетаться с каким-либо флагом, кроме --verbose )

Предостережения

Обычно tsc будет выдавать выходные данные ( .js и .d.ts ) при наличии синтаксических ошибок или ошибок типа, если только noEmitOnError не включен.
Делать это в системе инкрементной сборки было бы очень плохо - если бы одна из ваших устаревших зависимостей имела новую ошибку, вы бы увидели ее только один раз, потому что последующая сборка пропустила бы сборку теперь актуального проекта.
По этой причине tsc -b действует так, как будто noEmitOnError включен для всех проектов.

Если вы проверяете какие-либо результаты сборки ( .js , .d.ts , .d.ts.map и т. Д.), Вам может потребоваться запустить сборку --force после определенного элемента управления версиями операции в зависимости от того, сохраняет ли ваш инструмент управления версиями карты времени между локальной копией и удаленной копией.

msbuild

Если у вас есть проект msbuild, вы можете включить режим сборки, добавив

    <TypeScriptBuildMode>true</TypeScriptBuildMode>

в ваш proj файл. Это включит автоматическую инкрементную сборку, а также очистку.

Обратите внимание, что, как и в случае с tsconfig.json / -p , существующие свойства проекта TypeScript не будут соблюдаться - всеми настройками следует управлять с помощью файла tsconfig.

Некоторые группы настроили рабочие процессы на основе msbuild, в которых файлы tsconfig имеют тот же неявный порядок графов, что и управляемые проекты, с которыми они связаны.
Если ваше решение похоже на это, вы можете продолжать использовать msbuild с tsc -p вместе со ссылками на проекты; они полностью совместимы.

Руководство

Общая структура

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

Еще одна хорошая практика - иметь файл «решения» tsconfig.json который просто содержит references для всех ваших проектов листовых узлов.
Это простая точка входа; например, в репозитории TypeScript мы просто запускаем tsc -b src для создания всех конечных точек, потому что мы перечисляем все подпроекты в src/tsconfig.json
Обратите внимание, что начиная с версии 3.0 пустой массив files больше не является ошибкой, если у вас есть хотя бы один reference в файле tsconfig.json .

Вы можете увидеть этот шаблон в репозитории TypeScript - см. Ключевые примеры src/tsconfig_base.json , src/tsconfig.json и src/tsc/tsconfig.json .

Структурирование относительных модулей

В общем, для перехода репо с использованием относительных модулей требуется не так уж много.
Просто поместите файл tsconfig.json в каждый подкаталог данной родительской папки и добавьте reference s к этим файлам конфигурации, чтобы они соответствовали предполагаемому распределению уровней программы.
Вам нужно будет либо установить outDir в явную подпапку выходной папки, либо установить rootDir в общий корень всех папок проекта.

Структурирование outFiles

Макет для компиляций с использованием outFile более гибкий, потому что относительные пути не имеют большого значения.
Следует иметь в виду, что обычно вы не хотите использовать prepend до «последнего» проекта - это сократит время сборки и уменьшит количество операций ввода-вывода, необходимых в любой данной сборке.
Сам репозиторий TypeScript может служить здесь хорошей ссылкой - у нас есть несколько «библиотечных» проектов и несколько проектов «конечных точек»; "конечные" проекты сохраняются как можно меньше и включают только те библиотеки, которые им нужны.

Структурирование для монорепозитория

TODO: Больше экспериментируйте и выясните это. У Раша и Лерны разные модели, которые подразумевают разные вещи с нашей стороны.

Также ищу отзывы о # 25164

@RyanCavanaugh Очень хорошее

У меня есть пара заметок:

  1. Что такое монорепо? Было бы неплохо описать этот вариант использования немного подробнее.
  2. В большинстве случаев (особенно для больших проектов) во время сборки создается множество дополнительных артефактов. В нашем случае это CSS, HTML, файлы изображений и т. Д. Через gulp. Интересно, как использование таких инструментов сборки может адаптироваться к этому новому способу работы. Скажем, я бы хотел запустить watch не только для файлов * .ts, но и для всех других наших файлов (стилей, разметки и т. Д.). Как это сделать? Нужно запустить, скажем, gulp watch и tsc -b -w параллельно?

@vvs a monorepo - это набор пакетов NPM, обычно управляемых такими инструментами, как Rush или Lerna.

Если вы используете gulp, вы захотите использовать загрузчик, который изначально понимал ссылки на проекты, чтобы получить лучший опыт. @rbuckton проделал здесь некоторую работу, так как у нас есть некоторые разработчики, использующие gulpfile внутри себя; Может быть, он сможет взвесить, как там выглядят хорошие шаблоны

@RyanCavanaugh Это выглядит хорошо. Мне очень интересно руководство Лерны :)

@RyanCavanaugh, это выглядит великолепно, сейчас я работаю над тем, чтобы опробовать его с нашим lerna monorepo.

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

Это круто! Работаю над ts-loader и сопутствующими проектами. Вероятно, потребуются ли изменения для поддержки этого в проектах, использующих LanguageServiceHost / WatchHost TypeScript?

(См. Https://github.com/TypeStrong/ts-loader/blob/master/src/servicesHost.ts для примера того, что я имею в виду.)

Если да, то все рекомендации / PR принимаются с благодарностью! Фактически, если вы хотите, чтобы это было протестировано в мире веб-пакетов, я был бы рад помочь в выпуске версии ts-loader, которая поддерживает это.

Конечно, если это «просто работает», это даже лучше: smile:

Отличная работа!

@yortus @EisenbergEffect Я создал образец репозитория lerna на https://github.com/RyanCavanaugh/learn-a с README, в котором описаны шаги, которые я предпринял, чтобы заставить его работать.

Если я правильно понимаю, tsc -b X ничего не сделает, если все (X и все его зависимости и транзитивные зависимости) обновлены? Хотите знать, можно ли это получить даже без флага -b для отдельных проектов без ссылок? (в этом случае, конечно, без зависимостей)

Это довольно круто. Я предпочитаю использовать Lerna в такой конфигурации (чтобы разделить монорепозиторий по функциям). Я полагаю, это сработает так же хорошо.

{
"lerna": "2.11.0",
"пакеты": [
"пакеты / компоненты / ","пакеты / библиотеки / ",
"пакеты / фреймворки / ","пакеты / приложения / ",
"пакеты / инструменты / *"
],
"версия": "0.0.0"
}

Значит, это доступно на typescript@next ?

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

@RyanCavanaugh Сегодня вечером я взял репо для тестового прогона. Я открыл вопрос по репо, чтобы сообщить о некоторых проблемах, которые у меня были. Еще раз спасибо за то, что собрали это вместе. Я с нетерпением жду возможности использовать его в ближайшее время.

Очень интересно! В настоящее время в моей компании мы используем мой собственный инструмент под названием mtsc для поддержки режима просмотра нескольких проектов одновременно. У нас есть около 5 проектов, которые нужно скомпилировать и посмотреть в одном репо.

У проектов разные конфиги вроде; ECMA targeting (es5, es6), типы (node, jest, DOM и т. Д.), Emit (некоторые используют webpack, а некоторые сами компилируются в js). У всех них одно общее , и это

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

Я опробовал новый режим --build с нашим lerna monorepo, который в настоящее время состоит из 17 взаимозависимых пакетов. Потребовалось время, чтобы все заработало, но теперь все работает, и возможность постепенного наращивания - большое улучшение для нас.

Я столкнулся с несколькими проблемами, которые описываю ниже. Я надеюсь, что это полезный отзыв для команды TS и может помочь другим получить режим --build работающий в их проектах.

Отзыв о режиме tsc --build

1. Ложный "\устарел, потому что выходной файл '2.map' не существует "

Я получал это сообщение для каждого пакета в каждой сборке, поэтому каждая сборка превращалась в полную повторную сборку, даже если ничего не было изменено. Я заметил, что @RyanCavanaugh уже исправил это в # 25281, поэтому это больше не проблема, если вы обновляете до 20180628 или позже каждую ночь. Следующие ниже проблемы предполагают, что вы обновили по крайней мере до 20180628 nightly.

2. "\обновлен с файлами .d.ts из его зависимостей ", когда это не так

РЕДАКТИРОВАТЬ: сообщается на # 25337.

Чтобы воспроизвести эту проблему, настройте образец репозитория @RyanCavanaugh learn-a в соответствии с его инструкциями . Запустите tsc -b packages --verbose чтобы увидеть, как все будет построено с первого раза. Теперь измените строку 1 в pkg1/src/index.ts на import {} from "./foo"; и сохраните ее. Снова запустите tsc -b packages --verbose . Сборка для pkg2 пропускается, даже если pkg1 было изменено таким образом, что нарушает исходный код pkg2 . Теперь вы можете увидеть красную волнистую линию в pkg2/src/index.ts . Снова выполните сборку с помощью tsc -b packages --force и отобразится ошибка сборки. Следующие проблемы предполагают сборку с использованием --force чтобы обойти это.

3. Созданы файлы .d.ts вызывающие ошибки сборки "Дублирующийся идентификатор" в последующих пакетах.

РЕДАКТИРОВАТЬ: сообщается на # 25338.

Чтобы воспроизвести эту проблему, настройте репозиторий @RyanCavanaugh learn-a sample в соответствии с его инструкциями . Теперь запустите lerna add @types/node чтобы добавить типизацию Node.js во все три пакета. Запустите tsc -b packages --force чтобы убедиться, что сборка продолжается. Теперь добавьте следующий код в pkg1/src/index.ts :

// CASE1 - no build errors in pkg1, but 'duplicate identifier' build errors in pkg2
// import {parse} from 'url';
// export const bar = () => parse('bar');

// CASE2 - no build errors in pkg1 or in downstream packages
// import {parse, UrlWithStringQuery} from 'url';
// export const bar = (): UrlWithStringQuery => parse('bar');

// CASE3 - no build errors in pkg1 or in downstream packages
// export declare const bar: () => import("url").UrlWithStringQuery;

// CASE4 - no build errors in pkg1, but 'duplicate identifier' build errors in pkg2
// import {parse} from 'url';
// type UrlWithStringQuery = import("url").UrlWithStringQuery;
// export const bar = (): UrlWithStringQuery => parse('bar');

Раскомментируйте один случай за раз и запустите tsc -b packages --force . Случаи 1 и 4 вызывают поток ошибок сборки в pkg2 . В случаях 2 и 3 ошибок сборки нет. Важное различие со случаями 1 и 4 кажется первой строкой в ​​сгенерированном pkg1/lib/index.d.ts :

/// <reference path="../node_modules/@types/node/index.d.ts" />

Случаи 2 и 3 не генерируют эту строку. Когда pkg2 создается в случаях 1 и 4, он включает две идентичные копии объявлений @types/node на разных путях, что вызывает ошибки "повторяющийся идентификатор".

Возможно, это сделано намеренно, поскольку случаи 2 и 3 работают. Однако это кажется довольно запутанным. Нет никаких ошибок сборки или предупреждений в pkg1 для любого из этих 4 случаев, но поведение последующей сборки очень чувствительно к точному стилю экспортируемых объявлений. Я думаю, что либо (а) pkg1 должна выдать ошибку для случаев 1 и 4, либо (б) все четыре случая должны иметь одинаковое поведение при последующей сборке, либо (в) должны быть четкие указания от команды TS по как писать объявления, чтобы избежать этой проблемы.

4. Проблемы с типами import в сгенерированных файлах .d.ts при использовании рабочих пространств yarn.

Пытаясь заставить режим сборки работать с нашими 17 пакетами monorepo, я проработал ряд ошибок сборки, вызванных относительными путями в типах import в сгенерированных файлах .d.ts . Я наконец понял, что проблема связана с подъемом модуля. То есть при использовании рабочих пространств yarn все установленные модули поднимаются в каталог monorepo-root node_modules , включая символические ссылки для всех пакетов в monorepo. Я изменил монорепозицию на использование свойства packages в lerna.json , что заставляет lerna использовать собственный алгоритм начальной загрузки без подъема, и это решило проблему. В любом случае это лучше / безопаснее, хотя и медленнее.

Я не уверен, намеревается ли TS поддерживать установки с подъемом модулей, поэтому я не разработал репро для проблем, с которыми я столкнулся, но я мог бы попытаться сделать его, если есть интерес. Я думаю, проблема может заключаться в том, что некоторые сборки получают один и тот же тип как через каталог верхнего уровня packages (согласно настройкам tsconfig), так и через каталог верхнего уровня node_modules (согласно import типов в сгенерированных .d.ts файлах). Иногда это работает из-за структурной типизации, но не работает для таких вещей, как экспортируемые уникальные символы.

5. Повторяющаяся конфигурация

Настройка монорепозитория для использования lerna в основном требует размещения чего-то вроде "packages": ["packages/*"] в lerna.json . Лерна составляет точный список пакетов, разворачивая globstars, а затем составляет точный граф зависимостей, просматривая зависимости, объявленные в package.json каждого пакета. Вы можете добавлять и удалять пакеты и зависимости по своему усмотрению, и lerna поддерживает его, не трогая его конфигурацию.

TypeScript --build mode предполагает немного больше церемоний. Шаблоны глобусов не распознаются, поэтому все пакеты должны быть явно перечислены и поддерживаться (например, в packages/tsconfig.json ) в репозитории @RyanCavanaugh learn-a sample. В режиме сборки не учитываются зависимости package.json , поэтому каждый пакет должен поддерживать список других пакетов, от которых он зависит, как в своем файле package.json (под "dependencies" ), так и в это tsconfig.json файл (под "references" ).

Это незначительное неудобство, но я включил его сюда, так как обнаружил, что эта чушь заметна по сравнению с подходом lerna's .

6. tsc сбой при глобальных дополнениях модуля

РЕДАКТИРОВАТЬ: сообщается на # 25339.

Чтобы воспроизвести эту проблему, настройте образец репозитория @RyanCavanaugh learn-a в соответствии с его инструкциями . Теперь запустите lerna add @types/multer чтобы добавить multer типов во все три пакета. Запустите tsc -b packages --force чтобы убедиться, что сборка продолжается. Теперь добавьте следующую строку в pkg1/src/index.ts :

export {Options} from 'multer';

Снова запустите tsc -b packages --force . Компилятор аварийно завершает работу из-за нарушенного утверждения. Я кратко посмотрел на трассировку стека и утверждение, и, похоже, это как-то связано с глобальным расширением пространства имен Express .

спасибо @yortus за отзыв. полагаюсь ценю это. для 3, я думаю, это https://github.com/Microsoft/TypeScript/issues/25278.

Что касается 4, я не знаком с концепцией подъема модулей. Можете ли вы уточнить и / или поделиться репродукцией?

@mhegazy многие, кто использует lerna и yarn, используют рабочие пространства (включая меня). Подробнее здесь: https://yarnpkg.com/lang/en/docs/workspaces/

В настоящее время я использую рабочие области yarn, lerna, расширенные tsconfigs, где базовый tsconfig объявляет paths общим для всех пакетов с поднятым модулем, найденным в root/node_modules . Когда я слышу yarn и monorepo , я думаю, что workspaces потому что это и есть цель функции - облегчить использование и уменьшить дублирование. Я ожидал, что это изменение просто удалит мои длинные / болезненные paths объявленные в моем базовом tsconfig.

Вот образец нашего корневого монорепозитория tsconfig (если это может помочь):

{
  "extends": "./packages/build/tsconfig.base.json",
  "compilerOptions": {
    "baseUrl": "./packages",
    "paths": {
      "@alienfast/build/*": ["./build/src/*"],
      "@alienfast/common-node/*": ["./common-node/src/*"],
      "@alienfast/common/*": ["./common/src/*"],
      "@alienfast/concepts/*": ["./concepts/src/*"],
      "@alienfast/faas/*": ["./faas/src/*"],
      "@alienfast/math/*": ["./math/src/*"],
      "@alienfast/notifications/*": ["./notifications/src/*"],
      "@alienfast/ui/*": ["./ui/src/*"],
      "@alienfast/build": ["./build/src"],
      "@alienfast/common-node": ["./common-node/src"],
      "@alienfast/common": ["./common/src"],
      "@alienfast/concepts": ["./concepts/src"],
      "@alienfast/faas": ["./faas/src"],
      "@alienfast/math": ["./math/src"],
      "@alienfast/notifications": ["./notifications/src"],
      "@alienfast/ui": ["./ui/src"],
    }
  },
  "include": ["./typings/**/*", "./packages/*/src/**/*"],
  "exclude": ["node_modules", "./packages/*/node_modules"]
}

Я попробую разветвляться для образца:
https://github.com/RyanCavanaugh/learn-a

Вот PR не для слияния с репозиторием @RyanCavanaugh с рабочими пространствами пряжи:
https://github.com/RyanCavanaugh/learn-a/pull/3/files

Мы также использовали модульный подъемник в Jupyterlab с lerna и пряжей. Это позволяет нам в основном разделять наши установленные зависимости между всеми нашими пакетами, поэтому они существуют только один раз в файловой системе, в корневом проекте.

Я понимаю, что рабочие области немного чище, чем необходимость использования команды link между всеми пакетами, чтобы они могли обращаться друг к другу (или, по крайней мере, получать доступ к своим зависимостям).

Как и выше, при подъеме модуля все зависимости перемещаются в корневой каталог node_modules . При этом используется преимущество того факта, что при разрешении узловых модулей всегда выполняется поиск по дереву каталогов и поиск во всех каталогах node_modules пока не будет найден требуемый модуль. Затем отдельные модули в вашем монорепозитории связываются с этим корневым каталогом node_modules и все просто работает. Сообщение в блоге yarn, вероятно, объясняет это лучше, чем я.

Подъем не гарантируется. Если у вас есть несовпадающие версии одного и того же пакета, они не будут подняты. Кроме того, многие существующие инструменты не поддерживают подъем, потому что они делают предположения о том, где будет node_modules , или они неправильно следуют разрешению модуля узла. Из-за этого есть параметр nohoist который может отключить подъем для определенных модулей или зависимостей.

Я добавил шестой пункт в свой предыдущий отзыв . tsc аварийно завершает работу по описанному там сценарию.

@mhegazy Я не уверен, что пункт 3 имеет отношение к # 25278. # 25278 описывает недопустимую отправку декларации. Сгенерированные мной файлы объявлений были синтаксически и семантически действительными, но приводили к созданию последующих проектов с двумя копиями типизированных узлов, что приводило к ошибкам «повторяющегося идентификатора».

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

Кстати, у этой модели есть обратная сторона: она приводит к «фантомным зависимостям», когда проект может импортировать зависимость, которая не была явно объявлена ​​в его файле package.json. Когда вы публикуете свою библиотеку, это может вызвать такие проблемы, как (1) устанавливается другая версия зависимости, отличная от той, которая была протестирована / ожидалась, или (2) зависимость полностью отсутствует, потому что она была поднята из несвязанного проекта, который не установлен. в контексте. PNPM и Rush имеют архитектурные решения, предназначенные для защиты от фантомных зависимостей.

У меня общий вопрос по поводу tsc --build : пытается ли компилятор TypeScript взять на себя роль оркестровки сборки для проектов в монорепозитории? Обычно в цепочке инструментов будет целый ряд задач, например:

  • предварительная обработка
  • создание локализованных строк
  • преобразование ресурсов в JavaScript (CSS, изображения и т. д.)
  • компиляция (проверка типов / транспиляция)
  • сворачивание файлов .js (например, webpack)
  • сворачивание файлов .d.ts (например, API Extractor)
  • постобработка, включая модульные тесты и генерацию документации

Обычно этим конвейером управляет такая система, как Gulp или Webpack, а компилятор - всего лишь один шаг в середине цепочки. Иногда вторичный инструмент также запускает сборку другим способом, например Jest + ts-jest для jest --watch .

tsc стремится сам управлять этими вещами? А если нет, есть ли способ для обычного оркестратора сборки решить сам граф зависимостей и, например, многократно вызывать tsc в каждой папке проекта в правильном порядке (после обновления предварительной обработки)?

Или, если дизайн предназначен для обработки всего монорепозитория за один проход (тогда как сегодня мы строим каждый проект в отдельном процессе NodeJs), мне также любопытно, как будут участвовать другие задачи сборки: например, будем ли мы запускать webpack на все проекты сразу? (В прошлом это приводило к проблемам нехватки памяти.) Потеряем ли мы возможность использовать параллелизм нескольких процессов?

Кстати, это не критика. Я просто пытаюсь понять общую картину и предполагаемое использование.

@pgonzal, верно, есть много частей, не связанных с tsc, для создания реального монорепозитория. Для нашей lerna monorepo я применил следующий подход:

  • каждый пакет в монорепозитории опционально определяет сценарий prebuild и / или сценарий postbuild в своем package.json . Они содержат аспекты сборки, не относящиеся к tsc.
  • в package.json монорепозитория есть следующие скрипты:
    "prebuild": "lerna run prebuild", "build": "tsc --build monorepo.tsconfig.json --verbose", "postbuild": "lerna run postbuild",
  • Вот и все. Запуск yarn build на уровне монорепозитория запускает сценарии prebuild для каждого пакета, который их определяет, затем выполняет шаг tsc --build , затем выполняет все postbuild скрипты. (По соглашению как в npm, так и в yarn выполнение npm run foo примерно такое же, как npm run prefoo && npm run foo && npm run postfoo .)

Как поступить с jest --watch или webpack-dev-server ? Например, если исходный файл изменен, выполняются ли шаги до / после сборки снова?

Влияет ли это на ts-node и связанные с ним рабочие процессы? Некоторые из наших вспомогательных приложений запускаются «напрямую» из TypeScript, например "start": "ts-node ./src/app.ts" или "start:debug": "node -r ts-node/register --inspect-brk ./src/app.ts" .

Сообщается о другой проблеме с режимом сборки на # 25355.

Спасибо за все отличные отзывы и исследования. Я очень ценю всех, кто нашел время, чтобы попробовать что-то и надеть.

@yortus re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -400439520

Отличная статья, еще раз спасибо за это. Ваши вопросы в порядке -

  1. Фиксированный
  2. PR вверх на # 25370
  3. Обсуждение зарегистрированной проблемы - не сразу видно, какое исправление правильное, но мы что-то сделаем
  4. Расследование (ниже)
  5. Зарегистрированный # 25376
  6. Технически не имеет отношения к --build AFAICT. Это новое утверждение, которое мы добавили недавно; Натан расследует

@rosskevin 🎉 за PR репо learn-a ! Я собираюсь объединить это в ветку, чтобы мы могли лучше сравнивать и контрастировать.

@pgonzal re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -401577442

У меня общий вопрос о tsc --build: пытается ли компилятор TypeScript взять на себя роль оркестровки сборки проектов в монорепозитории?

Отличный вопрос; Я хочу ответить на этот вопрос очень четко: определенно нет .

Если вы сегодня счастливы, используя tsc для создания вашего проекта, мы хотим, чтобы вы были счастливы завтра, используя tsc -b для создания вашего многокомпонентного проекта. Если вы сегодня счастливы, используя gulp для создания вашего проекта, мы хотим, чтобы вы были счастливы завтра, используя gulp для создания вашего проекта, состоящего из нескольких частей. У нас есть контроль над первым сценарием, но нам нужны авторы инструментов и плагинов, чтобы помочь нам со вторым, поэтому даже tsc -b - это всего лишь тонкая оболочка над открытыми API, которую авторы инструментов могут использовать, чтобы помочь ссылкам на проекты хорошо воспроизводиться в своих моделях сборки.

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

@borekb re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -401593804

Влияет ли это на ts-node и связанные с ним рабочие процессы? Некоторые из наших вспомогательных приложений запускаются "напрямую" из TypeScript.

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

У @EisenbergEffect были вопросы в learn-a о межпроектном переименовании и других функциях языковых служб. Большой открытый вопрос здесь только в том, сможем ли мы получить эту функцию в пригодном для использования состоянии для версии 3.0 или нет. Если это так, то переименование между проектами будет «просто работать», с оговоркой, что для нас, очевидно, невозможно окончательно найти все последующие проекты и обновить их - это будет «лучшее усилие», основанное на некоторых эвристиках для поиска других проекты.

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

Что касается таких функций, как «Перейти к определению», они работают сегодня в VS Code и будут работать «из коробки» в будущих версиях Visual Studio. Все эти функции требуют включения файлов .d.ts.map (включите declarationMap ). Есть некоторая работа над каждой функцией, чтобы осветить это, поэтому, если вы видите, что что-то работает не так, как ожидалось, запишите ошибку, так как мы могли пропустить некоторые случаи.

Открытые проблемы, которые я отслеживаю на данный момент:

  • Переименование между проектами - @andy-ms реализует
  • Необходимо проанализировать настройки подъемного модуля и понять их значение - для меня
  • Должна быть версия образца learn-a репо, которая использует yarn , другая версия, которая использует pnpm , и другая, которая использует один из репозиториев в поднятом режиме.

Открытые вопросы

  • Должны ли мы реализовать логику чтения package.json s для вывода ссылок на проекты? Зарегистрированный # 25376
  • Следует ли неявно включать .d.ts.map emit для проектов composite ?

@RyanCavanaugh, чтобы добавить

Открытые проблемы, которые я отслеживаю на данный момент

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

извините за глупый вопрос, поскольку он отмечен в дорожной карте, как мне включить эту функцию?

@ aleksey-bykov вы можете использовать его в машинописном тексте @ next.

Я только что опробовал это на нашем монорепортере с питанием от пряжи, и он хорошо работает.

Одна вещь, которую я заметил, заключалась в том, что tsc --build --watch сообщает об ошибках, но затем ничего не выводит, чтобы сказать, что сборка теперь исправлена. Стандартный режим наблюдения tsc в 2.9 начал выдавать количество ошибок, и приятно видеть там ноль, чтобы вы знали, что строительство завершено.

У меня есть папка, полная * .d.ts, и ничего больше, что я должен с этим делать:

  • сделать проект и ссылаться на него (пробовал, не сработало)
  • используйте для этого "включить"

@timfish , отзывы которых совпадают с другими, которые я слышал; записан # 25562

@ aleksey-bykov https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -400439520 должен помочь объяснить некоторые концепции

@RyanCavanaugh, похоже, ссылка на проект работает только для commonjs и разрешения узловых модулей, не так ли?

в вашем примере:

import * as p1 from "@ryancavanaugh/pkg1";
import * as p2 from "@ryancavanaugh/pkg2";

p1.fn();
p2.fn4();
  1. что такое модуль @ryancavanaugh , имеет ли он какое-либо отношение к тому, как TS разрешает модули?
  2. Этот пример должен работать с AMD (классическое разрешение модуля)?
  3. требуется ли outFile для поиска определений?
  4. где должны быть файлы d.ts, чтобы их можно было найти в проекте (могу ли я использовать outDir? найдет ли TS их там?)

У меня есть 2 простых проекта essentials и common и общие вещи не могут разрешить вещи, скомпилированные в основах:

image

@ aleksey-bykov

  1. Это просто имя модуля с ограниченной областью видимости, разрешенное в соответствии с обычным алгоритмом разрешения модуля узла.
  2. Вы можете использовать ссылки на проекты с любой системой модулей, включая классическую, но имена примеров (модули с ограниченными областями) не очень удобны для использования вне узла.
  3. Нет
  4. TypeScript ищет файлы .d.ts в том месте, где проект, на который указывает ссылка, создает их.

Если у вас есть образец репо или что-то еще, я могу диагностировать, почему вы получаете эту ошибку

@RyanCavanaugh, пожалуйста, сделай
example.zip

@RyanCavanaugh , похоже, что tsc --build --watch изначально не выводит никаких файлов, пока не увидит модификацию исходного файла.

Слишком длинная нить (как во времени, так и в пространстве); начнем обсуждение с счастливого номера 100 * 2 ^ 8: # 25600

Была ли эта страница полезной?
0 / 5 - 0 рейтинги