Skip to content

getPublicPath: "function" form vs "return" form — is the difference intentional or a bug? #5001

Description

@goblin-pitcher

Describe the bug

Hi team, I ran into a confusing issue with getPublicPath and wanted to ask whether the current behavior is intentional or a bug. If it's intentional, could the docs mention it?

What confused me

The docs say getPublicPath accepts "a stringified function or a stringified return expression", and both are executed via new Function to get the publicPath. The main example in the docs uses the function form:

getPublicPath: `function() {return "https://" + window.cdn_host + "/app/"}`,

But when I tried it, the function form didn't actually change my chunk loading URLs. Only the "return" form did.

What I found in the source

In RemoteEntryPlugin.addPublicPathEntry, there's a branch on startsWith('function'):

if (!getPublicPath.startsWith('function')) {
  code = `${compiler.webpack.RuntimeGlobals.publicPath} = new Function(...)()`;
  //     ↑ assigns to __webpack_require__.p
} else {
  code = `(${getPublicPath}())`;
  //     ↑ just runs the function, return value is discarded
}

So:

  • return form (return "https://...") → generates __webpack_require__.p = new Function(...)() → writes the value into webpack's publicPath. Webpack's chunk loader (__webpack_require__.e / .l) picks it up.
  • function form (function(){ return "https://..." }) → generates (function(){...}()) → runs but throws away the return value. __webpack_require__.p stays at whatever output.publicPath was at compile time.

The function form does still end up in the manifest's metaData.getPublicPath, so the MF runtime side (loadRemote loading the expose chunk itself) works. But webpack's native chunk loader (loading sub-chunks inside an expose) doesn't use the manifest — it uses __webpack_require__.p, which the function form never touched.

The real-world symptom

I have a host that's also loaded as a qiankun sub-app. output.publicPath is '/' (relative). When embedded in another domain:

  • The MF runtime loaded expose chunks from the correct CDN (manifest getPublicPath worked).
  • But webpack's chunk loader loaded the expose's sub-chunks from '/', which resolved to the host page domain (wrong), returned index.html, and threw ChunkLoadError: Unexpected token '<'.

Switching the exact same getPublicPath string from function(){...} to a bare return ... body fixed it — sub-chunks now load from the CDN too.

My question

Is the startsWith('function') split intentional?

  • If yes (e.g. the function form is meant only for manifest metaData, and you expect users to set __webpack_public_path__ separately for webpack chunks) — could the docs say so? Right now the docs present both forms as equivalent and even use the function form as the example, so it's easy to assume it covers webpack's chunk loader too.
  • If no — it looks like the else branch just forgot the __webpack_require__.p = prefix.

Repro

Function form (sub-chunks use compile-time publicPath, not the CDN):

getPublicPath: `function () { return 'https://cdn.example.com/' }`,
// builds:  (function () { return 'https://cdn.example.com/' }())   ← discarded

Return form (works):

getPublicPath: `try { return 'https://cdn.example.com/' } catch(e) {} return '/'`,
// builds:  __webpack_require__.p = new Function("try { return '...' } ...")()   ← assigned

Tested on @module-federation/rspack / @module-federation/enhanced 2.7.0.

Thanks!

Reproduction

Reproduction

Used Package Manager

npm

System Info

System, Binaries, Browsers

Validations

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions