- 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.
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.