Skip to content

Update .browserslistrc to use Baseline #30036

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Conversation

dgp1130
Copy link
Collaborator

@dgp1130 dgp1130 commented Apr 5, 2025

This changes the default .browserslistrc file to one generated from a Baseline "widely available" date using baseline-browser-mapping, codifying the policy change from angular/angular#60754 into the Angular CLI.

This effectively pins the .browserslistrc file to a hard-coded Baseline date, preventing it from automatically updating based on caniuse-lite versions managed in user workspaces, which should make Angular's tooling much more stable. Prior to this, expressions like "last 2 Chrome versions" were relative to the installed caniuse-lite version and could update at any time. By pinning .browserslistrc, we're ensuring that supported browsers only change during Angular major version updates, not arbitrary caniuse-lite updates.

Angular CLI now warns if users define a custom .browserslistrc which includes browsers beyond Angular's support policy, however we do not error or prevent it, given that Angular is pretty stable as a framework and generally does not require newly released web features, so targeting older browser versions is very likely to work. However we still warn in this case because it is explicitly outside Angular's support policy and is not guaranteed to work.

baseline-browser-mapping is used as a dev dependency which generates a .browserslistrc that is vendored in the NPM package. This reduces proliferation of the dependency where not needed. Shout out to @tonypconway for building baseline-browser-mapping, which makes managing this configuration significantly easier.

See individual commits for notes on the implementation.

ng-packagr/ng-packagr#3034 makes a similar update for ng-packagr.

@dgp1130 dgp1130 added area: @schematics/angular action: review The PR is still awaiting reviews from at least one requested reviewer target: major This PR is targeted for the next major release area: @angular/build labels Apr 5, 2025
@dgp1130 dgp1130 added this to the v20 Candidates milestone Apr 5, 2025
@dgp1130 dgp1130 requested review from alan-agius4 and clydin April 5, 2025 02:02
@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: docs Related to the documentation labels Apr 5, 2025
Copy link
Collaborator

@alan-agius4 alan-agius4 left a comment

Choose a reason for hiding this comment

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

Overall, this looks good to me although I am curious why we used this approach instead of extended the browserslist configuration.

Note: we also need to do the same in ng-packagr.

@alan-agius4
Copy link
Collaborator

Based on the feedback in angular/angular#60754 (comment) it looks like the used package is going away and instead extending the browserslist configuration is the preferred approach.

@dgp1130
Copy link
Collaborator Author

dgp1130 commented Apr 7, 2025

I'm not sure browserslist-config-baseline really works in this context. I see two issues:

  1. Users need to have browserslist-config-baseline installed as a dev dependency.
    • I'm not sure a transitive dependency would work with pnpm users, maybe if I resolved it correctly in @angular/build, it might work as a regular dependency there?
    • Either way, this needs to be in the user's node_modules/ directory somewhere. We can do that, but I think part of the appeal here is that we can manage these browser versions pre-built and pinned in the Angular CLI rather than having to leak a dependency into all application projects.
  2. This would be enough for ng build to do the right thing, but ng generate config browserslist would not know what browsers to generate.
    • We could manually run browserslist on an extends browserslist-config-baseline file and then write out the results, but we'd get each individual browser version, not the nice chrome >= 100 syntax.
    • We could use browserslist and then try to convert that list of browsers into the nicer syntax, either with an automated script (take the oldest one for each browser and convert it to a >= expression?) or just manually when bumping versions, but that feels brittle / tedious to me.

@alan-agius4 @tonypconway, do either of you see a better way to achieve this with browserslist-config-baseline?

@alan-agius4
Copy link
Collaborator

We could adopt browserslist-config-baseline, but the main challenge is configuring its options, particularly widelyAvailableOnDate. In a monorepo, you might have multiple projects with different .browserslistrc files but only one package.json. Configuration is resolved based on the current working directory, not the browserslist config location itself, which complicates setup. Relying on CWD isn’t ideal, as it might point deep within a project rather than the monorepo root.

Looking at the code, both bl2bl and browserslist-config-baseline use baseline-browser-mapping. Ultimately, we only need the output of baseline-browser-mapping plus a small transformation.

import bbm from 'baseline-browser-mapping';

const browsers = {
  chrome: "Chrome",
  chrome_android: "ChromeAndroid",
  edge: "Edge",
  firefox: "Firefox",
  firefox_android: "FirefoxAndroid",
  safari: "Safari",
  safari_ios: "iOS",
};

