chore(build): Package modules according to Angular Package Format #1088
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
WIP, do not merge
Checklist
npm install
,npm run build
, andnpm test
run successfully? yesHouston, we have a packaging problem
AngularFire2 is designed to work with tree shaking. If you only need database, you import the core
AngularFireModule
and theAngularFireDatabaseModule
.While these two modules are installed through the single
angularfire2
module, they should be thought of as two separate modules.However, there is a problem with the way we packaging the library. To understand the problem, look at the import lines for
angularfire2
andangularfire2/database
.Deep imports vs. Packages
What happens when you import from a module? In 99.99% of Angular cases you're using Webpack. I'm not a Webpack expert, so I'm making some educated(ish) guesses at what is going on. Webpack is going to look at the
node_modules
for that module name and look at thepackage.json
to know which file to resolve. In the case ofangularfire2
, it will find themodule
ofindex.js
and start there. Webpack sees this is as a package.Now, what happens when you import from
angularfire2/database
? Webpack sees thesrc/database.ts
file and treats it as a deep import. This is not a package. A deep import means we are packaging this as one big module.We want Webpack and other build systems to see
angularfire2/database
as its own module. Webpack is clever and lets us get away with this mistake. However, Systemjs and commonjs builds for node break.Each feature needs to be treated as it's own hermetic module. To do this we need a proper folder structure, an ESM build, a UMD build, and a
package.json
that guides each build systems to the correct files.Project Structure
This is a rough diagram of the final build. The key thing to notice is that the main
angularfire2
folder has apackage.json
that manifests amain
,module
, andes2015
build. Themain
build is a UMD bundle, which is used by SystemJS and CommonJS. Themodule
build is ESM, and it is used by module bundlers like Webpack. Thees2015
build is pure ES2015 syntax and is used for dev sources.Notice that the
angularfire2/database
follows the same structure. It has apackage.json
with themain
,module
, andes2015
builds. When Webpack looks forangularfire2/database
it will see the dedicatedpackage.json
and not a deep import. If you look closer into thepackage.json
ofangularfire2/database
you'll see thatangularfire2
is a dependency.ESM Build
ESM is a term for JavaScript code that uses ES5 syntax but with ES2015 modules. While this is strange, this is used by module bundlers like Webpack and Rollup for better tree shaking. The ES2015 module syntax is just used to build a dependency graph and not used in the production build.
The ESM build is starts with the
index.js
file in each module.Fixing the UMD Build
AngularFire2 currently packages on UMD bundle for the core AngularFireModule, but not for the database and auth modules. This build generates a UMD bundle for each feature, and they are stored in the
bundles
folder.Angular Universal
Angular Universal requires a UMD bundle to run against CommonJS. This currently breaks due to the
angularfire2/database
deep import. However, due to the new structure andpackage.json
Angular Universal will use the UMD bundle instead of the deep import.