Skip to content

Commit eabb1f3

Browse files
bmeurerDevtools-frontend LUCI CQ
authored and
Devtools-frontend LUCI CQ
committed
[npm] Add watch mode to npm run build and npm start.
This adds a new script for `npm run build`, which supports a `--watch` argument, to automatically re-build whenever source files change. It also hooks this up to `npm start`, which to automatically rebuild in the background while Chrome is running. Bug: 404192426 Change-Id: I90ee3ea81312bcfff9470ab8c62d38608e4e9a71 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6367220 Auto-Submit: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Mathias Bynens <mathias@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org>
1 parent c6f9e04 commit eabb1f3

File tree

4 files changed

+103
-8
lines changed

4 files changed

+103
-8
lines changed

docs/get_the_code.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ To build, follow these steps:
2727
```bash
2828
cd devtools-frontend
2929
gclient sync
30-
gn gen out/Default
31-
autoninja -C out/Default
30+
npm run build
3231
```
3332

3433
The resulting build artifacts can be found in `out/Default/gen/front_end`.
@@ -38,6 +37,7 @@ There are two tips to have a faster development workflow:
3837
* Using watch script for faster incremental builds with CSS hot reload.
3938

4039
#### Disabling type checking
40+
4141
You can disable type checking for TypeScript by using `devtools_skip_typecheck` argument:
4242
```bash
4343
gn gen out/fast-build --args="devtools_skip_typecheck=true"
@@ -86,8 +86,10 @@ npm start
8686
```
8787

8888
to build DevTools front-end in `out/Default` (you can change this to `out/foo` by passing `--target=foo` if needed),
89-
and open Chrome for Testing (in `third_party/chrome`) with the custom DevTools front-end. It'll automatically open
90-
DevTools for every new tab, you can use
89+
and open Chrome for Testing (in `third_party/chrome`) with the custom DevTools front-end. This will also monitor the
90+
source files for changes while Chrome is running and automatically trigger a rebuild whenever source files change.
91+
92+
By default, `npm start` will automatically open DevTools for every new tab, you can use
9193

9294
```bash
9395
npm start -- --no-auto-open-devtools-for-tabs

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
},
1919
"scripts": {
2020
"bake-strings": "npm run collect-strings && vpython3 third_party/node/node.py --output third_party/i18n/bake-strings.js front_end/core/i18n/locales",
21-
"build": "autoninja -C out/Default",
22-
"build-release": "autoninja -C out/Release",
21+
"build": "vpython3 third_party/node/node.py --output scripts/run_build.mjs",
2322
"check-external-links": "vpython3 third_party/node/node.py --output scripts/check_external_links.js",
2423
"collect-strings": "vpython3 third_party/node/node.py --output third_party/i18n/collect-strings.js front_end",
2524
"components-server": "vpython3 third_party/node/node.py --output scripts/component_server/server.js",

scripts/run_build.mjs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import childProcess from 'node:child_process';
6+
import fs from 'node:fs';
7+
import path from 'node:path';
8+
import yargs from 'yargs';
9+
import {hideBin} from 'yargs/helpers';
10+
11+
const argv = yargs(hideBin(process.argv))
12+
.option('target', {
13+
alias: 't',
14+
type: 'string',
15+
default: 'Default',
16+
description: 'Specify the target build subdirectory under //out',
17+
})
18+
.option('watch', {
19+
alias: 'w',
20+
type: 'boolean',
21+
default: false,
22+
description: 'Monitor for changes and automatically rebuild',
23+
})
24+
.option('watch-only', {
25+
type: 'boolean',
26+
default: false,
27+
description: 'Skip the initial build',
28+
})
29+
.usage('npm run build -- [options]')
30+
.help('help')
31+
.version(false)
32+
.parse();
33+
34+
const {target, watch, watchOnly} = argv;
35+
const cwd = process.cwd();
36+
const {env} = process;
37+
38+
// Create and initiative the `out/<target>` directory as needed.
39+
const outDir = path.join('out', target);
40+
if (!fs.existsSync(outDir)) {
41+
const gnExe = path.join(cwd, 'third_party', 'depot_tools', 'gn');
42+
fs.mkdirSync(outDir, {recursive: true});
43+
childProcess.spawnSync(gnExe, ['gen', outDir], {
44+
cwd,
45+
env,
46+
stdio: 'inherit',
47+
});
48+
}
49+
50+
function build() {
51+
const autoninjaExe = path.join(cwd, 'third_party', 'depot_tools', 'autoninja');
52+
childProcess.spawnSync(autoninjaExe, ['-C', outDir], {
53+
cwd,
54+
env,
55+
stdio: 'inherit',
56+
});
57+
}
58+
59+
// Skip initial build if we should only watch.
60+
if (!watchOnly) {
61+
build();
62+
}
63+
64+
if (watch || watchOnly) {
65+
let timeoutId = -1;
66+
67+
function watchCallback(eventType, filename) {
68+
if (eventType !== 'change') {
69+
return;
70+
}
71+
if (['BUILD.gn'].includes(filename) || ['.css', '.js', '.ts'].includes(path.extname(filename))) {
72+
clearTimeout(timeoutId);
73+
timeoutId = setTimeout(build, 250);
74+
}
75+
}
76+
77+
const WATCHLIST = ['front_end', 'inspector_overlay', 'test'];
78+
for (const dirname of WATCHLIST) {
79+
fs.watch(
80+
dirname,
81+
{recursive: true},
82+
(eventType, filename) => watchCallback(eventType, path.join(dirname, filename)),
83+
);
84+
}
85+
}

scripts/run_start.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env vpython3
2-
# Copyright 2019 The Chromium Authors. All rights reserved.
2+
# Copyright 2025 The Chromium Authors. All rights reserved.
33
# Use of this source code is governed by a BSD-style license that can be
44
# found in the LICENSE file.
55
"""
@@ -138,4 +138,13 @@ def start(options):
138138
logging.basicConfig(
139139
level=logging.DEBUG if OPTIONS.verbose else logging.INFO)
140140
build(OPTIONS)
141-
start(OPTIONS)
141+
142+
rebuilderCmd = [
143+
devtools_paths.node_path(),
144+
path.join(devtools_paths.devtools_root_path(), 'scripts',
145+
'run_build.mjs'),
146+
'--target=%s' % OPTIONS.target, '--watch-only'
147+
]
148+
with subprocess.Popen(rebuilderCmd) as proc:
149+
start(OPTIONS)
150+
proc.kill()

0 commit comments

Comments
 (0)