const baselineBrowserslistConfig = bbm.getCompatibleVersions({
    widelyAvailableOnDate: '2025-01-01',
    includeDownstreamBrowsers: false,
    logConfigToConsole: false,
  })
  .filter((version) => browsers[version.browser])
  .map((version) => `${browsers[version.browser]} >= ${version.version}`);

This approach can be used with ng build by calling the API directly.

For ng generate config, we could follow the same strategy. While @dgp1130 potential concerns about module resolution, it's worth noting that @schematics/angular uses a custom resolution strategy that resolves modules from the repo root, so issues are unlikely.

We could also use the above to generate a static configuration. However, it might not be worth the extra complexities in the build process.

@tonypconway
Copy link

If the end goal is absolutely a static .browserslistrc file, then yes, you could very easily import baseline-browser-mapping directly and do the transform as above. A few things I'd like to point out:

  1. If you're importing as an ES module, you can access the function directly, i.e.
import { getCompatibleVersions } from `baseline-browser-mapping`;

We have to use @http-toolkit/esm to shim commonjs behaviour for browserslist-config-baseline because browserslist doesn't support ES modules for the extends functionality.
2. Is there a reason you're excluding downstream browsers? As @notpushkin mentioned in conversation with @thesmiler , the coverage you see in browsersl.ist will be stronger if you include them, especially for anyone building in China where QQ and UC are very popular. Samsung Internet is also widely used in a lot of markets. If you decide to include them, don't forget to map the MDN browser-compat-data names for them to the browserslist names, as we've done here.
3. logConfigToConsole is a browserslist-config-baseline option, not baseline-browser-mapping, so you can leave it out if you're talking to baseline-browser-mapping directly.
4. Is there a reason you need to generate a .browserslistrc file, rather than e.g. using the extends syntax? Could you instead expose browserslist-config-baseline in the generated package.json directly so that users know they can change it, and give them a warning if they adopt something other than your default? The next release of browserslist-config-baseline will include warnings if the user has conflicting configuration options in the same package.json.

@tonypconway
Copy link

tonypconway commented Apr 10, 2025

FYI, we've just released the latest version of browserslist-config-baseline which supports the widelyAvailableOnDate config option. Appreciate generating your own .browserslistrc file via baseline-browser-mapping may be the right option for you, but if you want to expose the option to end users, it's there.

@dgp1130 dgp1130 force-pushed the baseline branch 3 times, most recently from df61433 to 46f963b Compare April 11, 2025 23:39
@dgp1130
Copy link
Collaborator Author

dgp1130 commented Apr 11, 2025

Looking at the code, both bl2bl and browserslist-config-baseline use baseline-browser-mapping. Ultimately, we only need the output of baseline-browser-mapping plus a small transformation.

I started looking into baseline-browser-mapping instead of bl2bl. I think that can work, but for some reason I'm hitting a weird Bazel dependency issue when trying to load it in a tool. I'll need to do more investigation to see what's going on there.

Is there a reason you're excluding downstream browsers?

My reasoning for this is because AIUI (feel free to correct me), a strict interpretation of Baseline does not cover downstream browsers. Therefore Angular's support policy would not cover them and I'd like the implementation here to be as close to the stated policy as reasonably possible.

I'm certainly open to including those or having the CLI slightly diverge from stated policy if there's a compelling reason to do so. My immediate thinking is that it doesn't actually matter for the CLI if we're assuming that downstream browsers are functionally equivalent to their upstream versions (not sure how entirely true that is, but likely mostly true?). If so, then adding QQ or UC wouldn't actually change the behavior of Angular CLI's build process, since an equivalent upstream browser would already be present.

Is there a reason you need to generate a .browserslistrc file, rather than e.g. using the extends syntax?

This is a good point, I like the idea of ng generate config browserslist generating extends browserslist-config-baseline since that's semantically what Angular CLI is doing (pinned to a Baseline widely available date) and I'd like the generated config to be as close as we can get to what the CLI is doing by default.

Updated the schematic to generate this. I ended up dropping .browserslistrc altogether and moving it into package.json. Personally I'm not a huge fan of configuring things in package.json and would prefer a separate .browserslistrc file, but I opted to go this direction because we need to configure browserslist-config-baseline in the package.json as well, and I think keeping those two configs as close as possible is desirable.

That also means that this config needs to install browserslist-config-baseline, which makes it a little less reliable (npm install likes to run into conflicts). But there's a pretty small list of dependencies involved here and I think it's unlikely to be a problem in practice.

