Skip to content

Commit c4f7d3c

Browse files
committed
feat(bazel): update http_server to work with rules_js
Updates the `http_server` to work with `rules_js`.
1 parent 75b0023 commit c4f7d3c

12 files changed

+1670
-94
lines changed

.bazelrc

+5-6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ build --symlink_prefix=dist/
3636
# Write testing errors to log on test failure
3737
test --test_output=errors
3838

39+
# Do not build runfile forests by default. If an execution strategy relies on runfile
40+
# forests, the forest is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
41+
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df. This
42+
# also helps with: https://github.com/bazelbuild/bazel/issues/4327#issuecomment-922106293.
43+
build --nobuild_runfile_links
3944

4045
# Turn on --incompatible_strict_action_env which was on by default
4146
# in Bazel 0.21.0 but turned off again in 0.22.0. Follow
@@ -47,12 +52,6 @@ build --incompatible_strict_action_env
4752
run --incompatible_strict_action_env
4853
test --incompatible_strict_action_env
4954

50-
# Do not build runfile forests by default. If an execution strategy relies on runfile
51-
# forests, the forest is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
52-
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df. This
53-
# also helps with: https://github.com/bazelbuild/bazel/issues/4327#issuecomment-922106293.
54-
build --nobuild_runfile_links
55-
5655
################################
5756
# Remote Execution Setup #
5857
################################

bazel/http-server/BUILD.bazel

+17-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
2-
load("//bazel:defaults.bzl", "ts_library")
1+
load("@aspect_rules_js//js:defs.bzl", "js_binary")
2+
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
33

44
package(default_visibility = ["//visibility:public"])
55

@@ -11,33 +11,27 @@ filegroup(
1111
srcs = glob(["*"]),
1212
)
1313

14-
ts_library(
14+
ts_project(
1515
name = "server_lib",
1616
srcs = [
17-
"ibazel.ts",
18-
"main.ts",
19-
"server.ts",
17+
"ibazel.mts",
18+
"main.mts",
19+
"server.mts",
2020
],
21-
# A tsconfig needs to be specified as otherwise `ts_library` will look for the config
22-
# in `//:package.json` and this breaks when the BUILD file is copied to `@npm//`.
23-
tsconfig = "//:tsconfig.json",
21+
tsconfig = "//bazel:tsconfig",
2422
deps = [
25-
"@npm//@bazel/runfiles",
26-
"@npm//@types/browser-sync",
27-
"@npm//@types/node",
28-
"@npm//@types/send",
29-
"@npm//@types/yargs",
30-
"@npm//browser-sync",
31-
"@npm//send",
32-
"@npm//yargs",
23+
"//bazel:node_modules/@types/browser-sync",
24+
"//bazel:node_modules/@types/node",
25+
"//bazel:node_modules/@types/send",
26+
"//bazel:node_modules/@types/yargs",
27+
"//bazel:node_modules/browser-sync",
28+
"//bazel:node_modules/send",
29+
"//bazel:node_modules/yargs",
3330
],
3431
)
3532

36-
nodejs_binary(
33+
js_binary(
3734
name = "server_bin",
38-
data = [
39-
":server_lib",
40-
],
41-
entry_point = ":main.ts",
42-
templated_args = ["--nobazel_run_linker"],
35+
data = [":server_lib"],
36+
entry_point = ":main.mjs",
4337
)

bazel/http-server/ibazel.ts renamed to bazel/http-server/ibazel.mts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {createInterface} from 'readline';
10-
import {HttpServer} from './server';
10+
import {HttpServer} from './server.mjs';
1111

1212
// ibazel will write this string after a successful build.
1313
const ibazelNotifySuccessMessage = 'IBAZEL_BUILD_COMPLETED SUCCESS';

bazel/http-server/index.bzl

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("@aspect_rules_js//js:providers.bzl", "JsInfo")
12
load("@build_bazel_rules_nodejs//:providers.bzl", "JSNamedModuleInfo")
23

34
def _get_workspace_name(ctx):
@@ -29,6 +30,8 @@ def _http_server_rule_impl(ctx):
2930
for dep in ctx.attr.deps:
3031
if JSNamedModuleInfo in dep:
3132
transitive_depsets.append(dep[JSNamedModuleInfo].sources)
33+
if JsInfo in dep:
34+
transitive_depsets.append(dep[JsInfo].transitive_sources)
3235
elif DefaultInfo in dep:
3336
transitive_depsets.append(dep[DefaultInfo].files)
3437

@@ -136,6 +139,7 @@ http_server_rule = rule(
136139
"_bash_runfile_helpers": attr.label(default = Label("@bazel_tools//tools/bash/runfiles")),
137140
"_server_bin": attr.label(
138141
default = Label("//bazel/http-server:server_bin"),
142+
cfg = "exec",
139143
),
140144
"_launcher_template": attr.label(
141145
allow_single_file = True,

bazel/http-server/launcher_template.sh

+2-13
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,8 @@ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/
1414
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
1515
# --- end runfiles.bash initialization v2 ---
1616

17-
# If we do not run the server as part of a test, we always enforce runfile
18-
# resolution when invoking the server NodeJS binary. This is necessary as
19-
# runfile trees are disabled as part of this repository. The server NodeJS
20-
# binary would not find a relative runfile tree directory and error out.
21-
if [[ -z "${TEST_SRCDIR:-""}" ]]; then
22-
export RUNFILES_MANIFEST_ONLY="1"
23-
fi
24-
25-
# Resolve the path of the server binary. Note: usually we either need to
26-
# resolve the "nodejs_binary" executable with different file extensions on
27-
# windows, but since we already run this launcher as part of a "sh_binary", we
28-
# can safely execute another shell script from the current shell.
29-
serverBin=$(rlocation "devinfra/bazel/http-server/server_bin.sh")
17+
# Resolve the path of the server binary.
18+
serverBin=$(rlocation "devinfra/bazel/http-server/server_bin_/server_bin")
3019

3120
# Start the server with the given arguments. The arguments will be
3221
# substituted based on the rule attributes.

bazel/http-server/main.ts renamed to bazel/http-server/main.mts

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import yargs from 'yargs';
1010
import assert from 'node:assert';
1111

12-
import {HttpServer} from './server';
13-
import {setupBazelWatcherSupport} from './ibazel';
12+
import {HttpServer} from './server.mjs';
13+
import {setupBazelWatcherSupport} from './ibazel.mjs';
1414

1515
const {
1616
rootPaths,

bazel/http-server/server.ts renamed to bazel/http-server/server.mts

+34-25
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {readFileSync} from 'fs';
10-
import {runfiles} from '@bazel/runfiles';
9+
import fs from 'node:fs';
1110
import browserSync from 'browser-sync';
12-
import http from 'http';
13-
import path from 'path';
11+
import http from 'node:http';
12+
import path from 'node:path';
1413
import send from 'send';
14+
import assert from 'node:assert';
15+
16+
// The current working directory is the runfiles root.
17+
const runfilesRoot = process.env['RUNFILES_DIR']!;
18+
assert(runfilesRoot, 'Expected `RUNFILES_DIR` to be set.');
1519

1620
/**
1721
* Http Server implementation that uses browser-sync internally. This server
@@ -26,22 +30,7 @@ export class HttpServer {
2630
server = browserSync.create();
2731

2832
/** Options of the browser-sync server. */
29-
options: browserSync.Options = {
30-
open: false,
31-
online: false,
32-
port: this.port,
33-
notify: false,
34-
ghostMode: false,
35-
server: {
36-
directory: false,
37-
middleware: [
38-
(req, res) => {
39-
this._corsMiddleware(req, res);
40-
this._bazelMiddleware(req, res);
41-
},
42-
],
43-
},
44-
};
33+
options: browserSync.Options;
4534

4635
constructor(
4736
readonly port: number,
@@ -51,6 +40,23 @@ export class HttpServer {
5140
private _environmentVariables: string[] = [],
5241
private _relaxCors: boolean = false,
5342
) {
43+
this.options = {
44+
open: false,
45+
online: false,
46+
port: this.port,
47+
notify: false,
48+
ghostMode: false,
49+
server: {
50+
directory: false,
51+
middleware: [
52+
(req, res) => {
53+
this._corsMiddleware(req, res);
54+
this._bazelMiddleware(req, res);
55+
},
56+
],
57+
},
58+
};
59+
5460
if (enableUi === false) {
5561
this.options.ui = false;
5662
}
@@ -131,12 +137,15 @@ export class HttpServer {
131137
return new URL(reqURL, `http://_`).pathname;
132138
}
133139

134-
/** Resolves a given URL from the runfiles using the corresponding manifest path. */
140+
/** Resolves a given URL from the runfiles using a computed manifest path. */
135141
private _resolveUrlFromRunfiles(url: string): string | null {
136142
for (let rootPath of this._rootPaths) {
137-
try {
138-
return runfiles.resolve(path.posix.join(rootPath, getManifestPath(url)));
139-
} catch {}
143+
const fragment = path.posix.join(rootPath, getManifestPath(url));
144+
const diskPath = path.join(runfilesRoot, fragment);
145+
146+
if (fs.existsSync(diskPath)) {
147+
return diskPath;
148+
}
140149
}
141150
return null;
142151
}
@@ -155,7 +164,7 @@ export class HttpServer {
155164
}
156165

157166
const variables: Record<string, string | undefined> = {};
158-
const content = readFileSync(indexPath, 'utf8');
167+
const content = fs.readFileSync(indexPath, 'utf8');
159168

160169
// Populate variables object that will be inlined.
161170
this._environmentVariables.forEach((name) => (variables[name] = process.env[name]));

bazel/http-server/test/BUILD.bazel

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
1+
load("@aspect_rules_js//js:defs.bzl", "js_test")
2+
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
23
load("//bazel/http-server:index.bzl", "http_server")
3-
load("//tools:defaults.bzl", "ts_library")
44

5-
ts_library(
5+
ts_project(
66
name = "app_lib",
77
testonly = True,
88
srcs = ["main.ts"],
9+
tsconfig = "tsconfig.app.json",
910
)
1011

1112
http_server(
@@ -16,23 +17,21 @@ http_server(
1617
deps = [":app_lib"],
1718
)
1819

19-
ts_library(
20+
ts_project(
2021
name = "test_lib",
2122
testonly = True,
2223
srcs = ["server-test.ts"],
23-
# TODO(devversion): Remove this when `ts_library` supports `.mts` extension.
24-
devmode_module = "commonjs",
24+
tsconfig = "//bazel:tsconfig",
2525
deps = [
26-
"@npm//@bazel/runfiles",
27-
"@npm//@types/node",
28-
"@npm//@types/selenium-webdriver",
29-
"@npm//@types/wait-on",
30-
"@npm//selenium-webdriver",
31-
"@npm//wait-on",
26+
"//bazel:node_modules/@types/node",
27+
"//bazel:node_modules/@types/selenium-webdriver",
28+
"//bazel:node_modules/@types/wait-on",
29+
"//bazel:node_modules/selenium-webdriver",
30+
"//bazel:node_modules/wait-on",
3231
],
3332
)
3433

35-
nodejs_test(
34+
js_test(
3635
name = "test",
3736
# Pass the chromium and chromedriver binaries as arguments to the test.
3837
# These variables are made available by the toolchain alias.
@@ -45,6 +44,6 @@ nodejs_test(
4544
":test_lib",
4645
"//bazel/browsers/chromium",
4746
],
48-
entry_point = ":server-test.ts",
47+
entry_point = ":server-test.js",
4948
toolchains = ["//bazel/browsers/chromium:toolchain_alias"],
5049
)

bazel/http-server/test/server-test.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {runfiles} from '@bazel/runfiles';
109
import {Builder, By, WebDriver} from 'selenium-webdriver';
1110
import {Options as ChromeOptions, ServiceBuilder} from 'selenium-webdriver/chrome';
1211

1312
import waitOn from 'wait-on';
14-
import * as childProcess from 'child_process';
13+
import childProcess from 'node:child_process';
14+
import path from 'node:path';
1515

1616
/**
1717
* Test script that will start the test http server binary in a background process.
@@ -23,9 +23,9 @@ async function runTest() {
2323
const [chromiumRootpath, chromedriverRootpath] = process.argv.slice(2);
2424

2525
// Resolve chromium, chromedriver and the server binary to disk paths.
26-
const chromiumPath = runfiles.resolveWorkspaceRelative(chromiumRootpath);
27-
const chromedriverPath = runfiles.resolveWorkspaceRelative(chromedriverRootpath);
28-
const serverBinPath = runfiles.resolveWorkspaceRelative('bazel/http-server/test/server');
26+
const chromiumPath = path.resolve(chromiumRootpath);
27+
const chromedriverPath = path.resolve(chromedriverRootpath);
28+
const serverBinPath = path.resolve('bazel/http-server/test/server');
2929

3030
const serverPort = 1234;
3131
const serverHost = `127.0.0.1:${serverPort}`;
@@ -36,6 +36,12 @@ async function runTest() {
3636
stdio: 'inherit',
3737
});
3838

39+
console.error('Spawning');
40+
41+
serverProcess.on('error', (err) => {
42+
console.error(err);
43+
});
44+
3945
// Ensure the process gets killed, if the test terminates early.
4046
process.on('exit', () => serverProcess.kill());
4147

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"lib": ["dom", "es2022"],
5+
"module": "ES2022"
6+
}
7+
}

bazel/package.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@
22
"name": "@devinfra/bazel",
33
"dependencies": {
44
"@microsoft/api-extractor": "7.52.3",
5+
"@types/browser-sync": "2.29.0",
56
"@types/node": "22.14.0",
7+
"@types/selenium-webdriver": "^4.1.28",
8+
"@types/send": "0.17.4",
9+
"@types/wait-on": "^5.3.4",
10+
"@types/yargs": "17.0.33",
11+
"browser-sync": "3.0.4",
612
"piscina": "^4.9.2",
13+
"selenium-webdriver": "^4.31.0",
14+
"send": "1.2.0",
15+
"true-case-path": "2.2.1",
716
"typescript": "5.8.2",
8-
"true-case-path": "2.2.1"
17+
"wait-on": "^8.0.3",
18+
"yargs": "17.7.2"
919
},
1020
"pnpm": {
1121
"onlyBuiltDependencies": []

0 commit comments

Comments
 (0)