  JavaScript is an increasingly popular language for server-side development,
thanks in part to the Node.js runtime environment and its vast ecosystem of
modules. With the Node.js package manager npm, users are able to easily include
external modules as dependencies in their projects. However, npm installs
modules with all of their functionality, even if only a fraction is needed,
which causes an undue increase in code size. Eliminating this unused
functionality from distributions is desirable, but the sound analysis required
to find unused code is difficult due to JavaScript's extreme dynamicity.
  We present a fully automatic technique that identifies unused code by
constructing static or dynamic call graphs from the application's tests, and
replacing code deemed unreachable with either file- or function-level stubs. If
a stub is called, it will fetch and execute the original code on-demand, thus
relaxing the requirement that the call graph be sound. The technique also
provides an optional guarded execution mode to guard application against
injection vulnerabilities in untested code that resulted from stub expansion.
  This technique is implemented in an open source tool called Stubbifier, which
supports the ECMAScript 2019 standard. In an empirical evaluation on 15 Node.js
applications and 75 clients of these applications, Stubbifier reduced
application size by 56% on average while incurring only minor performance
overhead. The evaluation also shows that Stubbifier's guarded execution mode is
capable of preventing several known injection vulnerabilities that are
manifested in stubbed-out code. Finally, Stubbifier can work alongside
bundlers, popular JavaScript tools for bundling an application with its
dependencies. For the considered subject applications, we measured an average
size reduction of 37% in bundled distributions.
