Skip to content

Commit c3ff142

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 c3ff142

File tree

6 files changed

+142
-0
lines changed

6 files changed

+142
-0
lines changed
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,33 @@
1+
import { getCompatibleVersions } from 'baseline-browser-mapping';
2+
3+
// Map `baseline-browser-mapping` browsers to `browserslist` browsers.
4+
const browsers = new Map(
5+
Object.entries({
6+
chrome: 'Chrome',
7+
chrome_android: 'ChromeAndroid',
8+
edge: 'Edge',
9+
firefox: 'Firefox',
10+
firefox_android: 'FirefoxAndroid',
11+
safari: 'Safari',
12+
safari_ios: 'iOS',
13+
}),
14+
);
15+
16+
/**
17+
* Generates the `browserslist` configuration for the given Baseline date.
18+
*
19+
* @param date The Baseline "widely available" date to generate a `browserslist`
20+
* configuration for. Uses `YYYY-MM-DD` format.
21+
* @returns The `browserslist` configuration file content.
22+
*/
23+
export function generateBrowserslist(date: string): string {
24+
// Generate a `browserslist` configuration.
25+
return getCompatibleVersions({
26+
widelyAvailableOnDate: date,
27+
includeDownstreamBrowsers: false,
28+
})
29+
.map(({ browser, version }) => ({ browser: browsers.get(browser)!, version }))
30+
.filter(({ browser }) => Boolean(browser))
31+
.map(({ browser, version }) => `${browser} >= ${version}`)
32+
.join('\n');
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { generateBrowserslist } from './generate_browserslist.mjs';
2+
3+
describe('generate_browserslist', () => {
4+
describe('generateBrowserslist', () => {
5+
it('generates a `browserslist` file', () => {
6+
expect(generateBrowserslist('2025-03-31').trim()).toBe(
7+
`
8+
Chrome >= 107
9+
ChromeAndroid >= 107
10+
Edge >= 107
11+
Firefox >= 104
12+
FirefoxAndroid >= 104
13+
Safari >= 16
14+
iOS >= 16
15+
`.trim(),
16+
);
17+
});
18+
});
19+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { generateBrowserslist } from './generate_browserslist.mjs';
2+
3+
const [baselineDate] = process.argv.slice(2);
4+
const browserslist = generateBrowserslist(baselineDate);
5+
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+
}

0 commit comments

Comments
 (0)