alibaba / module-federation4

Webpack5 module federation for Webpack4
https://github.com/webpack/webpack/issues/10352
BSD 3-Clause "New" or "Revised" License
54 stars 4 forks source link

Status update on backport. #2

Open ScriptedAlchemy opened 4 years ago

ScriptedAlchemy commented 4 years ago

Hello,

I’m one of the authors of Module Federation, responsible for the github issue mentioned in the readme.

Having built this mechanism - I might be able to provide insights on a backport. I currently have proprietary backports or compatibility layers running in production.

How’s the progression been on the backport?

I’ve had some success by pulling parts of the webpack 5 runtime into the compatibility layers. Creating a bi-directional interface between the two major webpack versions.

xcodebuild commented 4 years ago

Hi,

I backport main features (exposes/remotes) by migrate code by hand, works in https://github.com/alibaba/module-federation4/tree/master/examples. But shared dose not work at present.

Compatibility layers between webpack4 and webpack5 sound a genius idea, which can keep good stuff in sync. I'd like to try this way.

I have invited you to be a collaborator :)

ScriptedAlchemy commented 4 years ago

Excellent - shared is pretty complex to get perfect. Are you able to move shared modules into their own async dependency blocks? Where does it fail / how does it fail with shared, if i run the exmaples will i see the issue?

xcodebuild commented 4 years ago

I can not find a way to move shared modules into async dependency blocks yet, so shared module which return a Promise dose not works.

image

I try to implement this in branch feature/shared, we can clone feature/shared branch and follow https://github.com/alibaba/module-federation4#exmaple to see the issue.

ScriptedAlchemy commented 4 years ago

Here’s a vanilla backport, with overrides working. It looks like the format the module is being returned as might be incorrect.

https://gist.github.com/ScriptedAlchemy/d386a094832dbd9a04324862d26570e9

Move dependency to async dependency block, make the source of the original request (import react from “react”) return a shared module with the module itself having a dependency that references the async block.

Then add a runtime requirement which is initialized when the app boots. You’d want to tap into either the require ensure or it’s object that is used to hydrate an array or object of key value chunks.

I’ll pull this down. But it looks close. Does the shared dependency ever load a js file? Is it a case of just not instantiating properly? Or is the execution that would / should trigger a require.ensure failing

ScriptedAlchemy commented 4 years ago

Also, does the promise contain the code you’re after? Or is it like an empty object?

It looks, from the screenshot, like it’s attempting to access the object directly instead of a key that’s installed into the graph.

I’ll take a look tomorrow but it looks pretty close. If you get shared to split vendors out and perhaps try to load or reference the chunk. Then I think we can make this operable :) the import bootstrap code works to prevent early execution of the entrypoint. The idea being we can hoist additional shared code up and webpack can determine if it needs to load it’s own copy or if one has been supplied. When running as “host” it seems pointless to look for shared but it’s a limitation of the mechanism. We plan to internalize the import bootstrap code but it’s complex to do so in the webpack core

xcodebuild commented 4 years ago

The promise contain the code of shared module already, it's from a async loaded js.

But the dependency is not a async dependency, also there is no bootstrap code for this logic now. So it get failed. I'll take some time to give a try.

ScriptedAlchemy commented 4 years ago

Okay I think I understand what’s going on

ScriptedAlchemy commented 4 years ago

Okay, so looking at the code - its not quite right. Overridables is tricky - you are missing a large portion of the plugin

Take a look at OverridablesPlugin - you need more code in there from overridabels plugin. What you are trying to do right now is handle if or when Shared is an external, but shared is never an external. So it ends up looking for React but the code has been removed form the codebase entirely.

I see you are trying to split the chunks off. but i would remove this code

                compilation.hooks.optimizeDependencies.tap(ModuleFederationPlugin.name, modules => {
                    compilation.dependencyTemplates.set(
                        ImportDependency,
                        new ImportDependency.Template()
                    );

                    for (let mod of modules) {
                        for (let i = 0; i < mod.dependencies.length; i ++) {
                            const dep = mod.dependencies[i];
                            if (dep && this.options.shared[dep.request]) {
                                // shared
                                mod.dependencies.splice(i, 1);
                                mod.blocks.push(new AsyncDependenciesBlock(
                                    {},
                                    dep.originModule,
                                    dep.loc,
                                    dep.request,
                                ));
                            }
                        }
                    }
                });

You need to have shared, be processed by https://github.com/webpack/webpack/blob/dev-1/lib/container/OverridablesPlugin.js https://github.com/webpack/webpack/blob/dev-1/lib/container/OverridableModule.js Pretty much all the overridable code is needed to make it work correctly with shared.,

    /**
     * @param {WebpackOptions} options webpack options
     * @param {Compilation} compilation the compilation
     * @param {ResolverWithOptions} resolver the resolver
     * @param {InputFileSystem} fs the file system
     * @param {function(WebpackError=): void} callback callback function
     * @returns {void}
     */
    build(options, compilation, resolver, fs, callback) {
        this.buildMeta = {};
        this.buildInfo = {};
        const block = new AsyncDependenciesBlock({});
        const dep = new OverridableOriginalDependency(this.originalModule);
        block.addDependency(dep);
        this.addBlock(block);
        callback();
    }

https://github.com/webpack/webpack/blob/dev-1/lib/container/OverridableOriginalDependency.js

this is how you should create SharedModulesm you have to go through dependency factories,

This might also help you understand how its built

https://www.youtube.com/watch?v=HDRIvks0yyk

ckken commented 4 years ago

mark

AngusFu commented 4 years ago

Any progress?

xcodebuild commented 4 years ago

I am very busy this days, maybe I will take it again after a few weeks...

ScriptedAlchemy commented 4 years ago

Here’s an example using v5 and v4 together. With some limits. https://youtu.be/tigwyK5Khck

ScriptedAlchemy commented 4 years ago

A full backport is seemingly becoming a wider and wider gap. We recent rewrote most of the webpack graph and many other parts of the core. With the next beta feature tripling the capabilities it’s going to be very hard to maintain

zhangwilling commented 4 years ago

wow, tripling 🤣

ScriptedAlchemy commented 4 years ago

Here’s one example. There’s at least 6 others of this magnitude Advanced API: Module Federation for power users https://youtu.be/9F7GoDMrzeQ