Skip to content

Commit 3497cc6

Browse files
committed
refactor(@angular/build): add baseline_browserslist tool
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.
1 parent 807ba25 commit 3497cc6

13 files changed

+382
-129
lines changed

Diff for: .bazelignore

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ packages/ngtools/webpack/node_modules
1717
packages/schematics/angular/node_modules
1818
modules/testing/builder/node_modules
1919
tests/node_modules
20+
tools/baseline_browserslist/node_modules
2021
tools/legacy-rnjs/node_modules

Diff for: WORKSPACE

+1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ npm_translate_lock(
169169
"//packages/ngtools/webpack:package.json",
170170
"//packages/schematics/angular:package.json",
171171
"//tests:package.json",
172+
"//tools/baseline_browserslist:package.json",
172173
],
173174
lifecycle_hooks_envs = {
174175
# TODO: Standardize browser management for `rules_js`

Diff for: pnpm-lock.yaml

+186-129
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pnpm-workspace.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ packages:
1515
- packages/ngtools/webpack
1616
- modules/testing/builder
1717
- tests
18+
- tools/baseline_browserslist

Diff for: tools/baseline_browserslist/BUILD.bazel

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
load("@aspect_rules_js//js:defs.bzl", "js_binary")
2+
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
3+
load("@npm2//:defs.bzl", "npm_link_all_packages")
4+
load("//tools:defaults2.bzl", "jasmine_test", "ts_project")
5+
6+
npm_link_all_packages()
7+
8+
js_binary(
9+
name = "baseline_browserslist",
10+
data = [":baseline_browserslist_lib"],
11+
entry_point = "index.mjs",
12+
visibility = ["//:__subpackages__"],
13+
)
14+
15+
ts_project(
16+
name = "baseline_browserslist_lib",
17+
srcs = ["index.mts"],
18+
source_map = True,
19+
tsconfig = ":build_tsconfig",
20+
deps = [":generate_browserslist"],
21+
)
22+
23+
ts_project(
24+
name = "generate_browserslist",
25+
srcs = ["generate_browserslist.mts"],
26+
source_map = True,
27+
tsconfig = ":build_tsconfig",
28+
deps = [
29+
":node_modules/baseline-browser-mapping",
30+
],
31+
)
32+
33+
ts_project(
34+
name = "generate_browserslist_test_lib",
35+
testonly = True,
36+
srcs = ["generate_browserslist_spec.mts"],
37+
tsconfig = ":test_tsconfig",
38+
deps = [
39+
":generate_browserslist",
40+
"//:node_modules/@types/jasmine",
41+
],
42+
)
43+
44+
jasmine_test(
45+
name = "generate_browserslist_test",
46+
data = [":generate_browserslist_test_lib"],
47+
)
48+
49+
ts_config(
50+
name = "build_tsconfig",
51+
src = "tsconfig-build.json",
52+
deps = [
53+
"//:build-tsconfig-esm",
54+
"//:node_modules/@types/node",
55+
],
56+
)
57+
58+
ts_config(
59+
name = "test_tsconfig",
60+
src = "tsconfig-test.json",
61+
deps = ["//:test-tsconfig-esm"],
62+
)
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Generates a `browserslist` configuration from a Baseline date."""
2+
3+
load("@aspect_rules_js//js:defs.bzl", "js_run_binary")
4+
5+
def baseline_browserslist(name, baseline, out, **kwargs):
6+
"""Generates a `browserslist` configuration from a Baseline date.
7+
8+
Args:
9+
name: Name of this target.
10+
baseline: A string date in "YYYY-MM-DD" format of the Baseline widely
11+
available browser set to use in the generated `browserslist`.
12+
out: Name of the output browserslist file. Prefer using `.browserslistrc`
13+
for the output as the `browserslist` package seems to not like files
14+
with a different name, even when explicitly provided.
15+
16+
See: https://web.dev/baseline
17+
"""
18+
19+
js_run_binary(
20+
name = name,
21+
srcs = [],
22+
tool = Label(":baseline_browserslist"),
23+
stdout = out,
24+
args = [baseline],
25+
**kwargs
26+
)
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { getCompatibleVersions } from 'baseline-browser-mapping';
10+
11+
// Map `baseline-browser-mapping` browsers to `browserslist` browsers.
12+
const browsers: Record<string, string> = {
13+
chrome: 'Chrome',
14+
chrome_android: 'ChromeAndroid',
15+
edge: 'Edge',
16+
firefox: 'Firefox',
17+
firefox_android: 'FirefoxAndroid',
18+
safari: 'Safari',
19+
safari_ios: 'iOS',
20+
};
21+
22+
/**
23+
* Generates the `browserslist` configuration for the given Baseline date.
24+
*
25+
* @param date The Baseline "widely available" date to generate a `browserslist`
26+
* configuration for. Uses `YYYY-MM-DD` format.
27+
* @returns The `browserslist` configuration file content.
28+
*/
29+
export function generateBrowserslist(date: string): string {
30+
// Generate a `browserslist` configuration.
31+
return getCompatibleVersions({
32+
widelyAvailableOnDate: date,
33+
includeDownstreamBrowsers: false,
34+
})
35+
.filter(({ browser }) => browsers[browser])
36+
.map(({ browser, version }) => `${browsers[browser]} >= ${version}`)
37+
.join('\n');
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { generateBrowserslist } from './generate_browserslist.mjs';
10+
11+
describe('generate_browserslist', () => {
12+
describe('generateBrowserslist', () => {
13+
it('generates a `browserslist` file', () => {
14+
expect(generateBrowserslist('2025-03-31').trim()).toBe(
15+
`
16+
Chrome >= 107
17+
ChromeAndroid >= 107
18+
Edge >= 107
19+
Firefox >= 104
20+
FirefoxAndroid >= 104
21+
Safari >= 16
22+
iOS >= 16
23+
`.trim(),
24+
);
25+
});
26+
});
27+
});

Diff for: tools/baseline_browserslist/index.mts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { generateBrowserslist } from './generate_browserslist.mjs';
10+
11+
const [baselineDate] = process.argv.slice(2);
12+
const browserslist = generateBrowserslist(baselineDate);
13+
14+
// eslint-disable-next-line no-console
15+
console.log(browserslist);

Diff for: tools/baseline_browserslist/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@angular/baseline-browserslist",
3+
"version": "0.0.0-PLACEHOLDER",
4+
"private": true,
5+
"type": "module",
6+
"devDependencies": {
7+
"baseline-browser-mapping": "^2.2.0"
8+
}
9+
}

Diff for: tools/baseline_browserslist/tsconfig-build.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig-build-esm.json",
3+
"include": ["**.mts"],
4+
"exclude": ["**_spec.mts"],
5+
"compilerOptions": {
6+
"types": ["node"]
7+
}
8+
}

Diff for: tools/baseline_browserslist/tsconfig-test.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig-test-esm.json",
3+
"include": ["**_spec.mts"]
4+
}

Diff for: tools/baseline_browserslist/tsconfig.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"files": [],
3+
"references": ["./tsconfig-build.json", "./tsconfig-test.json"]
4+
}

0 commit comments

Comments
 (0)