-
Notifications
You must be signed in to change notification settings - Fork 14
Design Decisions
While building Denali, we encounter many key design decisions that must be made. Often times, these decisions will be revisited in the future as circumstances change, Denali evolves, and new contributors arrive.
The goal of this page is to document some of the rationale for these design decisions to help our future selves remember the relevant discussion points, as well as help contributors get up to speed faster.
Bundlers provide a few benefits:
- Build step - many bundlers have turned into full fledged build systems in addition to simple bundling. We use Broccoli for this, which tends to be more robust and fits nicely with our addon system.
- Reduced bundle size via tree shaking - this is nice, but less critical for server applications than browser applications.
- Concatenated output - again, nice, but not crucial for server apps
On the other hand, bundlers introduce a non-trivial amount of complexity to the build process. Note that none of these are impossible to work around, but they add up:
- Denali leans heavily on dynamic imports via the container injections, which cannot be analyzed statically, meaning we miss out on a lot of the benefits of tree shaking
- Bundlers work best if you are dealing with the complete source code at build time. But Denali addons are precompiled before publishing, so the consuming app's bundler would need to consume the precompiled bundle for each addon. This becomes problematic by itself, and even worse with deep imports (i.e. how would the bundler import handle
import 'denali-auth/actions/login'if denali-auth only shipped with the bundledaddon.jsfile).
Nope. It just means that, given the various competing priorities in the project, and the scare contributor time available, it's not on the roadmap for the foreseeable future. That said, if you want to propose a plan for how Denali would leverage a bundler (be sure to address the shortcomings above!) and PR it - go for it!
Denali's addons are precompiled before publishing to speed up development cycles (no reason to wait for addons to compile every time you serve your app locally) and improve robustness (no "it compiled different on my machine").
There's a few constraints and use cases we designed Denali's building and packaging system around:
- Addons should precompile (therefore, published npm packages need the compiled files) to avoid the app author's performance hit of compiling on the fly every time
- Should be able to specify a Denali addon via a git dependency (i.e.
"denali-auth": "git+ssh://github.com/my-fork") - Compiled files should not be checked into source control so as not to clutter git history for addons.
- Addon authors should be able to use standard
npm linkcommands to develop their addons, andnpm publishcommands to publish them. Any caveats to the normal process should be automatically handled in the addon blueprint (i.e. needing a prepublish script is okay, as long as it can be included in the addon blueprint and it doesn't require addon authors to remember to do something different) - App authors should be able to "deep import" specific files from an addon (i.e.
import 'denali-auth/actions/login'without specifying build directories (i.e. notimport 'denali-auth/dist/actions/login').
(1), (2), and (3), taken together, are effectively impossible. If you don't check files into source control, that means you can't use an addon via a git dependency and expect it to be precompiled. So we relax (1) slightly - addons specified by git repo (or marked as in development, or more generally missing their compiled output) will build on-the-fly, but normally installed packages from npm should be precompiled, minimizing the performance hit.
(4) and (5) together are almost impossible - normally, if you npm link from a project with a dist/ folder, you need to include the dist/ in any deep imports. However, including dist/ in the import path is both confusing, and leaks information about how the addon was precompiled, making our build system more brittle should we need to change it in the future.
We could dump built files into the top level directory of the addon, meaning no dist/ path is necessary. But that means addon authors will have to deal with built files cluttering their project root while developing via npm link.
So to handle (4) and (5), we have to resort to an unfortunate bit of "magic": we patch Node's module lookup to automatically look in the dist folder of an addon when deep importing.