@dgp1130 dgp1130 force-pushed the baseline branch 2 times, most recently from 384eb6f to 6c8b37d Compare April 15, 2025 00:12
@dgp1130
Copy link
Collaborator Author

dgp1130 commented Apr 15, 2025

Resolved the build issues and updated to use baseline-browser-mapping instead of bl2bl as that works a bit cleaner with Bazel.

dgp1130 added a commit to dgp1130/ng-packagr that referenced this pull request Apr 15, 2025
This commit updates the `browserslist` configuration to use browsers generated by Baseline. The exact list is pulled from angular/angular-cli#30036.

Ideally this should be generated at build time from a raw date string like the Angular CLI repo does, however this would duplicate the tooling and isn't strictly necessary anyways as the release process already requires extracting the generated browser versions purely for documentation. Also copying that configuration to `ng-packagr` is not significantly more toil.
visibility = ["//packages/angular/build:__subpackages__"],
)

ts_project(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems unnecessarily, we shouldn't need two ts_projects here to build the script?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was just being extra fine-grained.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's a bit of overkill to create two separate projects for just two files especially when one file depends on the other.

@dgp1130
Copy link
Collaborator Author

dgp1130 commented Apr 16, 2025

Strict deps test failures seem to be a bug in the upstream infra: angular/dev-infra#2738.

@dgp1130 dgp1130 force-pushed the baseline branch 3 times, most recently from 9ff69f6 to 7a138f0 Compare April 16, 2025 00:51
Copy link
Collaborator

@alan-agius4 alan-agius4 left a comment

Choose a reason for hiding this comment

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

Thanks for this @dgp1130, happy to see that we finally got there. Good work!

Just 3 optional NITs.

  • Remove extra fields from package.json
  • Reduce ts_projects
  • Node.js Promise imports.
  • Update renovate config to include the new package.json

clydin and others added 4 commits April 16, 2025 14:20
This tool uses `baseline-browser-mapping` to generate a `browserslist` configuration at build time from a given widely-available Baseline date.

I opted _not_ to use "downstream browsers". This refers to browsers like Opera, which technically uses Blink and shares the same featureset as Chrome for a particular version. I decided against this to maintain a stricter interpretation of Baseline, given that those browsers are not included in Baseline today. Developers can manually widen their own `.browserslistrc` if they really want to and are comfortable accepting the risks that brings.

Using `baseline-browser-mapping` as part of the build process means there is a potential risk that a bugfix in the package generates a different browserslist file which leads to a different build configuration that causes a problem for existing users. However, it's also just as likely (if not moreso) to fix a problem than cause one, so I'm inclined to call that WAI. If it becomes an issue in the future, we can potentially check in the generated `.browserslistrc` file itself rather than the Baseline date, meaning the list of browsers would be frozen until we explicitly update it between majors.
…le Baseline

This commit includes a Baseline widely-available date which is hard-coded in `buid_vars.bzl` and used to generate a `.browserslistrc` file at build-time.

Using a browser outside of Angular's minimum defined browser set is still allowed as we expect that _most_ of the time this will work just fine. However, we log a warning to be clear to users that they are outside Angular's supported browserset.

I've currently pinned Angular to the March 31st baseline, but this will likely be updated again as we get closer to the v20 release.

The current set of browsers generated are:

```
Chrome >= 107
ChromeAndroid >= 107
Edge >= 107
Firefox >= 104
FirefoxAndroid >= 104
Safari >= 16
iOS >= 16
```
… config browserslist`

This changes `ng generate config browserslist` to no longer generate a list of browsers used by Angular, but instead generate a dependency on `browserslist-config-baseline` and configures the date to match Angular. This used to generate a `.browserslistrc` file, however since the config is a single line and `browserslist-config-baseline` requires a separate config in the `package.json`, it feels a little more ergonomic to put both in the `package.json` file instead.
@dgp1130
Copy link
Collaborator Author

dgp1130 commented Apr 16, 2025

  • Remove extra fields from package.json

Done.

  • Reduce ts_projects

Done.

  • Node.js Promise imports.

Done.

  • Update renovate config to include the new package.json

I think it's already covered by the **/package.json glob. I don't see anything that would exclude tools/baseline_browserslist/package.json. Let me know if I'm misreading this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action: review The PR is still awaiting reviews from at least one requested reviewer area: @angular/build area: docs Related to the documentation area: @schematics/angular detected: feature PR contains a feature commit target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants