Skip to content

Commit f50b578

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 f50b578

File tree

8 files changed

+192
-2
lines changed

8 files changed

+192
-2
lines changed

packages/angular/build/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,18 @@
4949
"lmdb": "3.2.6"
5050
},
5151
"devDependencies": {
52-
"@angular/ssr": "workspace:*",
5352
"@angular-devkit/core": "workspace:*",
53+
"@angular/ssr": "workspace:*",
54+
"baseline-browser-mapping": "^2.2.0",
5455
"less": "4.3.0",
5556
"ng-packagr": "20.0.0-next.6",
5657
"postcss": "8.5.3",
5758
"rxjs": "7.8.2"
5859
},
5960
"peerDependencies": {
60-
"@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP",
6161
"@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP",
6262
"@angular/compiler-cli": "0.0.0-ANGULAR-FW-PEER-DEP",
63+
"@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP",
6364
"@angular/localize": "0.0.0-ANGULAR-FW-PEER-DEP",
6465
"@angular/platform-browser": "0.0.0-ANGULAR-FW-PEER-DEP",
6566
"@angular/platform-server": "0.0.0-ANGULAR-FW-PEER-DEP",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
load("@aspect_rules_js//js:defs.bzl", "js_binary")
2+
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
3+
load("//tools:defaults2.bzl", "jasmine_test", "ts_project")
4+
5+
js_binary(
6+
name = "baseline_browserslist",
7+
data = [":baseline_browserslist_lib"],
8+
entry_point = "index.mjs",
9+
visibility = ["//packages/angular/build:__subpackages__"],
10+
)
11+
12+
ts_project(
13+
name = "baseline_browserslist_lib",
14+
srcs = ["index.mts"],
15+
source_map = True,
16+
tsconfig = ":tsconfig",
17+
deps = [":generate_browserslist"],
18+
)
19+
20+
ts_project(
21+
name = "generate_browserslist",
22+
srcs = ["generate_browserslist.mts"],
23+
source_map = True,
24+
tsconfig = ":tsconfig",
25+
deps = [
26+
"//packages/angular/build:node_modules/baseline-browser-mapping",
27+
],
28+
)
29+
30+
ts_project(
31+
name = "generate_browserslist_test_lib",
32+
testonly = True,
33+
srcs = ["generate_browserslist_spec.mts"],
34+
tsconfig = "//:test-tsconfig-esm",
35+
deps = [
36+
":generate_browserslist",
37+
"//:node_modules/@types/jasmine",
38+
],
39+
)
40+
41+
jasmine_test(
42+
name = "generate_browserslist_test",
43+
data = [":generate_browserslist_test_lib"],
44+
)
45+
46+
ts_config(
47+
name = "tsconfig",
48+
src = "tsconfig-build.json",
49+
deps = [
50+
"//:build-tsconfig-esm",
51+
"//:node_modules/@types/node",
52+
],
53+
)
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+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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 = new Map(
13+
Object.entries({
14+
chrome: 'Chrome',
15+
chrome_android: 'ChromeAndroid',
16+
edge: 'Edge',
17+
firefox: 'Firefox',
18+
firefox_android: 'FirefoxAndroid',
19+
safari: 'Safari',
20+
safari_ios: 'iOS',
21+
}),
22+
);
23+
24+
/**
25+
* Generates the `browserslist` configuration for the given Baseline date.
26+
*
27+
* @param date The Baseline "widely available" date to generate a `browserslist`
28+
* configuration for. Uses `YYYY-MM-DD` format.
29+
* @returns The `browserslist` configuration file content.
30+
*/
31+
export function generateBrowserslist(date: string): string {
32+
// Generate a `browserslist` configuration.
33+
return getCompatibleVersions({
34+
widelyAvailableOnDate: date,
35+
includeDownstreamBrowsers: false,
36+
})
37+
.map(({ browser, version }) => ({ browser: browsers.get(browser), version }))
38+
.filter(({ browser }) => Boolean(browser))
39+
.map(({ browser, version }) => `${browser} >= ${version}`)
40+
.join('\n');
41+
}
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+
});
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);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../../../tsconfig-build-esm.json",
3+
"compilerOptions": {
4+
"types": ["node"]
5+
}
6+
}

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)