All modern high level languages have very good cross-platform system libraries and build tools for those languages usually use that language to run. Occasionally you'll run into someone that's still using Makefiles and have to break out msys but other than that it's generally fine.
I'm not trying to be inflammatory at all. I just never had any luck with any Ruby/Rails type stuff when I was forced to use Windows. I ended up resorting to JRuby.
What complex things are you doing that Windows compat creates problems? (presuming you've got some form of bash on Windows) I tend to tuck any complexity into JS devDependency libraries when using npm scripts which takes care of most cross-platform issues.
And it's not an either-or case in the real world. Our company has a big Webpack config that does a lot, and still we have 15+ scripts as well (static analysis, inserting copyright headers, sourcemap uploads, 3rd party vendoring, release scripts...). Some things are better in WP (building), while some things are just simpler with a shell script.
- it's a single, central document listing your important pipelines and their purposes - anyone unfamiliar with your setup can look at the scripts property and get a very good idea very quickly
- it's auto-detected and even auto-parsed by some tools (a Ctrl-Shift-B in vscode, for example, will allow me to select a build pipeline - there's no more well-supported central format for arbitrary loose shell scripts)
- it's a consistent entry point. If you edit your npm script to point to a separate shell script, or to run webpack/whatever instead - anything external (IDEs, CI configs) pointing to npm need not change
The above are not unique to npm, but it's a well-supported, well-understood, and very simple setup that does the above.
EDIT: (Asking this in the context of the grandparent comment suggesting getting rid of build systems in favour of NPM scripts)
The most complex "shell scripts" I've actually written for this specific purpose are simple ordered command lists, with occasional exit code checks, where appropriate. So I'm not so much talking about "writing and debugging" - this is more "replacing semicolons with newlines in a very long one-liner"
I do actually put one-liners into npm scripts instead quite often - it's a trade-off, depending on the pipeline.
Single line shell pipelines :) Exactly why I made this - https://github.com/jeswin/basho/