Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App does not start when downleveling past chrome 84 or safari 15 since angular 19 #29145

Closed
1 task done
jsaguet opened this issue Dec 16, 2024 · 7 comments · Fixed by #29250
Closed
1 task done

App does not start when downleveling past chrome 84 or safari 15 since angular 19 #29145

jsaguet opened this issue Dec 16, 2024 · 7 comments · Fixed by #29250

Comments

@jsaguet
Copy link
Contributor

jsaguet commented Dec 16, 2024

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

19.0.0-next.13

Description

Application throws an error at startup when using the webpack builder with optimizations enabled and targeting browsers like Safari < 15 or Chrome < 84.

I think it may be linked to private class fields because of the browsers version but maybe not.

Minimal Reproduction

Set up a .browserslistrc file with either Safari >= 14 or Chrome >= 83,
Configure angular.json to use the @angular-devkit/build-angular:browser builder,
Set "optimization": true and "buildOptimizer": true,
Run ng serve.

Here is a stackblitz following the above instructions: https://stackblitz.com/edit/stackblitz-starters-fr33hnlm?file=.browserslistrc

Exception or Error

NullInjectorError: R3InjectorError[t -> nt -> nt]: 
  NullInjectorError: No provider for nt!

Your Environment

Angular CLI: 19.0.0-rc.0
Node: 22.12.0
Package Manager: npm 10.9.0
OS: win32 x64

Angular: 19.0.0-rc.0
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1900.0-rc.0
@angular-devkit/build-angular   19.0.0-rc.0
@angular-devkit/core            19.0.0-rc.0 (cli-only)
@angular-devkit/schematics      19.0.0-rc.0
@schematics/angular             19.0.0-rc.0
rxjs                            7.8.1
typescript                      5.5.4
zone.js                         0.15.0

Anything else relevant?

I've noticed that the latest next versions (of both angular and angular-cli) do not have this issue but the first rc does.

@angular-devkit/build-angular:browser-esbuild builder seems to work fine.

@JeanMeche
Copy link
Member

JeanMeche commented Dec 16, 2024

A correct repro : https://stackblitz.com/edit/stackblitz-starters-rkec1xbc

The runtime cause of the error is Injector missing a bunch of static fields.

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Dec 16, 2024

Likely this is due to the removal of #28682 since in version 19 Safari 17 and 18 are officially supported prior versions are not.

@jsaguet
Copy link
Contributor Author

jsaguet commented Dec 16, 2024

It looks like it indeed.
I thought that we could downlevel our build output by specifying a larger browsers range in browserslist.

Our customers are still massively coming from browsers versions outside of the supported range.

Will we have to move to a custom builder to enable those babel transforms going forward ?

@JeanMeche
Copy link
Member

There might by something related to terser at play as esbuild doesn't look to be impacted by this.

@alan-agius4
Copy link
Collaborator

I briefly reviewed this, and it appears the issue stems from the pure-toplevel-functions Babel transformer when applied to the @angular/core package.

@GlennLefevere
Copy link

We are experiencing exactly the errors as mentioned in this issue. We need to target chrome 74 for our applications. And since upgrading to ng 19 we experienced this exact issue.

@jsaguet
Copy link
Contributor Author

jsaguet commented Jan 5, 2025

Here is what I found out regarding this issue:

The problem starts appearing at this commit angular/angular@93c3f7a

Depending on the value of useDefineForClassFields, TS will produce different representations of the static fields (in the @angular/core package) which in the end does not get transformed the same by babel and then not tree shaken the same by terser.

Note: I will only focus on the ɵprov field here but it's the same thing for other static fields.

@angular/core package content

With useDefineForClassFields: false, the static fields use static blocks:

class Injector {
  static { this.ɵprov = ɵɵdefineInjectable({
        token: Injector,
        providedIn: 'any',
        factory: () => ɵɵinject(INJECTOR$1),
    }); }
}

With useDefineForClassFields: true, the static fields are not transformed:

class Injector {
  static ɵprov = ɵɵdefineInjectable({
        token: Injector,
        providedIn: 'any',
        factory: () => ɵɵinject(INJECTOR$1),
    });
}

Build output with useDefineForClassFields: true

When targeting Safari >= 15, we get the following:

class core_Injector {
    static #_ = (
      () => {
        defineProperty_defineProperty(
          this,
          'ɵprov',
          core_defineInjectable({
            token: core_Injector,
            providedIn: 'any',
            factory: () => core_inject(INJECTOR$1)
          })
        ),
        defineProperty_defineProperty(this, '__NG_ELEMENT_ID__', - 1)
      }
   ) ()
}

When targeting Safari 14, we get the following (without optimizations)

class core_Injector {
}
_Injector = core_Injector,
defineProperty_defineProperty(
  core_Injector,
  'ɵprov',
  core_defineInjectable({
    token: _Injector,
    providedIn: 'any',
    factory: () => core_inject(INJECTOR$1)
  })
),

With optimizations enabled, we can see that the previous code is now: (This is the breaking case)

class core_Injector {
}

So it looks like Babel helper _defineProperty gets tree-shaken.

And indeed if I update the pure-toplevel-functions.ts code to exclude _defineProperty from being marked as pure, everything seems to work fine.

I've opened a PR to do so. Please let me know what you think of the solution.

Why is this not happening with esbuild based builders ?

It looks like there is no usage of @babel/preset-env with esbuild builders, which means there is no downleveling and so the problem never occurs.
Am I understanding this right @alan-agius4 ? Because that would mean there is no way to target older browsers with these builders.

jsaguet added a commit to jsaguet/angular-cli that referenced this issue Jan 5, 2025
jsaguet added a commit to jsaguet/angular-cli that referenced this issue Jan 5, 2025
jsaguet added a commit to jsaguet/angular-cli that referenced this issue Jan 6, 2025
alan-agius4 pushed a commit that referenced this issue Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment