Skip to content

fix: hack for rspack module federation due to dayjs -> dayjs/esm alias#604

Open
JounQin wants to merge 2 commits into
release/v9from
fix/dayjs_esm
Open

fix: hack for rspack module federation due to dayjs -> dayjs/esm alias#604
JounQin wants to merge 2 commits into
release/v9from
fix/dayjs_esm

Conversation

@JounQin
Copy link
Copy Markdown
Member

@JounQin JounQin commented Jun 2, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed module federation compatibility issue with dayjs library imports.
  • Chores

    • Simplified and optimized build process by removing redundant build steps and updating build scripts.

Copilot AI review requested due to automatic review settings June 2, 2026 14:34
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 2, 2026

🦋 Changeset detected

Latest commit: bd3f0ec

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@alauda/ui Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (3)
  • main
  • master
  • ^\d.x$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1af78017-54e9-4743-886b-1fa1f36c6a39

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dayjs_esm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR folds the former theme resource copy step into the main build script and adds a post-build patch to the generated FESM entry to work around an Rspack Module Federation dayjs -> dayjs/esm alias interop issue.

Changes:

  • Removed the standalone scripts/copy-resources.js script and related npm scripts.
  • Added an afterBuild() post-build step to copy/compile theme SCSS assets.
  • Added a build-time string patch to rewrite the dayjs import in the generated fesm2022/alauda-ui.mjs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
scripts/copy-resources.js Removed the separate resource copy/SCSS compile script.
scripts/build.js Added post-build resource handling and a dayjs import patch in the build output.
package.json Removed scripts that invoked the deleted standalone build helpers.
Comments suppressed due to low confidence (1)

