Skip to content

NXT 6918#394

Open
dan-ichim-lgp wants to merge 20 commits into
developfrom
feature/NXT-6918
Open

NXT 6918#394
dan-ichim-lgp wants to merge 20 commits into
developfrom
feature/NXT-6918

Conversation

@dan-ichim-lgp

@dan-ichim-lgp dan-ichim-lgp commented Jul 13, 2026

Copy link
Copy Markdown

Checklist

  • I have read and understand the contribution guide
  • A CHANGELOG entry is included
  • I have run automated testing and it is passed
  • Documentation was added or is not needed
  • This is an API breaking change

Issue Resolved / Feature Added

Replace webpack / webpack-dev-server with Bun for enact pack and enact serve.

@enact/cli now builds and serves apps through Bun.build() / Bun.serve(), keeping the existing pack/serve CLI surface (production, isomorphic, snapshot, framework/externals, locales, content-hash, no-animation, etc.).

Resolution

Removed: webpack pack/serve pipeline (config/webpack.config.js, related hash helper).
Added: Bun bundler under config/bun/ (build options, pack/serve/framework entrypoints, plugins for resolve, LESS/PostCSS/CSS modules, ESLint, isomorphic, externals, snapshot, watch, prerender worker, webOS meta, ilib).
Updated: package.json / shrinkwrap (engines.bun ≥ 1.3), bootstrap Bun lock detection, eject/info, existing docs (building-apps, serving-apps, installation, etc.), CI reusable workflow, CHANGELOG Unreleased.
Pack/serve flags from develop are preserved (-p, -i, -l, --snapshot, --framework, --externals, --content-hash, --no-animation, --no-linting, …). Follow-up commits on the branch harden Limestone/Sandstone + Jenkins behavior (React dedupe, CSS module emit, watch roots, prerender/ilib, HTML title / main.js naming).

Additional Considerations

Links

NXY-6918

Comments

Enact-DCO-1.0-Signed-off-by: Dan Ichim (dan.ichim@lgepartner.com)

@dan-ichim-lgp dan-ichim-lgp self-assigned this Jul 13, 2026
Comment thread config/bun/framework.js Dismissed
Comment thread config/bun/framework.js Dismissed
@dan-ichim-lgp
dan-ichim-lgp changed the base branch from master to develop July 14, 2026 07:09
@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor

i tried to serve qa-a11y sample in limestone , but , the app does not load in the browser. Please check

Uncaught ReferenceError: global is not defined
at entry.js:1196:3
at entry.js:49:45
at entry.js:95114:3
at entry.js:49:45
at entry.js:95153:16

ReferenceError: global is not defined: config/polyfills.js and config/corejs-proxy.js use bare global, which the old webpack config shimmed via NodePolyfillPlugin. That plugin was dropped from package.json and never replaced in the Bun config, so every app bundle throws at startup.

@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor

CSS modules are silently broken. createCssLoadResult returns {loader: 'css', exports}, but Bun ignores exports on the css loader. Please check after fixing the error above

@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor

--watch doesn't watch. Bun.build has no watch/onRebuild option

@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor
  1. qa-a11y still is not loading:
image

