Skip to content

Differences in output when dynamically importing commonjs in local dev and production mode #28869

Open
@mattlewis92

Description

@mattlewis92

Command

serve, build

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

When dynamically loading a commonjs module with esbuild, the results differ depending on if you're building for production or in local development.

For local development (e.g. with vite pre-bundling) and with the old webpack builder, dynamically loading a commonjs module with import('commonjs-module') results in an object shape like so, where there is a default property containing all commonjs exports, and the commonjs exports are also available on the top level object:

{default: {export1, export2...}, export1, export2...}

However, when building for production, the dynamically imported module only has a default property like so:

{default: {export1, export2...}}

I believe this is actually a bug with esbuild, but it's been over a year now and the issue is still not fixed: evanw/esbuild#3245

The ask here is if this could be solved within the angular cli - within the esbuild builder, would it be possible to add a simple transform for all dynamically imported modules that essentially transforms:

import('commonjs-module')

to something like this (this was just a quick sketch, and probably not the optimal transform, but you get the idea):

import('commonjs-module').then((m) => ({
  ...m.default,
  ...m,
}));

For context, we are in the middle of migrating from webpack to the application builder but are stuck on this problem. While we can refactor our code to only rely on the default property, the problem becomes more difficult as it's only reproducible when disabling dependency pre-bundling (i.e. building for production), so our team are not even aware there is a problem during local development and will only find the issue after deployment, which is a bit of a footgun. Ideally behaviour during both development and in production builds would be the same.

Minimal Reproduction

gh repo clone mattlewis92/ng-cli-code-splitting-bug
cd ng-cli-code-splitting-bug
npm i
ng serve webpack # Observe browser console of the dynamically imported module contains all commonjs exports on the top level object
ng serve esbuild-with-pre-bundle # Observe browser console of the dynamically imported module contains all commonjs exports on the top level object
ng serve esbuild-no-pre-bundle # Observe browser console of the dynamically imported module only contains a `default` export

Exception or Error


Your Environment

Angular CLI: 18.2.12
Node: 20.15.0
Package Manager: npm 10.7.0
OS: darwin arm64

Angular: 18.2.12
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1802.12
@angular-devkit/build-angular   18.2.12
@angular-devkit/core            18.2.12
@angular-devkit/schematics      18.2.12
@schematics/angular             18.2.12
rxjs                            7.8.1
typescript                      5.5.4
zone.js                         0.14.10

Anything else relevant?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions