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

Предыдущая запись: Параметры трейлинг-типа

Следующая запись: Идентификаторы после числовых литералов

Постановка задачи

Проблема TypeScript 22124: Предоставить сводку ошибок в - красивом режиме? При рефакторинге больших проектов вывод CLI из запусков компиляции (как из одиночных компиляций, так и --watch) может накапливаться довольно быстро. Каждый раз, когда я сохраняю файл, мне кажется, что терминал заполняется огромным морем злых ошибок. Было бы удобно иметь ошибку вокруг строк Found 43 errors in 21 files. внизу каждой компиляции, чтобы отслеживать прогресс.

Вот как теперь выглядит --pretty --watch запуск с ошибками:

Пора отлаживать!

Шаг 1. Где

Где TypeScript должен печатать список сообщений об ошибках?

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

  • ~ подчеркивает под сообщениями об ошибках
  • Специальные цвета консоли

Я выполнил поиск по «~» и нашел несколько мест в src/compiler/program.ts, все в пределах функции с именем formatDiagnosticsWithColorAndContext. Идеально!

Я поставил console.log("Found", diagnostics.length, "errors"); в конец formatDiagnosticsWithColorAndContext и запустил компилятор над проектом с двумя ошибками в двух файлах, чтобы увидеть, как сообщение печатается дважды:

Похоже, что formatDiagnosticsWithColorAndContext запускается для каждого файла. Не при каждой пробежке. Не все так идеально. 😦

Полнотекстовый поиск показал, что он вызывается только в createDiagnosticReporter в src/compiler/watch.ts.. Это вызывается createWatchCompilerHostOfConfigFile, который передает его как reportDiagnostic параметр в createWatchCompilerHost. Внутри этой функции он всегда используется только в новой функции с именем emitFilesAndReportErrorUsingBuilder, которая передает его в emitFilesAndReportErrors.

Я поместил то же сообщение консоли в новое место и снова запустил компилятор в том же проекте. На этот раз в конце было одно сообщение о двух жалобах:

Ура! Нашел место.

Шаг 2: Как?

Как распечатать все ошибки?

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

2.1: Подсчет уникальных имен файлов

Тип Diagnostic имеет поле file: SourceFile | undefined, а тип SourceFile имеет поле fileName: string. Я решил, что это разумный источник информации об именах файлов. Но считать их?

Подсчет уникальных объектов обычно оптимально эффективен в JavaScript с Set. Я выполнил полнотекстовый поиск по new Set и Set.from, но ничего не нашел ... что имеет смысл, поскольку TypeScript работает в средах, в которых изначально нет Set. Но ведь в TypeScript должно быть что-то для подсчета уникальных объектов? А как насчет более старой Карты?

Я снова попытался выполнить поиск new Map и нашел две общие полезные функции в верхней части src/compiler/core.ts:

  • createDictionaryObject
  • createMap

Карты полезны, когда вам нужен объект, похожий на словарь, но нельзя гарантировать, что вам не придется проверять имена ключей крайнего регистра, такие как "hasOwnProperty". Это обычно происходит, когда вы пишете расширенный набор JavaScript.

createMap использует createDictionaryObject внутри, чтобы удовлетворить Map интерфейс. Похоже на полифиллы TypeScript Map. Я лично не проводил надежных тестов производительности, чтобы оценить, какой из них быстрее, но если у TypeScript есть функция для выбора между двумя вариантами, я решил, что имеет смысл согласиться с любым используемым TypeScript.

Вывод: используйте createMap, чтобы отслеживать уникальные имена файлов с ошибками.

2.2: Локализация ошибок и количества файлов

Диагностические сообщения, хранимые TypeScript в diagnosticInformationMap.generated.ts, представляют собой строковые шаблоны без какой-либо логики для логики множественного числа. Это немного неудобно, так как я хотел напечатать разные сообщения в каждой из трех возможностей единственного / множественного числа:

  • Одна ошибка, один файл
  • Множественные ошибки, один файл
  • Множественные ошибки, несколько файлов

Это были три новые струны.

В разделе Локализация данного руководства упоминается, что эти строки берутся из src/compiler/diagnosticMessages.json и генерируются командой jake generate-diagnostics. Например:

"Compilation complete. Watching for file changes.": {
    "category": "Message",
    "code": 6042
},