scripts/build.js:23

  • The Gulp copy/compile streams are started but not awaited. Since afterBuild() is used to sequence subsequent steps (e.g., copyDist in watch mode), the build can copy the release folder before theme resources finish writing, resulting in incomplete theme assets in the copied output. Wrap the streams in Promises and await them (e.g., via Promise.all).
  gulp
    .src([
      'src/theme/_base-var.scss',
      'src/theme/_pattern.scss',
      'src/theme/_var.scss',

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/build.js
Comment thread scripts/build.js
Comment thread scripts/build.js
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/build.js (2)

53-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sequence copyDist after afterBuild() in watch mode.

Line 55 kicks off the async patch, but Lines 57-61 immediately copy release/. In watch mode that means downstream destinations can receive the unpatched alauda-ui.mjs before the dayjs rewrite lands. Please chain the copy step after afterBuild() settles and surface failures instead of fire-and-forget.

Suggested fix
 if (watch) {
-  packagr.watch().subscribe(() => {
-    afterBuild();
-
-    if (!isDebug) {
-      const src = path.resolve(__dirname, '../release');
-      const destinations = getBuildDest();
-
-      destinations.forEach(dest => copyDist(src, dest));
-    }
-  });
+  packagr.watch().subscribe({
+    next: () =>
+      afterBuild()
+        .then(() => {
+          if (!isDebug) {
+            const src = path.resolve(__dirname, '../release');
+            const destinations = getBuildDest();
+
+            destinations.forEach(dest => copyDist(src, dest));
+          }
+        })
+        .catch(err => {
+          process.exitCode = 1;
+          throw err;
+        }),
+  });
 } else {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build.js` around lines 53 - 63, The watch-mode flow currently calls
afterBuild() but immediately triggers copying of release/ which can race with
the async patch; update the packagr.watch().subscribe handler to wait for
afterBuild() to complete (use await or .then on afterBuild() within the
subscribe callback) before computing destinations via getBuildDest() and
invoking copyDist for each destination, and ensure any errors from afterBuild()
or copyDist are surfaced (reject/log) instead of being fire-and-forget so
failures in afterBuild() or copyDist are handled.

17-32: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make afterBuild() wait for the Gulp pipelines to finish.

afterBuild() is now the build-completion hook, but these two streams are started and never awaited. The function can resolve while theme/ is still being written, so any caller that treats await afterBuild() as “artifact is ready” can still race on incomplete assets.

Suggested fix
 async function afterBuild() {
   const themeDest = path.resolve(releaseDest, 'theme');
-  gulp
-    .src([
-      'src/theme/_base-var.scss',
-      'src/theme/_pattern.scss',
-      'src/theme/_var.scss',
-      'src/theme/_theme-preset.scss',
-      'src/theme/_mixin.scss',
-    ])
-    .pipe(gulp.dest(themeDest));
-
-  gulp
-    .src('src/theme/style.scss')
-    .pipe(sass().on('error', sass.logError))
-    .pipe(gulp.dest(themeDest));
+  const waitForStream = stream =>
+    new Promise((resolve, reject) => {
+      stream.on('finish', resolve);
+      stream.on('error', reject);
+    });
+
+  await Promise.all([
+    waitForStream(
+      gulp
+        .src([
+          'src/theme/_base-var.scss',
+          'src/theme/_pattern.scss',
+          'src/theme/_var.scss',
+          'src/theme/_theme-preset.scss',
+          'src/theme/_mixin.scss',
+        ])
+        .pipe(gulp.dest(themeDest)),
+    ),
+    waitForStream(
+      gulp
+        .src('src/theme/style.scss')
+        .pipe(sass().on('error', sass.logError))
+        .pipe(gulp.dest(themeDest)),
+    ),
+  ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build.js` around lines 17 - 32, afterBuild currently fires two Gulp
streams and returns before they finish; change it to await both pipelines by
returning Promise.all of two Promises that resolve on stream completion and
reject on error. Specifically, for the pipeline that pipes
gulp.src(...).pipe(gulp.dest(themeDest)) and the pipeline that pipes
gulp.src('src/theme/style.scss').pipe(sass()...).pipe(gulp.dest(themeDest)),
wrap each stream in new Promise((resolve, reject) => stream.on('end',
resolve).on('finish', resolve).on('error', reject)) (or use stream.finished) and
await Promise.all; keep references to afterBuild, themeDest, gulp.src, sass()
and gulp.dest to locate the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/build.js`:
- Around line 34-43: The current exact-string replacement for dayjs may silently
not match and leave the file unchanged; update the logic around
esmEntry/esmEntryContent and the write operation to detect whether the
replacement actually occurred and fail the build if not — after performing the
replace on esmEntryContent, compare the result to the original content and if
identical (no replacement) throw an error or call process.exit(1) with a clear
message (e.g., "dayjs import patch did not match in esmEntry") so the build
fails rather than continuing without the federation workaround.

---

Outside diff comments:
In `@scripts/build.js`:
- Around line 53-63: The watch-mode flow currently calls afterBuild() but
immediately triggers copying of release/ which can race with the async patch;
update the packagr.watch().subscribe handler to wait for afterBuild() to
complete (use await or .then on afterBuild() within the subscribe callback)
before computing destinations via getBuildDest() and invoking copyDist for each
destination, and ensure any errors from afterBuild() or copyDist are surfaced
(reject/log) instead of being fire-and-forget so failures in afterBuild() or
copyDist are handled.
- Around line 17-32: afterBuild currently fires two Gulp streams and returns
before they finish; change it to await both pipelines by returning Promise.all
of two Promises that resolve on stream completion and reject on error.
Specifically, for the pipeline that pipes
gulp.src(...).pipe(gulp.dest(themeDest)) and the pipeline that pipes
gulp.src('src/theme/style.scss').pipe(sass()...).pipe(gulp.dest(themeDest)),
wrap each stream in new Promise((resolve, reject) => stream.on('end',
resolve).on('finish', resolve).on('error', reject)) (or use stream.finished) and
await Promise.all; keep references to afterBuild, themeDest, gulp.src, sass()
and gulp.dest to locate the code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22401e76-f970-4778-b7dc-99a59d86a5de

📥 Commits

Reviewing files that changed from the base of the PR and between c294cfa and bd3f0ec.

📒 Files selected for processing (4)
  • .changeset/pink-houses-lose.md
  • package.json
  • scripts/build.js
  • scripts/copy-resources.js
💤 Files with no reviewable changes (2)
  • scripts/copy-resources.js
  • package.json

Comment thread scripts/build.js
Comment on lines +34 to +43
// hack for rspack module federation due to `dayjs -> dayjs/esm` alias
const esmEntry = path.resolve(releaseDest, 'fesm2022/alauda-ui.mjs');
const esmEntryContent = await fs.readFile(esmEntry, 'utf-8');
await fs.writeFile(
esmEntry,
esmEntryContent.replace(
"import dayjs from 'dayjs';",
"import dayjs_ from 'dayjs';\nconst dayjs = 'default' in dayjs_ ? dayjs_.default : dayjs_;",
),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail the build if the dayjs import patch stops matching.

This replacement is exact-string and currently fails open. If ng-packagr changes the emitted import format even slightly, the build will succeed without applying the federation workaround, which is the whole point of this hook.

Suggested fix
   // hack for rspack module federation due to `dayjs -> dayjs/esm` alias
   const esmEntry = path.resolve(releaseDest, 'fesm2022/alauda-ui.mjs');
   const esmEntryContent = await fs.readFile(esmEntry, 'utf-8');
+  const targetImport = "import dayjs from 'dayjs';";
+  const replacement =
+    "import dayjs_ from 'dayjs';\nconst dayjs = 'default' in dayjs_ ? dayjs_.default : dayjs_;";
+  const matches = esmEntryContent.match(/import dayjs from 'dayjs';/g)?.length ?? 0;
+
+  if (matches !== 1) {
+    throw new Error(
+      `Expected exactly one dayjs import to patch in ${esmEntry}, found ${matches}.`,
+    );
+  }
+
   await fs.writeFile(
     esmEntry,
-    esmEntryContent.replace(
-      "import dayjs from 'dayjs';",
-      "import dayjs_ from 'dayjs';\nconst dayjs = 'default' in dayjs_ ? dayjs_.default : dayjs_;",
-    ),
+    esmEntryContent.replace(targetImport, replacement),
   );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build.js` around lines 34 - 43, The current exact-string replacement
for dayjs may silently not match and leave the file unchanged; update the logic
around esmEntry/esmEntryContent and the write operation to detect whether the
replacement actually occurred and fail the build if not — after performing the
replace on esmEntryContent, compare the result to the original content and if
identical (no replacement) throw an error or call process.exit(1) with a clear
message (e.g., "dayjs import patch did not match in esmEntry") so the build
fails rather than continuing without the federation workaround.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants