Skip to content

Conversation

vinicius-at-webflow
Copy link
Contributor

Description

The _handleContainerModule method from packages/manifest/src/ModuleHandler.ts calls split(' ') on the module's identifier which breaks if the path or expose key contain spaces or other special characters.

Image

Here's a an example of a problematic identifier (see the import path for the Button expose):

container entry (default) [["./Button",{"import":["./src/path with spaces/Button.tsx"],"name": "__federation_expose_Button"}]]

Related Issue

#2684

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

Copy link

changeset-bot bot commented Sep 1, 2025

⚠️ No Changeset found

Latest commit: 94d354c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

netlify bot commented Sep 1, 2025

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit 94d354c
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/68c713d6f16b300008dca037
😎 Deploy Preview https://deploy-preview-4034--module-federation-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@vinicius-at-webflow
Copy link
Contributor Author

vinicius-at-webflow commented Sep 1, 2025

Opened previous PR with main as source branch 😓. Sorry for the inconvenience!

Comment on lines +295 to +313
let entries: Array<
[exposeKey: string, { import: string[]; name?: string }]
> = [];

try {
// Prefer parsing exposes from stats first
entries = JSON.parse(data[3]);
} catch {
// If that fails, fallback to the original options
const exposes = this._options.exposes;

if (!exposes || typeof exposes !== 'object') {
return;
}

entries = Object.entries(exposes);
}

entries.forEach(([prefixedName, file]) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm opting to maintain the current JSON parsing logic and wrap it in a try catch block. There are probably good reason why it was implemented this way in the first place, but I lack the context, and I rather be as safe/backwards compatible as possible. If it fails the introduced logic just attempts to recover exposes from another source.

@2heal1 @ScriptedAlchemy As you mentioned in the issue, there are probably better ways of solving this, but again, I lack the context to propose a long-term solution. You mentioned a short-term solution, but since the issue was closed, I figured it wouldn't hurt proposing this patch 😅.

Also note I'm open to contribute and implement this in a different way if I can get some high level guidance (Just a couple of quick notes would be really helpful).

@@ -18,6 +18,7 @@ module.exports = {
library: { type: 'commonjs-module' },
filename: 'remoteEntry.js',
exposes: {
'./a test module': './test.js',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does break without the patch. Would this be enough test coverage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is not expected expose key, will this issue only happen in this scene ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@2heal1 Oh, you're absolutely right. But it does break if the spaces are present on the path, say:

{
  exposes: {
     './test': './path with spaces/test.js'
  }
}

I just wanted to modify the test as little as possible, but I can tweak it to represent an actual real scenario!

// If that fails, fallback to the original options
const exposes = this._options.exposes;

if (!exposes || typeof exposes !== 'object') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my investigations this.options.exposes in ModuleHandler is always parsed into

interface ExposesObject {
    [k: string]: ExposesConfig;
}

interface ExposesConfig {
    import: ExposesItem | ExposesItems;
    name?: string;
}

by ContainerManager.containerPluginExposesOptions. But due to how everything is currently typed, TS thinks this could be the broader "unparsed" Exposes shape.

type Exposes = (ExposesItem | ExposesObject)[] | ExposesObject;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how it looks like, for both enhanced/ModuleFederationPlugin and rspack/ModuleFederationPlugin

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, we could maintain this 100% decoupled and idempotently "re-parse" the entries ModuleHandler to ensure the type safety, but I would wait for a review first before increasing the complexity here. cc @ScriptedAlchemy @2heal1

@vinicius-at-webflow vinicius-at-webflow marked this pull request as ready for review September 1, 2025 19:40
@2heal1
Copy link
Member

2heal1 commented Sep 5, 2025

Does the issue only happen in legacy version ? I want to know which version , if old enough, maybe no need to compate it .

Can you provide a minimal reproduce repo ? So i can get more info .

@vinicius-at-webflow
Copy link
Contributor Author

vinicius-at-webflow commented Sep 5, 2025

@2heal1 No, it happens on any version, even the latest. Its probably under the radar (although there is an issue open #2684) because it is very unusual to have spaces in paths in "conventional" codebases 😆.

But in our use case of Module Federation, we have no control over what the project folder structure looks like (and in fact we have run into the issue more than once), so this is why I'm proposing this fix!

Here's the repro, just npm install; npm run build:

https://github.com/vinicius-at-webflow/mf-path-issues

You should see

[webpack-cli] HookWebpackError: Unterminated string in JSON at position 120
  SyntaxError: Unterminated string in JSON at position 120
    at JSON.parse (<anonymous>)
    at ModuleHandler._handleContainerModule

because of "./test-2": "./src/path with spaces/test-2.js",

@vinicius-at-webflow
Copy link
Contributor Author

vinicius-at-webflow commented Sep 5, 2025

I can probably add an integration test using this repo I sent you, to avoid any confusion around what this is fixing, exactly, so I guess a test would make everything more clear.

@vinicius-at-webflow
Copy link
Contributor Author

vinicius-at-webflow commented Sep 6, 2025

@2heal1 I added a dedicated test case to validate the fix. If you uncomment you should see the same

Unterminated string in JSON at position 31; stack = HookWebpackError: Unterminated string in JSON at position 31
    at JSON.parse (<anonymous>)
    at ModuleHandler._handleContainerModule 

as the user reports in the issue, as well as in the minimal repro I shared with you

@vinicius-at-webflow
Copy link
Contributor Author

@2heal1 bump

@ScriptedAlchemy
Copy link
Member

@codex review

Copy link

Codex Review: Didn't find any major issues. Hooray!

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

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

Successfully merging this pull request may close these issues.

3 participants