@mark1russell7/site-pages 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -2
- package/build/chunks/build.js +208 -9
- package/build/chunks/build.js.map +1 -1
- package/build/chunks/kpi.d.ts +2 -0
- package/build/chunks/kpi.d.ts.map +1 -1
- package/build/chunks/kpi.js +15 -11
- package/build/chunks/kpi.js.map +1 -1
- package/build/cli.js +2 -2
- package/build/cli.js.map +1 -1
- package/build/index.d.ts +75 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +18 -2
- package/build/index.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,6 +126,37 @@ The check runs the probe on the first page, in the first scheme, at each width.
|
|
|
126
126
|
|
|
127
127
|
The check finds a browser in this order: `--browser`, `$SITE_CHECK_BROWSER`, the newest Chromium of Playwright, and then Chrome or Edge. The folder `src/check` imports only Node.js. Thus Node.js 22.18 or later can start `src/check/main.ts` with no install. The workflow `reusable-pages.yml` of the template uses it this way.
|
|
128
128
|
|
|
129
|
+
## The freshness of a measured page
|
|
130
|
+
|
|
131
|
+
A measured page shows numbers from a run. The numbers become old when the run becomes old or when an engine has a new release. A site keeps one file of data stamps. Each stamp names the page, its runs, the date of each run and the engine versions of each run. The gate reads this file:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
site-pages freshness --stamps data/stamps.json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"schema": "data-stamp/1",
|
|
140
|
+
"pages": [
|
|
141
|
+
{
|
|
142
|
+
"page": "index.html",
|
|
143
|
+
"runs": [{ "id": "101", "url": "https://github.com/owner/repo/actions/runs/101", "date": "2026-11-02T10:00:00Z", "engines": { "@scope/engine": "0.7.0" } }],
|
|
144
|
+
"inFlight": { "id": "102", "url": "https://github.com/owner/repo/actions/runs/102", "date": "2026-11-04T10:00:00Z", "engines": { "@scope/engine": "0.8.0" } }
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The gate has three rules. A fault of one rule gives the exit code 1.
|
|
151
|
+
|
|
152
|
+
1. Each page names a run, and each run has an id, a link, a date and its engine versions.
|
|
153
|
+
2. The data of a page is at most 3 days old (`MAX_DATA_AGE_DAYS`), or a run on newer engines is in flight. The stamp of that run is at most 3 days old, so an old stamp does not hold the gate open.
|
|
154
|
+
3. The engines of the newest run are at most 1 minor version behind npm at the start of the run (`MAX_MINORS_BEHIND`). A release during a long run does not fail the gate.
|
|
155
|
+
|
|
156
|
+
When npm does not answer, the gate prints the rule 3 under `Not run` and does not fail. A workflow turns the gate on with the input `data-stamps` of `reusable-pages.yml`. The gate then runs from the template commit, so it needs no install.
|
|
157
|
+
|
|
158
|
+
`renderDataStamp(stamp)` gives the line that the page shows with its data: the run, its date, its engine versions and the run in flight.
|
|
159
|
+
|
|
129
160
|
## The releases
|
|
130
161
|
|
|
131
162
|
The command merges four sources by version:
|
|
@@ -387,14 +418,15 @@ interface KpiFile {
|
|
|
387
418
|
project: string;
|
|
388
419
|
at: string; // the ISO time of the data in UTC, never the time of the build
|
|
389
420
|
commit: string; // the full commit of the build
|
|
390
|
-
measures: Array<{ id: string; value: number; unit?: string }>;
|
|
421
|
+
measures: Array<{ id: string; value: number; unit?: string; at?: string }>;
|
|
391
422
|
engines?: Array<{ name: string; version: string }>; // the Status page shows them
|
|
392
423
|
}
|
|
393
424
|
```
|
|
394
425
|
|
|
395
426
|
- Give `at` from the record of the data. The writer has no default for this field, because a build time hides old data.
|
|
427
|
+
- Give `at` on a measure when its data is from another time than the file. The Status page then shows a date for each measure.
|
|
396
428
|
- Without `commit`, the plugin takes `GITHUB_SHA`, else the head of the repository.
|
|
397
|
-
- A fault in the data stops the build, and the old site stays. A fault is a wrong form, a value that is not finite, a repeated id or a time after the check.
|
|
429
|
+
- A fault in the data stops the build, and the old site stays. A fault is a wrong form, a value that is not finite, a repeated id or a time after the check. The time rules apply to the file and to each measure.
|
|
398
430
|
- A site with no Vite build calls `writeKpi(out, input)` from the root entry, before `site-pages build`.
|
|
399
431
|
- `site-pages build` reads the file from the output folder. It adds the file to `data/status.json`, and the Status page shows the measures, the data date and the engines.
|
|
400
432
|
|
package/build/chunks/build.js
CHANGED
|
@@ -1414,7 +1414,7 @@ function listText(parts) {
|
|
|
1414
1414
|
* The dates of the pages. The pages show each date in UTC, so the build and the browser agree.
|
|
1415
1415
|
*/
|
|
1416
1416
|
/** The milliseconds of one day. */
|
|
1417
|
-
const DAY_MS = 864e5;
|
|
1417
|
+
const DAY_MS$1 = 864e5;
|
|
1418
1418
|
const MONTHS = [
|
|
1419
1419
|
"Jan",
|
|
1420
1420
|
"Feb",
|
|
@@ -1431,7 +1431,7 @@ const MONTHS = [
|
|
|
1431
1431
|
];
|
|
1432
1432
|
/** This function gives the start of the UTC day of a time. */
|
|
1433
1433
|
function utcDay(time) {
|
|
1434
|
-
return Math.floor(time / DAY_MS) * DAY_MS;
|
|
1434
|
+
return Math.floor(time / DAY_MS$1) * DAY_MS$1;
|
|
1435
1435
|
}
|
|
1436
1436
|
/** This function gives "Oct 18": the UTC day of a time or of an ISO date. */
|
|
1437
1437
|
function shortDate(date) {
|
|
@@ -1688,13 +1688,13 @@ function timelineLayout(releases, options) {
|
|
|
1688
1688
|
counts.set(day, (counts.get(day) ?? 0) + 1);
|
|
1689
1689
|
}
|
|
1690
1690
|
const raw = [];
|
|
1691
|
-
for (let day = utcDay(Date.parse(first.date)); day <= utcDay(Date.parse(last.date)); day += DAY_MS) {
|
|
1691
|
+
for (let day = utcDay(Date.parse(first.date)); day <= utcDay(Date.parse(last.date)); day += DAY_MS$1) {
|
|
1692
1692
|
const count = counts.get(day) ?? 0;
|
|
1693
1693
|
const previous = raw[raw.length - 1];
|
|
1694
|
-
if (count === 0 && previous?.empty) previous.end = day + DAY_MS;
|
|
1694
|
+
if (count === 0 && previous?.empty) previous.end = day + DAY_MS$1;
|
|
1695
1695
|
else raw.push({
|
|
1696
1696
|
start: day,
|
|
1697
|
-
end: day + DAY_MS,
|
|
1697
|
+
end: day + DAY_MS$1,
|
|
1698
1698
|
count,
|
|
1699
1699
|
empty: count === 0,
|
|
1700
1700
|
weight: count === 0 ? emptyWeight : 1
|
|
@@ -2138,14 +2138,19 @@ function openWork(data) {
|
|
|
2138
2138
|
const doneList = done.length === 0 ? `<p class="sp-done sp-small"><span aria-hidden="true">✓</span> No item is done.</p>` : `<details class="sp-done sp-c-done" data-sp-done><summary><span aria-hidden="true">✓</span> ${doneText} <span data-sp-done-label>Show the done items</span></summary><section class="sp-group sp-c-done" data-sp-group data-class="done" aria-label="The done items">${itemTable(done, data, columns)}<p class="sp-callout" data-sp-done-empty hidden>No done item matches the filters.</p></section></details>`;
|
|
2139
2139
|
return `<section class="sp-section" aria-labelledby="open-work" data-sp-board><h2 id="open-work">Open work</h2><p class="sp-small sp-muted">Each row of a table of ${link(roadmapUrl(data), `<code>${escapeHtml(file)}</code>`)} with an ID and a status that is not done. The build reads the file, so this list follows each commit.</p>` + meter(items) + (open.length > 0 ? filters : "") + list + doneList + "</section>";
|
|
2140
2140
|
}
|
|
2141
|
-
/**
|
|
2141
|
+
/**
|
|
2142
|
+
* This function renders the measures of `kpi.json`: their values, the date of the data and the engines that made it.
|
|
2143
|
+
* A measure with its own date shows that date in a column, so that the line above the table stays true.
|
|
2144
|
+
*/
|
|
2142
2145
|
function measures(data) {
|
|
2143
2146
|
const { kpi } = data;
|
|
2144
2147
|
if (kpi === void 0) return "";
|
|
2145
|
-
const
|
|
2148
|
+
const ownDates = kpi.measures.some((measure) => measure.at !== void 0);
|
|
2149
|
+
const dateCell = (at) => ownDates ? `<td><time datetime="${escapeHtml(at)}">${escapeHtml(longDate(at))}</time></td>` : "";
|
|
2150
|
+
const rows = kpi.measures.map((measure) => `<tr><th scope="row">${escapeHtml(measure.id)}</th><td>${measure.value}${measure.unit === void 0 ? "" : ` ${escapeHtml(measure.unit)}`}</td>${dateCell(measure.at ?? kpi.at)}</tr>`).join("");
|
|
2146
2151
|
const engines = kpi.engines ?? [];
|
|
2147
2152
|
const commit = link(`https://github.com/${data.source.github}/commit/${kpi.commit}`, `<code>${kpi.commit.slice(0, 7)}</code>`);
|
|
2148
|
-
return `<section class="sp-section" aria-labelledby="measures"><h2 id="measures">Measures</h2><p
|
|
2153
|
+
return `<section class="sp-section" aria-labelledby="measures"><h2 id="measures">Measures</h2><p>${ownDates ? "The data date of the file is" : "The data of these numbers is from"} <time datetime="${escapeHtml(kpi.at)}" data-sp-data-date>${escapeHtml(longDate(kpi.at))}</time>. The build of ${commit} published them.</p>` + (engines.length > 0 ? `<p>The engines of the data: ${listText(engines.map((engine) => `<code>${escapeHtml(engine.name)}</code> ${escapeHtml(engine.version)}`))}.</p>` : "") + `<div class="sp-table-box" tabindex="0" role="region" aria-label="Measures"><table class="sp-table"><thead><tr><th scope="col">Measure</th><th scope="col">Value</th>${ownDates ? "<th scope=\"col\">Data date</th>" : ""}</tr></thead><tbody>${rows}</tbody></table></div></section>`;
|
|
2149
2154
|
}
|
|
2150
2155
|
function when(date) {
|
|
2151
2156
|
const text = escapeHtml(longDate(date));
|
|
@@ -2302,6 +2307,195 @@ const TYPESCRIPT_PROBE = /\.[cm]?ts$/i;
|
|
|
2302
2307
|
function probeScript(source) {
|
|
2303
2308
|
return stripTypeScriptTypes(source, { mode: "strip" }).replace(/^export \{\s*\};?[ \t]*$/m, "");
|
|
2304
2309
|
}
|
|
2310
|
+
// src/check/freshness.ts
|
|
2311
|
+
/**
|
|
2312
|
+
* The freshness gate of a measured page. A site keeps a file of data stamps.
|
|
2313
|
+
* Each stamp names the page, its runs, their dates and their engine versions.
|
|
2314
|
+
* The gate fails on a page with no versions or no run date, and on old data with no newer run in flight.
|
|
2315
|
+
* The gate also fails on a run that started behind the engines on npm.
|
|
2316
|
+
* This file imports only Node.js and the folder `data`, so `reusable-pages.yml` starts it with no npm release of the package.
|
|
2317
|
+
*/
|
|
2318
|
+
/** The schema of a file of data stamps. */
|
|
2319
|
+
const DATA_STAMP_SCHEMA = "data-stamp/1";
|
|
2320
|
+
/** The largest age of the data of a page, in days, when no newer run is in flight. */
|
|
2321
|
+
const MAX_DATA_AGE_DAYS = 3;
|
|
2322
|
+
/** The most minor versions that an engine of a run can be behind npm at the start of the run. */
|
|
2323
|
+
const MAX_MINORS_BEHIND = 1;
|
|
2324
|
+
const DAY_MS = 864e5;
|
|
2325
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2326
|
+
/** This function gives the faults of one run of a stamp. */
|
|
2327
|
+
function runFaults(raw, at) {
|
|
2328
|
+
const run = record(raw);
|
|
2329
|
+
if (run === null) return [`${at}: a run is not an object.`];
|
|
2330
|
+
const faults = [];
|
|
2331
|
+
if (typeof run["id"] !== "string" || run["id"] === "") faults.push(`${at}: a run has no id.`);
|
|
2332
|
+
const label = `${at}, run ${String(run["id"])}`;
|
|
2333
|
+
if (typeof run["url"] !== "string" || !run["url"].startsWith("https://")) faults.push(`${label}: the run has no https link.`);
|
|
2334
|
+
if (typeof run["date"] !== "string" || !Number.isFinite(Date.parse(run["date"]))) faults.push(`${label}: the run has no date.`);
|
|
2335
|
+
const engines = record(run["engines"]);
|
|
2336
|
+
if (engines === null || Object.keys(engines).length === 0) return [...faults, `${label}: the run names no engine versions.`];
|
|
2337
|
+
for (const [name, version] of Object.entries(engines)) if (typeof version !== "string" || parseVersion(version) === null) faults.push(`${label}: the version of ${name} is not a version.`);
|
|
2338
|
+
return faults;
|
|
2339
|
+
}
|
|
2340
|
+
/**
|
|
2341
|
+
* This function reads the content of a file of data stamps. It gives each fault of the content, so a page with no versions
|
|
2342
|
+
* or no run date fails here.
|
|
2343
|
+
*/
|
|
2344
|
+
function parseDataStamps(raw) {
|
|
2345
|
+
const file = record(raw);
|
|
2346
|
+
if (file === null) return {
|
|
2347
|
+
ok: false,
|
|
2348
|
+
faults: ["The file is not an object."]
|
|
2349
|
+
};
|
|
2350
|
+
if (file["schema"] !== "data-stamp/1") return {
|
|
2351
|
+
ok: false,
|
|
2352
|
+
faults: [`The schema is not ${DATA_STAMP_SCHEMA}.`]
|
|
2353
|
+
};
|
|
2354
|
+
const pages = file["pages"];
|
|
2355
|
+
if (!Array.isArray(pages) || pages.length === 0) return {
|
|
2356
|
+
ok: false,
|
|
2357
|
+
faults: ["The file has no pages."]
|
|
2358
|
+
};
|
|
2359
|
+
const faults = [];
|
|
2360
|
+
for (const item of pages) {
|
|
2361
|
+
const page = record(item);
|
|
2362
|
+
if (page === null || typeof page["page"] !== "string" || page["page"] === "") {
|
|
2363
|
+
faults.push("A stamp has no page.");
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
const at = page["page"];
|
|
2367
|
+
const runs = page["runs"];
|
|
2368
|
+
if (!Array.isArray(runs) || runs.length === 0) faults.push(`${at}: the page names no run.`);
|
|
2369
|
+
else for (const run of runs) faults.push(...runFaults(run, at));
|
|
2370
|
+
if (page["inFlight"] !== void 0) faults.push(...runFaults(page["inFlight"], `${at}, in flight`));
|
|
2371
|
+
}
|
|
2372
|
+
return faults.length > 0 ? {
|
|
2373
|
+
ok: false,
|
|
2374
|
+
faults
|
|
2375
|
+
} : {
|
|
2376
|
+
ok: true,
|
|
2377
|
+
stamps: raw
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
/** This function gives the count of minor versions from `used` to `latest`. A newer major version counts as no limit. */
|
|
2381
|
+
function minorsBehind(used, latest) {
|
|
2382
|
+
const a = parseVersion(used);
|
|
2383
|
+
const b = parseVersion(latest);
|
|
2384
|
+
if (a === null || b === null) return Number.POSITIVE_INFINITY;
|
|
2385
|
+
if (a.major !== b.major) return b.major > a.major ? Number.POSITIVE_INFINITY : 0;
|
|
2386
|
+
return Math.max(0, b.minor - a.minor);
|
|
2387
|
+
}
|
|
2388
|
+
/** This function gives the newest stable version of a package on npm at a time, or undefined when npm has none. */
|
|
2389
|
+
function latestAt(times, name, date) {
|
|
2390
|
+
const limit = Date.parse(date);
|
|
2391
|
+
return times.filter((t) => t.name === name && Date.parse(t.date) <= limit && parseVersion(t.version)?.prerelease.length === 0).map((t) => t.version).sort(compareVersions).at(-1);
|
|
2392
|
+
}
|
|
2393
|
+
const ageInDays = (date, now) => (now - Date.parse(date)) / DAY_MS;
|
|
2394
|
+
/** This function tells if a run uses a newer version of an engine than another run. */
|
|
2395
|
+
function hasNewerEngine(run, than) {
|
|
2396
|
+
return Object.entries(run.engines).some(([name, version]) => {
|
|
2397
|
+
const old = than.engines[name];
|
|
2398
|
+
return old !== void 0 && compareVersions(version, old) > 0;
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* This function applies the rules of the gate to the stamps.
|
|
2403
|
+
*
|
|
2404
|
+
* - The data of each page is at most `maxAgeDays` old, or a run on newer engines is in flight.
|
|
2405
|
+
* - The engines of the newest run are at most `maxMinorsBehind` behind npm at the start of that run.
|
|
2406
|
+
* - A run in flight has the same age limit, so an old stamp does not hold the gate open.
|
|
2407
|
+
*/
|
|
2408
|
+
function checkFreshness(stamps, options) {
|
|
2409
|
+
const { times, maxAgeDays = 3, maxMinorsBehind = 1 } = options;
|
|
2410
|
+
const now = Date.parse(options.now);
|
|
2411
|
+
const faults = [];
|
|
2412
|
+
const notRun = [];
|
|
2413
|
+
for (const stamp of stamps.pages) {
|
|
2414
|
+
for (const run of [...stamp.runs, ...stamp.inFlight === void 0 ? [] : [stamp.inFlight]]) if (ageInDays(run.date, now) < 0) faults.push(`${stamp.page}: the run ${run.id} has a date after the check.`);
|
|
2415
|
+
const flight = stamp.inFlight;
|
|
2416
|
+
const flying = flight !== void 0 && ageInDays(flight.date, now) <= maxAgeDays;
|
|
2417
|
+
if (flight !== void 0 && !flying) faults.push(`${stamp.page}: the run ${flight.id} is in flight for more than ${maxAgeDays} days.`);
|
|
2418
|
+
for (const run of stamp.runs) {
|
|
2419
|
+
const age = ageInDays(run.date, now);
|
|
2420
|
+
if (age > maxAgeDays && !(flying && hasNewerEngine(flight, run))) faults.push(`${stamp.page}: the run ${run.id} is ${age.toFixed(1)} days old, over the limit of ${maxAgeDays}, and no run on newer engines is in flight.`);
|
|
2421
|
+
}
|
|
2422
|
+
const newest = flight ?? [...stamp.runs].sort((a, b) => Date.parse(a.date) - Date.parse(b.date)).at(-1);
|
|
2423
|
+
if (newest === void 0) continue;
|
|
2424
|
+
for (const [name, version] of Object.entries(newest.engines)) {
|
|
2425
|
+
const latest = latestAt(times, name, newest.date);
|
|
2426
|
+
if (latest === void 0) notRun.push(`${stamp.page}: npm gives no version of ${name} at the start of the run ${newest.id}.`);
|
|
2427
|
+
else if (minorsBehind(version, latest) > maxMinorsBehind) faults.push(`${stamp.page}: the run ${newest.id} used ${name} ${version}, and npm had ${latest} at its start. The limit is ${maxMinorsBehind} minor version behind.`);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
return {
|
|
2431
|
+
faults,
|
|
2432
|
+
notRun
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
/** The help of the command `site-pages freshness`. */
|
|
2436
|
+
const FRESHNESS_HELP = `Usage: site-pages freshness --stamps <file> [options]
|
|
2437
|
+
|
|
2438
|
+
Examine the data stamps of the measured pages of a site.
|
|
2439
|
+
|
|
2440
|
+
--stamps <file> The file of data stamps, schema ${DATA_STAMP_SCHEMA}. (necessary)
|
|
2441
|
+
--now <time> The ISO time of the check. The default is the time of the call.
|
|
2442
|
+
--help Show this text.
|
|
2443
|
+
|
|
2444
|
+
The exit code is 0 when the stamps pass, 1 for a fault, and 2 for an incorrect command line.
|
|
2445
|
+
`;
|
|
2446
|
+
/** This function is the command `site-pages freshness`. The `fetch` option is for a test. */
|
|
2447
|
+
async function runFreshnessCli(argv, io, fetcher) {
|
|
2448
|
+
let values;
|
|
2449
|
+
try {
|
|
2450
|
+
({values} = parseArgs({
|
|
2451
|
+
args: [...argv],
|
|
2452
|
+
allowPositionals: false,
|
|
2453
|
+
options: {
|
|
2454
|
+
stamps: { type: "string" },
|
|
2455
|
+
now: { type: "string" },
|
|
2456
|
+
help: { type: "boolean" }
|
|
2457
|
+
}
|
|
2458
|
+
}));
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
io.stderr(`site-pages freshness: ${error instanceof Error ? error.message : String(error)}\n\n${FRESHNESS_HELP}`);
|
|
2461
|
+
return 2;
|
|
2462
|
+
}
|
|
2463
|
+
if (values.help === true) {
|
|
2464
|
+
io.stdout(FRESHNESS_HELP);
|
|
2465
|
+
return 0;
|
|
2466
|
+
}
|
|
2467
|
+
if (values.stamps === void 0) {
|
|
2468
|
+
io.stderr(`site-pages freshness: --stamps is necessary.\n\n${FRESHNESS_HELP}`);
|
|
2469
|
+
return 2;
|
|
2470
|
+
}
|
|
2471
|
+
const path = isAbsolute(values.stamps) ? values.stamps : join(io.cwd, values.stamps);
|
|
2472
|
+
let raw;
|
|
2473
|
+
try {
|
|
2474
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
2475
|
+
} catch (error) {
|
|
2476
|
+
io.stderr(`site-pages freshness: the file ${values.stamps} cannot be read: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
2477
|
+
return 1;
|
|
2478
|
+
}
|
|
2479
|
+
const parsed = parseDataStamps(raw);
|
|
2480
|
+
if (!parsed.ok) {
|
|
2481
|
+
for (const fault of parsed.faults) io.stderr(`${fault}\n`);
|
|
2482
|
+
return 1;
|
|
2483
|
+
}
|
|
2484
|
+
const engines = parsed.stamps.pages.flatMap((p) => [...p.runs, ...p.inFlight === void 0 ? [] : [p.inFlight]]).flatMap((r) => Object.keys(r.engines));
|
|
2485
|
+
const npm = await npmPublishTimes({
|
|
2486
|
+
packages: [...new Set(engines)],
|
|
2487
|
+
...fetcher === void 0 ? {} : { fetch: fetcher }
|
|
2488
|
+
});
|
|
2489
|
+
const result = checkFreshness(parsed.stamps, {
|
|
2490
|
+
now: values.now ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2491
|
+
times: npm.times
|
|
2492
|
+
});
|
|
2493
|
+
for (const problem of [...npm.problems, ...result.notRun]) io.stdout(`Not run: ${problem}\n`);
|
|
2494
|
+
for (const fault of result.faults) io.stderr(`${fault}\n`);
|
|
2495
|
+
if (result.faults.length > 0) return 1;
|
|
2496
|
+
io.stdout(`The data stamps of ${parsed.stamps.pages.length} pages pass.\n`);
|
|
2497
|
+
return 0;
|
|
2498
|
+
}
|
|
2305
2499
|
// src/build.ts
|
|
2306
2500
|
/**
|
|
2307
2501
|
* The `site-pages build` command. It reads the sources. Then it writes the JSON data and the two
|
|
@@ -2402,6 +2596,11 @@ Usage: site-pages check (--out <dir> | --url <url>) [options]
|
|
|
2402
2596
|
|
|
2403
2597
|
Opens the pages of the site in a headless Chromium at phone and desktop widths. Start
|
|
2404
2598
|
"site-pages check --help" for its options.
|
|
2599
|
+
|
|
2600
|
+
Usage: site-pages freshness --stamps <file> [options]
|
|
2601
|
+
|
|
2602
|
+
Examine the data stamps of the measured pages of a site. See
|
|
2603
|
+
"site-pages freshness --help" for its options.
|
|
2405
2604
|
`;
|
|
2406
2605
|
/**
|
|
2407
2606
|
* This function reads the command line into the options of `build`.
|
|
@@ -2505,6 +2704,6 @@ async function runCli(argv, io) {
|
|
|
2505
2704
|
throw error;
|
|
2506
2705
|
}
|
|
2507
2706
|
}
|
|
2508
|
-
export {
|
|
2707
|
+
export { DEFAULT_CHANGELOGS as $, BASE_CSS as A, safeUrl as B, RELEASES_SCRIPT as C, oldestFirst as D, laneCenter as E, utcDay as F, linksOf as G, RoadmapParseError as H, escapeHtml as I, plainText as J, markdownTables as K, inlineMarkdown as L, dayRange as M, longDate as N, releaseSummary as O, shortDate as P, statusClassOf as Q, link as R, statusLead as S, renderReleasesPage as T, STATUS_CLASSES as U, OPEN_CLASSES as V, buildStatusBoard as W, roadmapItems as X, readStatusBoard as Y, splitRow as Z, classCounts as _, kindOf as _t, runCli as a, NPM_REGISTRY as at, sectionsOf as b, MAX_MINORS_BEHIND as c, GITHUB_GRAPHQL as ct, minorsBehind as d, githubReleases as dt, ReleaseDataError as et, parseDataStamps as f, firstSentence as ft, STATUS_SCRIPT as g, compareVersions as gt, probeScript as h, releaseBodyEntries as ht, parseBuildArgs as i, globFiles as it, pageShell as j, timelineLayout as k, checkFreshness as l, checkStateOf as lt, TYPESCRIPT_PROBE as m, parseChangelog as mt, UsageError as n, readChangelogs as nt, DATA_STAMP_SCHEMA as o, npmPublishTimes as ot, runFreshnessCli as p, headingAnchor as pt, parseRoadmap as q, build as r, readReleases as rt, MAX_DATA_AGE_DAYS as s, GITHUB_API as st, HELP as t, mergeReleases as tt, latestAt as u, githubOpenWork as ut, openGroups as v, parseVersion as vt, releasesLead as w, slugOf as x, renderStatusPage as y, versionOfTag as yt, listText as z };
|
|
2509
2708
|
|
|
2510
2709
|
//# sourceMappingURL=build.js.map
|