Поле "code" интересно. Вокруг этого сообщения компиляции есть коды от 6001 до 6190, а затем пробелы. Я предположил, что это какая-то организационная стратегия, и добавил три новых сообщения в _42 _-_ 43_:

  • Found_1_error_in_1_file: diag(6191, ...
  • Found_0_errors_in_1_file: diag(6192, ...
  • Found_0_errors_in_1_files: diag(6193, ...

Позже я решил щелкнуть ссылку с рекомендациями на CONTRIBUTING.md…

Диагностика Печать

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

В другом месте, например, в красивом сообщении «Compilation complete. ...», использовалась функция reportWatchDiagnostic, недоступная в emitFilesAndReportErrors. Он создавался локально в createWatchCompilerHost, но не передавался (и не был виден внутри) emitFilesAndReportErrors. Я изменил emitFilesAndReportErrors, чтобы использовать его в качестве параметра (в дополнение к system.newLine, передаваемому в reporter) и использовать его:

Здорово, что напечатали! Но тот снимок экрана с двумя сообщениями был всем. Почему нет «Starting compilation in watch mode...»?

Я просмотрел цепочку вызовов для печати сообщений через createWatchStatusReporter и обнаружил функцию clearScreenIfNotWatchingForFileChanges, очищающую экран при печати каждого диагностического сообщения, если сообщение не является конкретным «Starting compilation in watch mode...». Пугающий! 👻

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

Шаг 3: очистка

emitFilesAndReportErrors также использовался performCompilation в src/compiler/tsc.ts. Я обнаружил это, когда пытался скомпилировать, и это кричало на меня. Это было немного удивительно: почему основной компилятор tsc использует логику, специфичную для часов? Больше жуткости… 🤔

Я не смог передать добавленный reporter из performCompilation, потому что это тип WatchStatusReporter, который доступен только в коде, который имеет дело с логикой наблюдения. Это означало, что мне нужно было найти способ сообщать только о новой диагностике в режиме часов. После того, как я некоторое время ковырялся с необязательными параметрами, я вырезал и вставил emitFilesAndReportErrors в три функции:

  • getProgramDiagnosticsAndEmit: Получает диагностические данные и выводит результаты из программы.
  • reportDiagnosticErrors: выводит традиционные отчеты об ошибках, которые уже существовали.
  • summarizeDiagnosticsAcrossFiles: выводит новое итоговое сообщение.

tsc вызывает первые два; watch вызывает всех троих.

Шаг 4: запрос на включение

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



Первый коммит в этом PR содержит все, что я упомянул выше:

Https://github.com/Microsoft/TypeScript/pull/22254/commit/e0d067b48f71e545c9bef49ad5fceb0a2a17a0ea

Шаг 5: обратная связь

Кто-то попросил меня что-то изменить! Этого не произошло в запросе на извлечение последней статьи дневника отзывов…! 😥!

Было несколько важных отзывов:

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

5.1: Удаление счетчика файлов

Отсутствие необходимости сообщать о количестве файлов на самом деле упростило внесение изменений. Я удалил функцию подсчета уникальных файлов с помощью карты. Сначала было немного обидно, что мне не потребовались знания из createMap расследования… но с другой стороны, теперь я знал о внутреннем устройстве TypeScript больше, чем мог бы в противном случае. Это хорошо! 😇

5.2: Фильтрация ошибок

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

diagnostics.filter(diagnostic.category === DiagnosticCategory.Error)

👉 Совет: никогда не бойтесь просить разъяснений по пул реквестам! 👈

5.3 Дополнительный сумматор ошибок

В частности, отзывы были такими:

Я бы предпочел изменить существующий emitFilesAndReportErrors, чтобы он принимал дополнительный параметр в summaryReporter, который мог бы быть undefined | reporter, и если бы вы написали резюме, используя этого репортера.

Повод для отмены всех изменений в tsc.ts! Идеально.

5.4 Прочие отзывы

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

Слияние от мастера, пара обновлений и PR был принят! 🙌

Выводы

  1. Поиск в файлах, поиск всех ссылок, переход к определению по-прежнему являются лучшими. 🥇
  2. Учиться полезно, даже если вы не используете эти знания сразу.
  3. Рецензенты хотят, чтобы вы написали свой лучший код для проекта. Слушай их!
  4. Не бойтесь запрашивать пояснения к комментариям рецензентов, если вам это нужно.
  5. Не бойтесь начинать диалог с рецензентами, если вам это нужно.

Спасибо за прочтение! Надеюсь, у тебя что-то получилось! 💖