module-federation / core

Module Federation is a concept that allows developers to share code and resources across multiple JavaScript applications
https://module-federation.io/
MIT License
1.38k stars 204 forks source link

__webpack_require__.l is not a function after build in importRemote #551

Closed konclave closed 1 year ago

konclave commented 1 year ago

I'm using importRemote from @module-federation/utilities for dynamic load of the modules inside the web component:

import { lazy, Suspense } from 'react';
import { createRoot } from 'react-dom/client';
import { importRemote } from '@module-federation/utilities';

export class CustomComponent extends HTMLElement {
....
  connectedCallback() {
    if (this.shadowRoot) {
      this.shadowRoot.innerHTML = `<div id="root"></div>`;
      CustomComponent = lazy(() =>
        importRemote({
          bustRemoteEntryCache: false,
          url: remoteEntryUrl, // comes as web component attribute
          scope,
          module,
        })
      );
      const rootElement = this.shadowRoot.getElementById('root');
      const reactRoot = createRoot(rootElement);
      reactRoot.render(
        <Suspense fallback={<slot name="loading"></slot>}>
          <CustomComponent />
        </Suspense>,
      );
    }
  }
}

And it works in development environment, but when I build the code with ts compiler and webpack, it then importRemote call fails with the error:

TypeError: t.l is not a function

t.l is webpackRequire.l call, which is literally __webpack_require__.l call.

When I log the __webpack_require__ object inside the importRemote call, I see that it has only I,S,c,g,m,o methods, but not l.

webpack configs for dev and prod are the same except devServer, ESLintPlugin and mode: develoment in dev config. Seems like this l method is not provided in webpack runtime which was created after build. What did I miss in using importRemote? Should I provide any additional details?

feedm3 commented 1 year ago

I also did a deep dive into this problem. It seems that __webpack_require__.l function gets removed with the production build.

This is __webpack_require__ in dev mode:

screenshot-31-01-2023-at-17-47-08@2x

This is __webpack_require__ after the build:

screenshot-31-01-2023-at-17-48-42@2x

Using ts-loader with "default" TS and webpack config to build the Typescript code.

ScriptedAlchemy commented 1 year ago

do you have any dynamic imports in the application? require.l is the chunk loading mechanics of webpack, if its not loading any chunks - it would remove require.l

You can try adding something like my AddRuntimeRequirementsToPromiseExternal plugin which inject loadScript as a requirement of webpack runtime (so its not shaken out)

konclave commented 1 year ago

@ScriptedAlchemy thanks! That worked. I've added a plugin based on yours to keep loadScript in the runtime and it works perfectly.

ScriptedAlchemy commented 1 year ago

Boom!

eraffel-MDSol commented 1 year ago

I'm still having this issue. I'm using importRemote and I also tried to pull in AddRuntimeRequirementToPromiseExternal to my repo, but it's never entering the module.externalType === 'promise' block when the plugin executes. Any advice?

konclave commented 1 year ago

@eraffel-MDSol I suggest removing that condition. Here's the plugin I created just to keep loadScript in the bundle:

class AddRuntimeRequirement {
  apply(compiler) {
    compiler.hooks.compilation.tap('AddRuntimeRequirement', (compilation) => {
      const { RuntimeGlobals } = compiler.webpack;
      compilation.hooks.additionalModuleRuntimeRequirements.tap('AddRuntimeRequirement', (module, set) => {
        set.add(RuntimeGlobals.loadScript);
      });
    });
  }
}
eraffel-MDSol commented 1 year ago

~@konclave Thanks, that worked, although now the issue I have is that it's trying to load the shared bundles from the host server rather than the same server as the remoteEntry.js. Any thoughts?~ nvm

konclave commented 1 year ago

@eraffel-MDSol seems like that is not related to issue topic already.