вторник, 26 декабря 2017 г.

ASP.NET Core + NPM + Bootstrap 4

Решил попробовать bootstrap 4 в asp.net core web app. Но оказалось не все так просто ((
Создаю новое веб-приложение:
По умолчанию оно создается с bootstrap 3.3.7. Manage bower packages...
Четвертой версии в списке нет. Правильно, она же пока бета. Ставлю галку Include Prerelease:
Появилась в списке. Нажимаю кнопочку Update. Чуда не произошло:
Ничего страшного, уже привычный... Смотрю очередной бред в окне output:
EMALFORMED Failed to read ... Additional error details:
Unexpected token @
В гитхабах читаю, что это типа нормально. Просто bootstrap перестал поддерживать bower. Смирись и живи дальше. А на сайте bower я уже давно заприметил однозначный посыл
...psst! While Bower is maintained, we recommend using Yarn and Webpack for front-end projects read how to migrate!
Погуглил. Умные (наверное) люди советуют переходить на npm. Он говорят жил, жив и будет жить. Хорошо, попробуем...
Удаляю bower и все с ним связанное:

  • удаляю bower.json
  • очищаю папку wwwroot\lib
Создаю в корне проекта package.json:
Прописываю в нем то что нужно для bootstrap 4:
{
    "version": "1.0.0",
    "name": "asp.net",
    "private": true,
    "dependencies": {
        "popper.js": "1.12.9",
        "bootstrap": "4.0.0-beta.2"
    }
}
По аналогии там прописываются все остальные нужные библиотеки типа JQuery и проч.
Дальше происходит закулисная магия и в корне проекта появляется папка node_modules со всеми прописанными библиотеками. В проект эта папка не включена. Да и не нужна она там.

Вылезают два момента:

  1. Все статические файлы скриптов, стилей и т.п. должны лежать в wwwroot. Желательно там же где они и были, т.е. в wwwroot\lib. Там все будут ожидать увидеть вспомогательные сторонние библиотеки, которые пользует веб-приложение. В некоторых статьях советуют подключить к статическим файлам asp.net всю папку node_modules, но...
  2. В папке node_modules лежит куча барахла, типа исходного кода, неминифицированной версии библиотеки, справочной инфы, описания. Все это добро я не хочу видеть ни в репозитории исходного кода ни в своем веб-приложении. 
Погуглил. Умные (наверное) люди советуют для решения этих двух проблем использовать gulp. Grunt говорят устарел. Типа ставишь gulp, настраиваешь его прямыми руками и он тебе будет копировать из node_modules в wwwroot\lib, минифицировать и варить кофе.

Я .net разработчик с десятилетним стажем, изощренный в winforms, webforms и прочих forms не особо вник, что такое есть эти gulp, grunt и зачем мне вообще все это добро. Но надо пощупать...
Вкратце расскажу как было дело. Ставлю gulp. Настраиваю его прямыми руками. Он молодец - все делает - копирует, минифицирует, варит кофе. А главное его поддерживает Visual Studio. В контекстном меню Task Runner Explorer можно запускать все gulp-задачи, когда тебе хочется. Но... 

  • В node_modules появилась куча папок нужных gulp-у библиотек.
  • В gulp нужно разбираться. И мне, не особо изощренному во фронтендах разработчику, оно не особо хорошо зашло. 
  • Моим коллегам тоже придется все это ставить, разбираться, вникать...
  • В gulp надо настраивать копирование каждого скрипта, стиля и проч. Он не умеет сам определять, что важное и нужное скопировать из node_modules. А по сути надо скопировать только минифицированные файлы css и js.
  • Появилось ощущение, что для решения простых задач народ в интернетах советует слишком навороченные штуки.
Задача решается вполне знакомым инструментом из коробки - MSBuild. 

Создаю в корне проекта файл CopyLib.targets:
<Project Sdk="Microsoft.NET.Sdk.Web">
    <Target Name="BeforeBuildCustom" AfterTargets="BeforeBuild">
        <Message Text="Restore npm packages." Importance="high" />
        <Exec WorkingDirectory="$(ProjectDir)" Command="npm install" ContinueOnError="false" />
        <Message Text="Copy files from node_modules to wwwroot\lib." Importance="high" />
        <Copy SkipUnchangedFiles="true" SourceFiles="$(ProjectDir)node_modules\bootstrap\dist\css\bootstrap.min.css" DestinationFolder="wwwroot\lib\bootstrap" />
        <Copy SkipUnchangedFiles="true" SourceFiles="$(ProjectDir)node_modules\bootstrap\dist\js\bootstrap.min.js" DestinationFolder="wwwroot\lib\bootstrap" />
        <Copy SkipUnchangedFiles="true" SourceFiles="$(ProjectDir)node_modules\popper.js\dist\umd\popper.min.js" DestinationFolder="wwwroot\lib\popper.js" />
    </Target>
</Project>
По аналогии добавляется копирование всех остальных библиотек.
В csproj файл веб-приложения подключаю созданный файл:
<import project="$(MSBuildProjectDirectory)\CopyLib.targets" />
Перед сборкой проекта MSBuild:

  • восстановит все npm-пакеты с bootstrap и прочими библиотеками
  • скопирует нужные файлы в wwwroot\libs

Т.о.:

  • Используется знакомый и проверенный инструмент MSBuild
  • Не нужно затаскивать в проект новые инструменты и объяснять коллегам, что такое gulp и зачем он нужен .net разработчику. Черепная коробка не резиновая, все туда не влезет.

Наиболее эффективное программирование на C# - Билл Вагнер (2-е издание)

Закончил чтение очередной книги Билла Вагнера - Наиболее эффективное программирование на C#. 50 способов улучшения кода (2-е издани...