Open AGawrys opened 3 years ago
Excited for this! I think perhaps we could do something like this for the Bundler API, rather than having two separate bundler plugins, which seems harder to fit into the existing plugin system:
export default new Bundler({
bundle({bundleGraph}) {
// perform initial bundling
},
optimize({bundleGraph}) {
// perform initial optimization
},
update({bundleGraph, changedAssets}) {
// incrementally update cached bundle graph
}
});
One other question I had would be whether you think incremental bundling will occur only for dev builds or for cached prod builds as well? In that case, we may need another method to update an optimized build, or perhaps just a shouldOptimize
flag passed to update
?
Alternatively, we could keep only the bundle
and optimize
methods, and pass an isIncremental
flag to each rather than having a separate update
method? That would be more of a breaking API change though, whereas update
is additive. What do you think?
I think our initial though process was to flag incremental bundling, which I believe should work for both options. You are absolutely right about the additive being more preferred since we could potentially flag that similar to how we flag optimize. I think our concern was trying to minimize the amount risk to the default bundler and do this "incrementally (ha)", but it's likely improbable.
Prod builds would be interesting, especially when we move to relative pathing (I like the idea of giving someone the parcel cache then re-building).
Has there been any movement on this one? I'm taking a closer look at Parcel for some of our large codebases and am really curious about this architectural shift.
One related PR was https://github.com/parcel-bundler/parcel/pull/6514 (where bundling is skipped completely if you didn't change any imports or exports).
👊 RFC - Incremental Bundling
Created with: @joeyslater
🔦 Context
Currently, developers working on medium-to-large sized applications wait too long to see code changes reflected while developing with Parcel on the order of seconds to minutes. After building with Parcel initially, making a code change such as a statement or altering an import causes non-negligible rebuilding times; therefore, causing developer experience pain since changes can only be viewed once Parcel has finished the entire lifecycle.
🤔 Case Study
For a medium-sized application, here is a trace that highlights where time is being spent:
Almost half of rebuilding takes place in bundling, in which we’ve identified redundant building of our Bundle Graph. 70% of bundling is spent rebuilding / packaging a mutable graph that could be mutated instead. The most impact can be made by prioritizing reduction of redundant calculation in build. Success would be a time improvement in build times for developers upwards of 5s
✏️ Proposal
(Open in new window)
Incremental Bundling
Altering the bundling process requires a rewrite of the bundler as well as changes to the API. However, our changes may be localized by exploiting the plugin system Parcel already has in place.
High-level updates to the bundler
changedAssets
to take those assets, find where these assets are being used in the bundle phase and update from there.BundleGraph
is persisted from the cache when zero changes take place; start with that cached graph regardless of what changes were made.IterativeBundler
(orDefaultBundler
handles the initial and the iterative) ifchangedAssets
does not include the entire AssetGraph.changedBundles
, to give packager an opportunity to only package what’s been updated.Incremental Packaging
Ideally this could be a result of incremental bundling, since for a medium sized application, writing bundles takes almost as much time as bundling.
Once incremental bundling is implemented, the bundler should be able to provide
changedBundles
(similar tochangedAssets
), the packaging phase can target only those bundles the were updates.Since packager accepts bundles, Parcel will only pass changed bundles to the Packager.
🦋 Updates and Current Implementation
The current prototype isolates a user’s transformation (e.g. adding a dependency) , when
--incremental
flag is enabled, and then uses that subgraph (an AssetGraph of "islands") in addition to the previous cached bundle graph to produce an updated BundleGraph.The TransformationGraph is merged with the cached BundleGraph, and the
update()
function takes in the merged graph and updates all relevant bundles.✨ This method has decreased the build time on save by ~47% on a larger scale application.
Diagrams
This diagram shows the TransformationSubgraph for adding dependency
bar
tofoo
. It is used in place of the AssetGraph in the Bundling phase.Here's a high level overview of the general logic.
🧷 Current Concerns and Q's
merge()
takes on another responsibility besides being used atapplyRuntimes
🎈 Risks
🥉 Other Relevant Efforts
Update to data structures / types
Port the bundler (and other areas of Core) to Rust
LONGTERM