Comment thread config/bun/plugins/less-enact.js Outdated
return {
name: 'enact-less',
setup (build) {
build.onResolve({filter: /\?enact-css$/}, args => ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bun.build only emits CSS that is loaded in the file namespace. A {loader: 'css'} result returned from a custom namespace (enact-css-asset) is accepted without error and then discarded. The rules never reach the output bundle.

Suggestion: emit the processed CSS to a real file and import that instead

// name must NOT end in .module.css, or Bun re-scopes the already-scoped names
const hash = crypto.createHash('sha256').update(filePath).digest('hex').slice(0, 8);
const target = path.join(cssCacheDir, ${path.basename(filePath).replace(/\W/g, '_')}_${hash}.css);
fs.writeFileSync(target, processed.css, 'utf8');
return {
loader: 'js',
contents: import ${JSON.stringify(target.replace(/\\/g, '/'))};\nexport default ${JSON.stringify(processed.exports || {})};
};

Two follow-ups this requires:

  1. the .css onLoad handler must skip files under the cache dir, or it re-runs postcss on them (and with forceCSSModules recurses infinitely);
  2. relative @imports must be pinned to absolute paths before relocation: url() is already absolute via createPostcssUrlPlugin, but @import is not, and the @enovaui token imports in ThemeDecorator.module.less fail to resolve from the cache dir without it (this was the postcss-enact.js plugin).

return entryPath;
}

function getResolveAliases (context) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Multiple copies of React are bundled; App crashes at mount with "Invalid hook call".

getResolveAliases pins only react-is; the webpack config used an array ['react', 'react-dom', 'react-is', 'scheduler']. Extend the alias map the same way:

Comment thread config/bun/build.mjs
Comment on lines +319 to +324

const result = await Bun.build(buildConfig);
const info = finalizeBuild(result, buildOpts, options);
if (!info) process.exit(1);
logBuildResult(buildOpts, options, info);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In watch mode, a failing initial build exits instead of starting the watcher.

if (!info) process.exit(1) runs before runWatchLoop().
Start enact pack --watch on a tree with one syntax error and the process dies; with webpack --watch you fix the file, save, and it recovers. In watch mode, log the failure and enter the watch loop anyway:

const info = finalizeBuild(result, buildOpts, options);
if (!info && !buildOpts.watch) process.exit(1);
if (info) logBuildResult(buildOpts, options, info);

Comment thread config/bun/watch-files.js
const fs = require('fs');
const path = require('path');

const IGNORE_DIR_NAMES = new Set([

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The ignore list excludes any path segment named node_modules, and watchRoots is only the app context, so edits to the linked framework (enact/packages/* via junction) never trigger a rebuild.
webpack watched the resolved module graph, so framework development loses its feedback loop.

Consider adding resolved @enact/* real paths (when they're symlinks/junctions) to watchRoots. Also IGNORE_DIR_NAMES containing build will ignore a legitimate source dir with that name.

Comment thread config/bun/plugins/eslint-enact.js Outdated
Comment on lines +40 to +52
build.onLoad({filter: /\.(js|mjs|jsx|ts|tsx)$/}, async args => {
if (!shouldLint(args.path)) {
return undefined;
}

const eslint = getESLint(options.context);
const results = await eslint.lintFiles(args.path);
const formatter = await eslint.loadFormatter(formatterPath);
const resultText = await formatter.format(results);

if (resultText) {
console.log(resultText);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

eslint.lintFiles(args.path) + loadFormatter() run once per loaded module. This mean hundreds of full ESLint invocations per build, and the new watcher now pays that on every rebuild.
Lint once in build.onStart over the project globs and hoist the formatter load.

Comment thread config/bun/generate-html.js Outdated
.map(href => `\n\t\t<link rel="stylesheet" href="${href}" />`)
.join('');
const css = cssHref ? `\n\t\t<link rel="stylesheet" href="${cssHref}" />` : '';
const externalJs = !isomorphic && (externalScripts || [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

!isomorphic && [...] yields boolean false → isomorphic index.html renders a literal false text node after #root

// Prerender uses FileXHR, which maps bundled ilib URLs back to the filesystem.
process.env.ILIB_CONTEXT = context;
if (defines.ILIB_BASE_PATH) {
process.env.ILIB_BASE_PATH = JSON.parse(defines.ILIB_BASE_PATH);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

process.env.ILIB_BASE_PATH reset from pre-externals defines, clobbering the externals-derived path for --isomorphic --externals

@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor

please fill in PR description with relevant information for both cli and dev-utils PRs

@daniel-stoian-lgp

Copy link
Copy Markdown
Contributor

Check the output (both of the build and manual check result in the browser +no errors in console for every command).
Only after these checks pass, rerun jenkins and remeasure all the metrics to compare with webpack

Bun's CSS pipeline does not understand ~package/path imports that css-loader and less-loader handled in webpack. Add shared resolution for @import and @import-json, plus a LESS FileManager for .less imports.
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.

3 participants