Skip to content

Commit 0e3f967

Browse files
authored
Merge pull request #12 from preactjs/local-dev-mode
Configure local Preact dependencies
2 parents 1ef8dbe + d7711b1 commit 0e3f967

28 files changed

+2027
-2327
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
preact-bench.config.js
33
out
44

5+
# Added here so tools like prettier ignore this file, event though we have force
6+
# added to the git repository.
7+
pnpm-lock.yaml
8+
59

610
# Logs
711
logs

.prettierrc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
{
22
"singleQuote": false,
33
"arrowParens": "always",
4-
"trailingComma": "all",
5-
"overrides": [
6-
{
7-
"files": "pnpm-lock.yaml",
8-
"options": {
9-
"singleQuote": true
10-
}
11-
}
12-
]
4+
"trailingComma": "all"
135
}

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,55 @@ $ pnpm bench --help
5454
$ preact-bench bench apps/todo/todo.html -d preact@local,signals@local -d preact@main,signals@local -i preact-signals -n 2 -t 0
5555
$ preact-bench bench apps/todo/todo.html -d preact@local -d preact@main --trace
5656
```
57+
58+
## Benchmarking within another repository
59+
60+
This repository is intended to be included as a submodule in another repository. This allows you to run benchmarks against local changes in that repository. The `dev` script in this repository starts a benchmarking dev server that is useful when benchmarking changes in another repository.
61+
62+
```
63+
$ pnpm dev --help
64+
65+
Description
66+
Run a dev server to interactively run a benchmark while developing changes
67+
68+
Usage
69+
$ preact-bench dev [benchmark_file] [options]
70+
71+
Options
72+
--interactive Prompt for options (default false)
73+
-d, --dependency What group of dependencies (comma-delimited) and version to
74+
use for a run of the benchmark (package@version) (default latest)
75+
-i, --impl What implementation of the benchmark to run (default preact-class)
76+
-n, --sample-size Minimum number of times to run each benchmark (default 25)
77+
-h, --horizon The degrees of difference to try and resolve when auto-sampling
78+
("N%" or "Nms", comma-delimited) (default 5%)
79+
-t, --timeout Maximum number of minutes to spend auto-sampling (default 1)
80+
--trace Enable performance tracing (Chrome only) (default false)
81+
--debug Enable debug logging (default false)
82+
-b, --browser Which browser to run the benchmarks in: chrome, chrome-headless,
83+
firefox, firefox-headless, safari, edge (default chrome-headless)
84+
-p, --port What port to run the benchmark server on (default 5173)
85+
-h, --help Displays this message
86+
87+
Examples
88+
$ preact-bench dev apps/todo/todo.html -d preact@local -d preact@main -i preact-hooks
89+
$ preact-bench dev apps/todo/todo.html -d preact@local -d preact@local-pinned -i preact-hooks
90+
```
91+
92+
This command shares the same options as the `bench` command. Once you start the server you can press `b⏎` to re-build your local Preact repository (or whatever repository this is within) and re-run the configured benchmarks.
93+
94+
```text
95+
$ pnpm dev apps/many-updates/many-updates.html -i preact -d preact@local -d preact@local-pinned -n 2 -t 0
96+
97+
> @preact/[email protected] dev /Users/andre_wiggins/github/preactjs/preact-v10/benchmarks
98+
> node cli/bin/preact-bench.js dev "apps/many-updates/many-updates.html" "-i" "preact" "-d" "preact@local" "-d" "preact@local-pinned" "-n" "2" "-t" "0"
99+
100+
➜ Local: http://localhost:5173/
101+
➜ Network: use --host to expose
102+
➜ press p + enter Pin current local changes into local-pinned
103+
➜ press b + enter run Benchmarks
104+
➜ press h + enter show help
105+
106+
```
107+
108+
You can also press the `p⏎` key to build your local repos changes and copy them into the relevant `local-pinned` directory. This command is useful when you want to compare different local changes against each other.

cli/bin/preact-bench.js

Lines changed: 75 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { readdir } from "node:fs/promises";
55
import inquirer from "inquirer";
66
import sade from "sade";
77
import { analyze } from "../src/analyze.js";
8-
import { runBenchServer, runBenchmarks } from "../src/index.js";
8+
import {
9+
runBenchServer,
10+
runBenchmarks,
11+
runBenchmarksInteractively,
12+
} from "../src/index.js";
913
import { getAppConfig, getDepConfig } from "../src/config.js";
1014
import {
1115
baseTraceLogDir,
@@ -322,7 +326,12 @@ async function analyzeAction(requestedBench) {
322326
return;
323327
}
324328

325-
const benchmarkNames = await readdir(baseTraceLogDir());
329+
const benchmarkNames = [];
330+
for (let dirName of await readdir(baseTraceLogDir())) {
331+
for (let benchmarkName of await readdir(baseTraceLogDir(dirName))) {
332+
benchmarkNames.push(`${dirName}/${benchmarkName}`);
333+
}
334+
}
326335

327336
/** @type {string} */
328337
let selectedBench;
@@ -360,8 +369,56 @@ async function analyzeAction(requestedBench) {
360369

361370
const prog = sade("preact-bench").version("0.0.0");
362371

363-
prog
364-
.command("bench [benchmark_file]")
372+
/** @type {(cmd: import('sade').Sade) => import('sade').Sade} */
373+
function setupBenchmarkCLIArgs(cmd) {
374+
cmd
375+
.option("--interactive", "Prompt for options", false)
376+
.option(
377+
"-d, --dependency",
378+
"What group of dependencies (comma-delimited) and version to use for a run of the benchmark (package@version)",
379+
defaultBenchOptions.dependency,
380+
)
381+
.option(
382+
"-i, --impl",
383+
"What implementation of the benchmark to run",
384+
defaultBenchOptions.impl,
385+
)
386+
.option(
387+
"-n, --sample-size",
388+
"Minimum number of times to run each benchmark",
389+
defaultBenchOptions["sample-size"],
390+
)
391+
.option(
392+
"-h, --horizon",
393+
'The degrees of difference to try and resolve when auto-sampling ("N%" or "Nms", comma-delimited)',
394+
defaultBenchOptions.horizon,
395+
)
396+
.option(
397+
"-t, --timeout",
398+
"Maximum number of minutes to spend auto-sampling",
399+
defaultBenchOptions.timeout,
400+
)
401+
.option(
402+
"--trace",
403+
"Enable performance tracing (Chrome only)",
404+
defaultBenchOptions.trace,
405+
)
406+
.option("--debug", "Enable debug logging", defaultBenchOptions.debug)
407+
.option(
408+
"-b, --browser",
409+
"Which browser to run the benchmarks in: chrome, chrome-headless, firefox, firefox-headless, safari, edge",
410+
defaultBenchOptions.browser,
411+
)
412+
.option(
413+
"-p, --port",
414+
"What port to run the benchmark server on",
415+
defaultBenchOptions.port,
416+
);
417+
418+
return cmd;
419+
}
420+
421+
setupBenchmarkCLIArgs(prog.command("bench [benchmark_file]"))
365422
.describe(
366423
"Run the given benchmark using the specified implementation with the specified dependencies. If no benchmark file, no dependencies, or no implementations are specified, will prompt for one.",
367424
)
@@ -375,57 +432,26 @@ prog
375432
"bench apps/todo/todo.html -d preact@local,signals@local -d preact@main,signals@local -i preact-signals -n 2 -t 0",
376433
)
377434
.example("bench apps/todo/todo.html -d preact@local -d preact@main --trace")
378-
.option(
379-
"--interactive",
380-
"Prompt for options. Defaults to true of no benchmark file, dependencies, or implementations are specified",
381-
defaultBenchOptions.interactive,
382-
)
383-
.option(
384-
"-d, --dependency",
385-
"What group of dependencies (comma-delimited) and version to use for a run of the benchmark (package@version)",
386-
defaultBenchOptions.dependency,
387-
)
388-
.option(
389-
"-i, --impl",
390-
"What implementation of the benchmark to run",
391-
defaultBenchOptions.impl,
392-
)
393-
.option(
394-
"-n, --sample-size",
395-
"Minimum number of times to run each benchmark",
396-
defaultBenchOptions["sample-size"],
397-
)
398-
.option(
399-
"-h, --horizon",
400-
'The degrees of difference to try and resolve when auto-sampling ("N%" or "Nms", comma-delimited)',
401-
defaultBenchOptions.horizon,
402-
)
403-
.option(
404-
"-t, --timeout",
405-
"Maximum number of minutes to spend auto-sampling",
406-
defaultBenchOptions.timeout,
407-
)
408-
.option(
409-
"--trace",
410-
"Enable performance tracing (Chrome only)",
411-
defaultBenchOptions.trace,
435+
.action(benchAction);
436+
437+
setupBenchmarkCLIArgs(prog.command("dev [benchmark_file]"))
438+
.describe(
439+
"Run a dev server to interactively run a benchmark while developing changes",
412440
)
413-
.option("--debug", "Enable debug logging", defaultBenchOptions.debug)
414-
.option(
415-
"-b, --browser",
416-
"Which browser to run the benchmarks in: chrome, chrome-headless, firefox, firefox-headless, safari, edge",
417-
defaultBenchOptions.browser,
441+
.example(
442+
"dev apps/todo/todo.html -d preact@local -d preact@main -i preact-hooks",
418443
)
419-
.option(
420-
"-p, --port",
421-
"What port to run the benchmark server on",
422-
defaultBenchOptions.port,
444+
.example(
445+
"dev apps/todo/todo.html -d preact@local -d preact@local-pinned -i preact-hooks",
423446
)
424-
.action(benchAction);
447+
.action((benchmarkFile, args) => {
448+
const benchConfig = parseBenchmarkCLIArgs(args);
449+
runBenchmarksInteractively(benchmarkFile, benchConfig);
450+
});
425451

426452
prog
427453
.command("start")
428-
.describe("Run a dev server - useful when building benchmarks")
454+
.describe("Run a server to serve benchmark HTML files")
429455
.option(
430456
"-p, --port",
431457
"What port to run the benchmark server on",

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
"devDependencies": {
4848
"@types/d3-array": "^3.2.1",
4949
"@types/d3-scale": "^4.0.8",
50-
"@types/node": "^20.11.6",
5150
"@types/inquirer": "^9.0.7",
51+
"@types/node": "^20.11.6",
5252
"@types/selenium-webdriver": "^4.1.21"
5353
}
5454
}

cli/src/analyze.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { existsSync } from "fs";
22
import { readFile, readdir } from "fs/promises";
3-
import {
4-
baseTraceLogDir,
5-
makeBenchmarkLabel,
6-
parseBenchmarkId,
7-
} from "./utils.js";
3+
import { baseTraceLogDir, makeBenchmarkLabel } from "./utils.js";
84

95
import { summaryStats, computeDifferences } from "tachometer/lib/stats.js";
106
import {
@@ -307,21 +303,22 @@ export async function analyze(selectedBench) {
307303
/** @type {Map<string, ResultStats[]>} */
308304
const resultStatsMap = new Map();
309305
for (let benchName of benchmarkNames) {
310-
const { implId, dependencies } = parseBenchmarkId(benchName);
311306
const logDir = baseTraceLogDir(selectedBench, benchName);
312307

313308
let logFilePaths;
314309
try {
315-
logFilePaths = (await readdir(logDir)).map((fn) =>
316-
baseTraceLogDir(selectedBench, benchName, fn),
317-
);
310+
logFilePaths = (await readdir(logDir, { withFileTypes: true }))
311+
.filter((dirEntry) => dirEntry.isFile())
312+
.map((dirEntry) =>
313+
baseTraceLogDir(selectedBench, benchName, dirEntry.name),
314+
);
318315
} catch (e) {
319316
// If directory doesn't exist or we fail to read it, just skip
320317
continue;
321318
}
322319

323320
const resultStats = await getStatsFromLogs(
324-
makeBenchmarkLabel(implId, dependencies),
321+
makeBenchmarkLabel(benchName),
325322
logFilePaths,
326323
getDurationThread,
327324
isDurationLog,

cli/src/config.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import fs from "fs";
1+
import fs, { existsSync } from "fs";
22
import { readdir } from "node:fs/promises";
33
import path from "node:path";
44
import { appFilePath, depFilePath } from "./utils.js";
@@ -117,7 +117,16 @@ export async function getDepConfig(useCache = false) {
117117
const version = depDir.slice(index + 1);
118118

119119
if (!dependencies[depName]) dependencies[depName] = {};
120-
dependencies[depName][version] = depDir;
120+
121+
const scriptsPath = depFilePath(depDir, "scripts.js");
122+
if (existsSync(scriptsPath)) {
123+
dependencies[depName][version] = {
124+
path: depDir,
125+
scriptsPath,
126+
};
127+
} else {
128+
dependencies[depName][version] = depDir;
129+
}
121130
}
122131

123132
depConfigCache = dependencies;

0 commit comments

Comments
 (0)