Почему нативные модули не так просто скомпилировать под любой контроллер?
Потому что нужен компилятор под нужную архитектуру и код должен быть написан так что бы он под эту архитектуру в принципе компилировался. К тому же компиляция это обычно долго. Поэтому для популярных нативных модулей авторы распространяют заранее скомпилированные файлы (тот же sharp), но только для какого-то набора популярных операционных систем и процессоров.
В нативных модулях добавлены куски из других языков или как это происходит?
Да. Обычно на C/C++, но может быть что угодно, Rust, Go, да хоть ассемблер.
И как в npm понять, какие модули ванильные, а какие нативные?
Обычно это в документации написано, но формально никак пока не попробуешь поставить.