.js file containing all your project code, but this leads to problems in scope, size, readability and maintainability.
IIFEs solve scoping issues for large projects; when script files are wrapped by an IIFE, you can safely concatenate or safely combine files without worrying about scope collision.
The use of IIFEs led to tools like Make, Gulp, Grunt, Broccoli or Brunch. These tools are known as task runners, and they concatenate all your project files together.
However, changing one file means you have to rebuild the whole thing. Concatenating makes it easy to reuse scripts across files but makes build optimizations more difficult. How can you find out if code is actually being used or not?
Even if you only use a single function from lodash, you have to add the entire library and then squish it together. How do you treeshake the dependencies on your code? Lazy loading chunks of code can be hard to do at scale and requires a lot of manual work from the developer.
CommonJS came out and introduced
require, which allows you to load and use a module in the current file. This solved scope issues out of the box by importing each module as it was needed.
But there is no browser support for CommonJS. There are no live bindings. There are problems with circular references. Synchronous module resolution and loading is slow. While CommonJS was a great solution for Node.js projects, browsers didn't support modules, so bundlers and tools like Browserify, RequireJS and SystemJS were created, allowing us to write CommonJS modules that run in a browser.
The good news for web projects is that modules are becoming an official feature in the ECMAScript standard. However, browser support is incomplete and bundling is still faster and currently recommended over these early module implementations.
Old school Task Runners and even Google Closure Compiler requires you to manually declare all dependencies upfront. While bundlers like webpack automatically builds and infer your dependency graph based on what is imported and exported. This along with other plugins and loaders make for a great developer experience.
...to have something that will not only let us write modules but also support any module format (at least until we get to ESM) and handle resources and assets at the same time?
webpack cares about performance and load times; it's always improving or adding new features, such as async chunk loading and prefetching, to deliver the best possible experience for your project and your users.