В WebStorm и PHPStorm для Angular проектов есть плагин встроенный, который упрощает создание всяких Angular-овских сущностей (сервисов, компонентов, директив и тыпы).
Когда создаешь что-то новое в проекте, там можно выбрать "Angular Schematic...", он спросит что именно нужно и сгенерит это (под капотом там всё тот же ng generate).
Но если вместо NodeJS интерпретатором ставить Bun, то это не работает, сыпятся ошибки вида "@angular/core - Error: failed to parse core.mjs as JSON AST object."
Ставишь обратно NodeJs - все работает. Значит проблема в Bun (спойлер, да).
Но вначале я решил, что виноват ангуляровский плагин, и что он как-то не так что-то делает.
Пошел "копать".
Нашел я значит его иходники. Написан он на Kotlin, но для получения данных вызывается кусок на JavaScript (конкретнее тут). Делает он там, по сути, 2 вещи - запускает и парсит "ng generate --help" и еще парсит всякие schematics, если я правильно понял, в добавленных в проект пакетах, ну и в ключевых @angular/core и @schematics/angular. Пока копался в этом заодно узнал, что это всё можно расширять если нужно. Вот тут отличная вводная статья про это.
Ну в общем по итогу плагин из всего этого строит доступный список команд и показывает пользователю для выбора. Ну и поскольку с виду там нет никакого криминала, я решил собрать это плагин самомстоятельно в IDEA и в отладке посмотреть, что же там приходит/уходит. Клонировал, открыл, подождал пока gradle там закачает весь свой скарб, но сбилдить не получается - сыпятся ошибки при сборке.
Вначале компилятор орал, что не может найти класс JsonFileType. Оказывается из IntelliJ Platform Plugin SDK его выкинули в 2024.3. Зачем-то предлагается подлючать com.intellij.modules.json, но там это класса тоже нет. Нарыл какой-то исходник тут, слегка доработал напильником, эта ошибка ушла.
Но дальше там пошли ошибки, что методов нет, и часть функций SDK теперь сделана корутинами, ну в смысле suspend'ами... Короче там выдало где-то 430 ошибок в проекте, я посмотрел на эту идею и забил нафиг. Может надо было не мастер клонировать, а другую ветку, но я посмотрел на 251ю чисто на гитхабе (в данный момоент IDE EAP на 251 версии), и там вроде та же проблема с JsonFileType. Хз как у джетбрэйнов оно собирается. В общем я забил.
Из исходников плагина я понял, что он запускает JS-файл и читает его вывод. Поэтому дальше я просто решил взять JS-кусок этого плагина и вызывать его напрямую через ноду и через bun и смотерть в чем там разница. Вернее вначале я хотел взять исходный кусок на TypeScript из src-js/ngCli , но там тоже какие-то ошибки компиляции были, поэтому я взял готовое JS, в которое превращается этот TS, из папки gen-resources/ngCli. Запускать там надо runner.js, передавая ему в качестве аргументов путь к папке с проектом и имя скрипта для запуска, который есть schematicsInfoProvider.js и лежит там же рядом с runner.js. Чтобы исключить побочки, я также создал новый пустой проект на Angular и путь к нему и передавал в runner.js.
Создал 2 конфигурации запуска для nodejs и для bun в WebStorm и начал запускать. И реально при запуске через nodejs я получал на выходе JSON с описанием найденных schematics. А при запуске через Bun я получал JSON c ошибками:
[
{
"name": "@angular/core",
"error": "Failed to parse \"C:\\Users\\<username>\\.bun\\install\\cache\\@angular\\core@19.1.7@@@1\\fesm2022\\core.mjs\" as JSON AST Object. InvalidSymbol at location: 102."
},
{
"name": "@schematics/angular",
"error": "Collection \"@schematics/angular\" cannot be resolved."
}
]
Ну пошел в отладку. И если отлаживать то, что было запущено под bun ещё куда ни шло (хотя все точки остановки почему-то показывались неактивными, но отладчик на них останавливался), то с нодой был полный треш в том плане, что в билиотечные функции отладчик не заходил. Ну ладно, худо-бедно, я нашел место где всё ломается. Это класс NodeModulesEngineHost из @andular-devkit и в частности его метод resolve. Там есть место где с помощь require.resolve определяется путь до package.json нужного пакета. При этом в require.resolve в resolveOptions.paths передаются пути, где надо искать. И туда передается 3 пути - 2 раза путь с проектом и еще один путь, где лежит модуль, запущенный для поиска schematic (не знаю как это правильно сформулировать, в @angular/cli в общем есть SchematicsCommandModule, он по сути и запускается из schematicsInfoProvider.js, и вот в нём есть приватный метод getResolvePaths, который с помощью __dirname этот 3й путь и определяет).
Ну и в случае nodejs там <путь к проекту>\node_modules\@angular\cli\src\command-builder. А в случае bun там "C:\Users\<username>\.bun\install\cache\@angular\cli@<version>@@@1\src\command-builder".
И nodejs перебирает все пути до корня, подставляя к ним @angular/core/package.json, и в итоге находит файл в <путь к проекту>\node_modules\@angular\core\package.json. Ну и дальше он там по логике парсится.
А bun не находит. И возникает исключение. И тогда логика пытается найти просто '@angular/core'. И согласно описанному в bun module resulution он смотрит, что указано в exports в package.json искомого модуля, а если не находит - то, что указано в module там же. А в @angular/core там указано "./fesm2022/core.mjs" . И в итоге у нас получается путь "C:\Users\<username>\.bun\install\cache\@angular\core@<version>@@@1\fesm2022\core.mjs". И вот это вот в итоге пытается парситься как JSON, естественно обламывается и генериует ошибку. С @schematics/angular аналогичная фигня. Заметьте - package.json bun якобы найти не может, но при поиске всего пакета он как-то находит этот самый package.json, чтобы из него прочитать значение module.
И вот я подумал - ну да, ошибка из-за того, что mjs пытается парситься как json. Но! Сфигали мы вообще досюда дошли? Сфигали bun не нашел @angular\core\package.json ?
Вообще, как выяснилось, bun не понимает paths, переданные ему для поиска . Ну ОК, это объясняет почему он не находит "@angular/core/package.json" в папке проекта - он просто туда не смотрит. Но почему он не находит его в своем кэше-то?!
И вот тут начинаетсмя вообще магия. Для теста я создал еще один простейший проект JS - в нем файл index.js и package.json.
package,json вообще нужен просто чтобы 2 команды там запомнить - запуск через nodejs и запуск через bun:
{
"main": "index.js",
"scripts": {
"run:node": "node index.js",
"run:bun": "bun run index.js"
}
}
index.js тоже простейший:
const request = "@angular/core/package.json";
try {
const paths = [
"E:/Projects/WEB/JS/ngTest2",
]
const path = require.resolve(request, {
paths,
});
console.log(path);
console.log(require.resolve.paths(request));
} catch (e) {
console.error(e);
console.log(require.resolve.paths(request));
}
E:/Projects/WEB/JS/ngTest2 - это пустой проект с ангуляром.
При запуске run:node, нода работает как и раньше - находит путь "E:\Projects\WEB\JS\ngTest2\node_modules\@angular\core\package.json"
При запуске run:bun, bun... находит путь у себя в кэше
"C:\Users\<username>\.bun\install\cache\@angular\core@<version>@@@1\package.json"
Да, находит. Но стоит создать в проекте (в этом из 2х файлов) каталог node_modules пустой - bun снова начинает кидать ошибку. Но стоит добавить в проект (все в этот же) @angular/core - и bun начинает его, естественно, находить и естественно в node_modules проекта.
Окай. Значит выходит bun ищет только в node_modules, если оно есть, и в своем кэше - если нет.
Но runner.js из плагина перед выполнением всей логики ставит же текущим каталогом каталог переданного ему проекта. Там же будет и node_modules, и @angular/core в нем. Будет, только вот bun на этот текущий каталог клал с прибором и ищет все относительно того каталога, где лежит запускаемый скрипт.
Окай. Но ведь там, где лежит runner.js от плагина, нет никакого node_modules - почему bun не находит в кэше своем? И вот это "сия тайна великая есть" почему так. Там когда плагин запускает ноду(или bun) есть некий класс NodeCommandLineConfigurator, может он такое окружение создает, что bun как-то видит какой-то node-module. Может вообще дело не в этом. Я попробовал покопаться в исходниках bun, но там черт ногу сломит: намешано и js+ts, и c++, и zed. Возможно, это где-то тут или тут - я не нашёл, короче.
И вот тут я подумал - а что если в каталог с плагином, там где лежит runner.js, положить и node_modules от какого-нибудь проекта с ангуляром... Плагин лежит по пути
%LOCALAPPDATA%\Programs\WebStorm*\plugins\angular\ - вместо * там может быть цифра.
Кладем в подкаталог ngCli символическую ссылку на node_modules своего ангуляровского проекта. И оно заработало!!!

Комментариев нет:
Отправить комментарий