@mark1russell7/site-pages 0.2.7 → 0.2.9

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/build/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as OpenWorkTotals, D as StatusBoardData, E as ReleaseKind, O as StatusClass, S as NpmVersionTime, T as ReleaseChange, _ as KpiEngine, a as kpiFile, b as Link, c as readKpi, d as BoardItem, f as BoardPullRequest, g as GitHubReleases, h as GitHubRelease, i as KpiInput, l as writeKpi, m as GitHubOpenWork, n as KPI_FORMAT, o as kpiProblems, p as CheckState, r as KpiError, s as kpiText, t as KPI_FILE, u as BoardIssue, v as KpiFile, w as Release, x as NpmPublishTimes, y as KpiMeasure } from "./chunks/kpi.js";
1
+ import { A as GitHubRelease, B as ReleaseChange, C as readKpi, D as BoardPullRequest, E as BoardItem, F as Link, H as StatusBoardData, I as NpmPublishTimes, L as NpmVersionTime, M as KpiEngine, N as KpiFile, O as CheckState, P as KpiMeasure, R as OpenWorkTotals, S as kpiText, T as BoardIssue, U as StatusClass, V as ReleaseKind, _ as KPI_FORMAT, a as DataStampsParse, b as kpiFile, c as MAX_DATA_AGE_DAYS, d as PageStamp, f as checkFreshness, g as KPI_FILE, h as parseDataStamps, i as DataStamps, j as GitHubReleases, k as GitHubOpenWork, l as MAX_LIMIT_DAYS_WITHOUT_RULING, m as minorsBehind, n as DataLimit, o as FreshnessCheck, p as latestAt, r as DataRun, s as FreshnessOptions, t as DATA_STAMP_SCHEMA, u as MAX_MINORS_BEHIND, v as KpiError, w as writeKpi, x as kpiProblems, y as KpiInput, z as Release } from "./chunks/freshness.js";
2
2
  /** The parts of a SemVer 2.0.0 version. */
3
3
  interface ParsedVersion {
4
4
  major: number;
@@ -513,77 +513,6 @@ export declare const TYPESCRIPT_PROBE: RegExp;
513
513
  * that is not only types (an `enum`, a `namespace`) causes an error.
514
514
  */
515
515
  export declare function probeScript(source: string): string;
516
- /** The schema of a file of data stamps. */
517
- export declare const DATA_STAMP_SCHEMA = "data-stamp/1";
518
- /** The largest age of the data of a page, in days, when no newer run is in flight. */
519
- export declare const MAX_DATA_AGE_DAYS = 3;
520
- /** The most minor versions that an engine of a run can be behind npm at the start of the run. */
521
- export declare const MAX_MINORS_BEHIND = 1;
522
- /** One run that gives the data of a page. */
523
- interface DataRun {
524
- readonly id: string;
525
- readonly url: string;
526
- /** The ISO time of the start of the run. */
527
- readonly date: string;
528
- /** The version of each engine of the run, by its npm name. */
529
- readonly engines: Readonly<Record<string, string>>;
530
- }
531
- /** The stamp of one measured page. */
532
- interface PageStamp {
533
- readonly page: string;
534
- /** Each run that gives data to the page. */
535
- readonly runs: readonly DataRun[];
536
- /** The run on newer engines that is in progress. */
537
- readonly inFlight?: DataRun;
538
- }
539
- /** The content of a file of data stamps. */
540
- interface DataStamps {
541
- readonly schema: typeof DATA_STAMP_SCHEMA;
542
- readonly pages: readonly PageStamp[];
543
- }
544
- /** The result of `parseDataStamps`. */
545
- type DataStampsParse = {
546
- readonly ok: true;
547
- readonly stamps: DataStamps;
548
- } | {
549
- readonly ok: false;
550
- readonly faults: readonly string[];
551
- };
552
- /** The options of `checkFreshness`. */
553
- interface FreshnessOptions {
554
- /** The ISO time of the check. */
555
- readonly now: string;
556
- /** The publication times of the engines on npm. */
557
- readonly times: readonly NpmVersionTime[];
558
- /** The limit of the age. The default is `MAX_DATA_AGE_DAYS`. */
559
- readonly maxAgeDays?: number;
560
- /** The limit of the minor versions. The default is `MAX_MINORS_BEHIND`. */
561
- readonly maxMinorsBehind?: number;
562
- }
563
- /** The result of `checkFreshness`. */
564
- interface FreshnessCheck {
565
- /** Each break of a rule. One fault fails the gate. */
566
- readonly faults: readonly string[];
567
- /** Each rule that the gate could not run, with the reason. */
568
- readonly notRun: readonly string[];
569
- }
570
- /**
571
- * This function reads the content of a file of data stamps. It gives each fault of the content, so a page with no versions
572
- * or no run date fails here.
573
- */
574
- export declare function parseDataStamps(raw: unknown): DataStampsParse;
575
- /** This function gives the count of minor versions from `used` to `latest`. A newer major version counts as no limit. */
576
- export declare function minorsBehind(used: string, latest: string): number;
577
- /** This function gives the newest stable version of a package on npm at a time, or undefined when npm has none. */
578
- export declare function latestAt(times: readonly NpmVersionTime[], name: string, date: string): string | undefined;
579
- /**
580
- * This function applies the rules of the gate to the stamps.
581
- *
582
- * - The data of each page is at most `maxAgeDays` old, or a run on newer engines is in flight.
583
- * - The engines of the newest run are at most `maxMinorsBehind` behind npm at the start of that run.
584
- * - A run in flight has the same age limit, so an old stamp does not hold the gate open.
585
- */
586
- export declare function checkFreshness(stamps: DataStamps, options: FreshnessOptions): FreshnessCheck;
587
516
  /** This function gives the html line of the stamp of a page. A page shows this line with its data. */
588
517
  export declare function renderDataStamp(stamp: PageStamp): string;
589
518
  /** An error in the command line. The CLI shows the help and exits with code 2. */
@@ -657,5 +586,5 @@ export declare function parseBuildArgs(argv: readonly string[], io: Pick<CliIo,
657
586
  * source is not correct, and 2 for an incorrect command line.
658
587
  */
659
588
  export declare function runCli(argv: readonly string[], io: CliIo): Promise<number>;
660
- export { type BoardIssue, type BoardItem, type BoardPullRequest, type BoardSection, type BuildOptions, type BuildResult, type ChangelogEntry, type ChangelogSection, type ChangelogSource, type CheckState, type CliIo, type DataRun, type DataStamps, type DataStampsParse, type FreshnessCheck, type FreshnessOptions, type GitHubOpenWork, type GitHubOptions, type GitHubRelease, type GitHubReleases, type ItemGroup, KPI_FILE, KPI_FORMAT, type KpiEngine, KpiError, type KpiFile, type KpiInput, type KpiMeasure, type Link, type LinkOptions, type MarkdownTable, type NpmOptions, type NpmPublishTimes, type NpmVersionTime, type OpenWorkTotals, type PageOptions, type PageParts, type PageStamp, type ParsedVersion, type ReadReleasesOptions, type ReadStatusBoardOptions, type Release, type ReleaseChange, type ReleaseKind, type ReleasePoint, type ReleaseSources, type ReleaseSummary, type StatusBoardData, type StatusBoardInput, type StatusClass, type TagDate, type TagVersion, type TimelineBand, type TimelineLayout, type TimelineMark, type TimelineOptions, kpiFile, kpiProblems, kpiText, readKpi, writeKpi };
589
+ export { type BoardIssue, type BoardItem, type BoardPullRequest, type BoardSection, type BuildOptions, type BuildResult, type ChangelogEntry, type ChangelogSection, type ChangelogSource, type CheckState, type CliIo, DATA_STAMP_SCHEMA, type DataLimit, type DataRun, type DataStamps, type DataStampsParse, type FreshnessCheck, type FreshnessOptions, type GitHubOpenWork, type GitHubOptions, type GitHubRelease, type GitHubReleases, type ItemGroup, KPI_FILE, KPI_FORMAT, type KpiEngine, KpiError, type KpiFile, type KpiInput, type KpiMeasure, type Link, type LinkOptions, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, type MarkdownTable, type NpmOptions, type NpmPublishTimes, type NpmVersionTime, type OpenWorkTotals, type PageOptions, type PageParts, type PageStamp, type ParsedVersion, type ReadReleasesOptions, type ReadStatusBoardOptions, type Release, type ReleaseChange, type ReleaseKind, type ReleasePoint, type ReleaseSources, type ReleaseSummary, type StatusBoardData, type StatusBoardInput, type StatusClass, type TagDate, type TagVersion, type TimelineBand, type TimelineLayout, type TimelineMark, type TimelineOptions, checkFreshness, kpiFile, kpiProblems, kpiText, latestAt, minorsBehind, parseDataStamps, readKpi, writeKpi };
661
590
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/data/versions.ts","../src/data/changelog.ts","../src/data/github.ts","../src/data/npm.ts","../src/data/git.ts","../src/data/glob.ts","../src/data/releases.ts","../src/data/status.ts","../src/render/html.ts","../src/render/dates.ts","../src/render/theme.ts","../src/render/timeline.ts","../src/render/releases.ts","../src/render/status.ts","../src/check/probe.ts","../src/check/freshness.ts","../src/render/data-stamp.ts","../src/build.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/data/versions.ts","../src/data/changelog.ts","../src/data/github.ts","../src/data/npm.ts","../src/data/git.ts","../src/data/glob.ts","../src/data/releases.ts","../src/data/status.ts","../src/render/html.ts","../src/render/dates.ts","../src/render/theme.ts","../src/render/timeline.ts","../src/render/releases.ts","../src/render/status.ts","../src/check/probe.ts","../src/render/data-stamp.ts","../src/build.ts"],"mappings":"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"}
package/build/index.js CHANGED
@@ -1,19 +1,4 @@
1
- import { $ as DEFAULT_CHANGELOGS, A as BASE_CSS, B as safeUrl, C as RELEASES_SCRIPT, D as oldestFirst, E as laneCenter, F as utcDay, G as linksOf, H as RoadmapParseError, I as escapeHtml, J as plainText, K as markdownTables, L as inlineMarkdown, M as dayRange, N as longDate, O as releaseSummary, P as shortDate, Q as statusClassOf, R as link, S as statusLead, T as renderReleasesPage, U as STATUS_CLASSES, V as OPEN_CLASSES, W as buildStatusBoard, X as roadmapItems, Y as readStatusBoard, Z as splitRow, _ as classCounts, _t as kindOf, a as runCli, at as NPM_REGISTRY, b as sectionsOf, c as MAX_MINORS_BEHIND, ct as GITHUB_GRAPHQL, d as minorsBehind, dt as githubReleases, et as ReleaseDataError, f as parseDataStamps, ft as firstSentence, g as STATUS_SCRIPT, gt as compareVersions, h as probeScript, ht as releaseBodyEntries, i as parseBuildArgs, it as globFiles, j as pageShell, k as timelineLayout, l as checkFreshness, lt as checkStateOf, m as TYPESCRIPT_PROBE, mt as parseChangelog, n as UsageError, nt as readChangelogs, o as DATA_STAMP_SCHEMA, ot as npmPublishTimes, pt as headingAnchor, q as parseRoadmap, r as build, rt as readReleases, s as MAX_DATA_AGE_DAYS, st as GITHUB_API, t as HELP, tt as mergeReleases, u as latestAt, ut as githubOpenWork, v as openGroups, vt as parseVersion, w as releasesLead, x as slugOf, y as renderStatusPage, yt as versionOfTag, z as listText } from "./chunks/build.js";
2
- import { a as kpiProblems, c as writeKpi, d as headingDate, f as isShallow, i as kpiFile, l as gitTagDates, n as KPI_FORMAT, o as kpiText, p as originRepository, r as KpiError, s as readKpi, t as KPI_FILE, u as headCommit } from "./chunks/kpi.js";
3
- // src/render/data-stamp.ts
4
- /** This function gives an engine and its version in the short form, without the npm scope. */
5
- function engineText(name, version) {
6
- return `${name.slice(name.indexOf("/") + 1)} ${version}`;
7
- }
8
- /** This function gives the html of one run: a link to the run, the day of its start and each engine. */
9
- function runHtml(run) {
10
- const engines = Object.entries(run.engines).map(([name, version]) => escapeHtml(engineText(name, version)));
11
- return `run ${link(run.url, escapeHtml(run.id))} of ${escapeHtml(run.date.slice(0, 10))}, on ${listText(engines)}`;
12
- }
13
- /** This function gives the html line of the stamp of a page. A page shows this line with its data. */
14
- function renderDataStamp(stamp) {
15
- return `<p class="data-stamp">${`<span>The data comes from ${listText(stamp.runs.map(runHtml))}.</span>`}${stamp.inFlight === void 0 ? "" : ` <span>A newer run is in flight: ${runHtml(stamp.inFlight)}.</span>`}</p>`;
16
- }
17
- export { BASE_CSS, DATA_STAMP_SCHEMA, DEFAULT_CHANGELOGS, GITHUB_API, GITHUB_GRAPHQL, HELP, KPI_FILE, KPI_FORMAT, KpiError, MAX_DATA_AGE_DAYS, MAX_MINORS_BEHIND, NPM_REGISTRY, OPEN_CLASSES, RELEASES_SCRIPT, ReleaseDataError, RoadmapParseError, STATUS_CLASSES, STATUS_SCRIPT, TYPESCRIPT_PROBE, UsageError, build, buildStatusBoard, checkFreshness, checkStateOf, classCounts, compareVersions, dayRange, escapeHtml, firstSentence, gitTagDates, githubOpenWork, githubReleases, globFiles, headCommit, headingAnchor, headingDate, inlineMarkdown, isShallow, kindOf, kpiFile, kpiProblems, kpiText, laneCenter, latestAt, linksOf, longDate, markdownTables, mergeReleases, minorsBehind, npmPublishTimes, oldestFirst, openGroups, originRepository, pageShell, parseBuildArgs, parseChangelog, parseDataStamps, parseRoadmap, parseVersion, plainText, probeScript, readChangelogs, readKpi, readReleases, readStatusBoard, releaseBodyEntries, releaseSummary, releasesLead, renderDataStamp, renderReleasesPage, renderStatusPage, roadmapItems, runCli, safeUrl, sectionsOf, shortDate, slugOf, splitRow, statusClassOf, statusLead, timelineLayout, utcDay, versionOfTag, writeKpi };
18
-
19
- //# sourceMappingURL=index.js.map
1
+ import { A as npmPublishTimes, C as writeKpi, D as isShallow, E as headingDate, M as kindOf, N as parseVersion, O as originRepository, P as versionOfTag, S as readKpi, T as headCommit, _ as KPI_FORMAT, a as checkFreshness, b as kpiProblems, c as parseDataStamps, d as inlineMarkdown, g as KPI_FILE, h as safeUrl, i as MAX_MINORS_BEHIND, j as compareVersions, k as NPM_REGISTRY, n as MAX_DATA_AGE_DAYS, o as latestAt, r as MAX_LIMIT_DAYS_WITHOUT_RULING, s as minorsBehind, t as DATA_STAMP_SCHEMA, u as escapeHtml, v as KpiError, w as gitTagDates, x as kpiText, y as kpiFile } from "./chunks/freshness.js";
2
+ import { $ as parseChangelog, A as STATUS_CLASSES, B as DEFAULT_CHANGELOGS, C as pageShell, D as utcDay, E as shortDate, F as plainText, G as globFiles, H as mergeReleases, I as readStatusBoard, J as checkStateOf, K as GITHUB_API, L as roadmapItems, M as linksOf, N as markdownTables, O as OPEN_CLASSES, P as parseRoadmap, Q as headingAnchor, R as splitRow, S as BASE_CSS, T as longDate, U as readChangelogs, V as ReleaseDataError, W as readReleases, X as githubReleases, Y as githubOpenWork, Z as firstSentence, _ as renderReleasesPage, a as runCli, b as releaseSummary, c as STATUS_SCRIPT, d as renderStatusPage, et as releaseBodyEntries, f as sectionsOf, g as releasesLead, h as RELEASES_SCRIPT, i as parseBuildArgs, j as buildStatusBoard, k as RoadmapParseError, l as classCounts, m as statusLead, n as UsageError, o as TYPESCRIPT_PROBE, p as slugOf, q as GITHUB_GRAPHQL, r as build, s as probeScript, t as HELP, u as openGroups, v as laneCenter, w as dayRange, x as timelineLayout, y as oldestFirst, z as statusClassOf } from "./chunks/build.js";
3
+ import { t as renderDataStamp } from "./chunks/data-stamp.js";
4
+ export { BASE_CSS, DATA_STAMP_SCHEMA, DEFAULT_CHANGELOGS, GITHUB_API, GITHUB_GRAPHQL, HELP, KPI_FILE, KPI_FORMAT, KpiError, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, NPM_REGISTRY, OPEN_CLASSES, RELEASES_SCRIPT, ReleaseDataError, RoadmapParseError, STATUS_CLASSES, STATUS_SCRIPT, TYPESCRIPT_PROBE, UsageError, build, buildStatusBoard, checkFreshness, checkStateOf, classCounts, compareVersions, dayRange, escapeHtml, firstSentence, gitTagDates, githubOpenWork, githubReleases, globFiles, headCommit, headingAnchor, headingDate, inlineMarkdown, isShallow, kindOf, kpiFile, kpiProblems, kpiText, laneCenter, latestAt, linksOf, longDate, markdownTables, mergeReleases, minorsBehind, npmPublishTimes, oldestFirst, openGroups, originRepository, pageShell, parseBuildArgs, parseChangelog, parseDataStamps, parseRoadmap, parseVersion, plainText, probeScript, readChangelogs, readKpi, readReleases, readStatusBoard, releaseBodyEntries, releaseSummary, releasesLead, renderDataStamp, renderReleasesPage, renderStatusPage, roadmapItems, runCli, safeUrl, sectionsOf, shortDate, slugOf, splitRow, statusClassOf, statusLead, timelineLayout, utcDay, versionOfTag, writeKpi };
package/build/vite.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BuildInfo } from "./chrome.js";
2
- import { i as KpiInput } from "./chunks/kpi.js";
2
+ import { i as DataStamps, y as KpiInput } from "./chunks/freshness.js";
3
3
  /** The environment variables that this module reads. */
4
4
  export type SiteEnv = Readonly<Record<string, string | undefined>>;
5
5
  /** The options of `siteShell`. */
@@ -67,6 +67,34 @@ export interface SiteKpiPlugin {
67
67
  * @param env - The environment variables. A test gives its own.
68
68
  */
69
69
  export declare function siteKpi(input: SiteKpiInput | (() => SiteKpiInput), env?: SiteEnv): SiteKpiPlugin;
70
+ /** The file that `siteStamps` adds to the root of a built site. The input `data-stamps` of `reusable-pages.yml` names it. */
71
+ export declare const STAMPS_FILE = "data-stamps.json";
72
+ /** The mark in the HTML of a measured page. `siteStamps` replaces it with the stamp line of the page. */
73
+ export declare const STAMP_MARK = "<!-- data-stamp -->";
74
+ /** The part of the context of a page that `siteStamps` uses. */
75
+ export interface SiteStampsPage {
76
+ /** The path of the page from the root of the site, with a `/` at the start. */
77
+ readonly path: string;
78
+ }
79
+ /** The Vite plugin that `siteStamps` gives. */
80
+ export interface SiteStampsPlugin {
81
+ readonly name: string;
82
+ readonly apply: "build";
83
+ readonly transformIndexHtml: {
84
+ readonly order: "pre";
85
+ handler(html: string, page: SiteStampsPage): string;
86
+ };
87
+ generateBundle(this: SiteKpiContext): void;
88
+ }
89
+ /**
90
+ * This function gives the Vite plugin of the data stamps of a site. The plugin puts the stamp line in each measured page.
91
+ * It also adds `data-stamps.json` to the root of the site. So the page and the freshness gate read one record.
92
+ *
93
+ * A fault stops the build. A fault is a bad stamp, a mark with no stamp or a stamp with no mark. A stamp of a page that the build does not hold is also a fault.
94
+ *
95
+ * @param input - The stamps, or a function that reads them at the time of the build.
96
+ */
97
+ export declare function siteStamps(input: DataStamps | (() => DataStamps)): SiteStampsPlugin;
70
98
  /**
71
99
  * This function gives the script that sets the saved theme before the first paint, so a dark page
72
100
  * does not show white for a moment. The script reads `storageKey`, as `applyTheme` writes it.
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;YAiBY,UAAU,SAAS;;iBAGd;;WAEN;;;;;WAKA;;;YAIC;;iBAGK;WACN;WACA;WACA;aAAmB,SAAS;;;;iBAItB;WACN;WACA;aAAkB,QAAQ;;WAC1B,QAAQ,SAAS;;;iBAIX;WACN;WACA;WACA;;;iBAIM;WACN;EACT,OAAO,QAAQ,uBAAuB;EACtC,sBAAsB;;;YAIZ,eAAe,KAAK;WAAiC;;;iBAGhD;EACf,SAAS;IAAQ;IAAe;IAAkB;;;;iBAInC;WACN;WACA;EACT,eAAA,MAAqB;;;;;;;;wBASP,QAAQ,OAAO,sBAAsB,eAAe,MAAK,UAAwB;;;;;;;;wBAmBjF,YAAY;;;;;wBASZ,SAAS,MAAK;;wBAMd,UAAU,kBAAkB,MAAK,SAAuB,MAAK,OAAoB;;;;;;;;;;;;wBAoBjF,UAAU,SAAS,mBAAmB"}
1
+ {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;YAmBY,UAAU,SAAS;;iBAGd;;WAEN;;;;;WAKA;;;YAIC;;iBAGK;WACN;WACA;WACA;aAAmB,SAAS;;;;iBAItB;WACN;WACA;aAAkB,QAAQ;;WAC1B,QAAQ,SAAS;;;iBAIX;WACN;WACA;WACA;;;iBAIM;WACN;EACT,OAAO,QAAQ,uBAAuB;EACtC,sBAAsB;;;YAIZ,eAAe,KAAK;WAAiC;;;iBAGhD;EACf,SAAS;IAAQ;IAAe;IAAkB;;;;iBAInC;WACN;WACA;EACT,eAAA,MAAqB;;;;;;;;wBASP,QAAQ,OAAO,sBAAsB,eAAe,MAAK,UAAwB;;qBAapF;;qBAGA;;iBAGI;;WAEN;;;iBAIM;WACN;WACA;WACA;aAA+B;IAAc,QAAQ,cAAc,MAAM;;EAClF,eAAA,MAAqB;;;;;;;;;;wBAWP,WAAW,OAAO,oBAAoB,cAAc;;;;;;;;wBA2CpD,YAAY;;;;;wBASZ,SAAS,MAAK;;wBAMd,UAAU,kBAAkB,MAAK,SAAuB,MAAK,OAAoB;;;;;;;;;;;;wBAoBjF,UAAU,SAAS,mBAAmB"}
package/build/vite.js CHANGED
@@ -1,4 +1,5 @@
1
- import { i as kpiFile, o as kpiText, t as KPI_FILE, u as headCommit } from "./chunks/kpi.js";
1
+ import { T as headCommit, c as parseDataStamps, g as KPI_FILE, x as kpiText, y as kpiFile } from "./chunks/freshness.js";
2
+ import { t as renderDataStamp } from "./chunks/data-stamp.js";
2
3
  import { readFileSync } from "node:fs";
3
4
  import { resolve } from "node:path";
4
5
  // src/vite.ts
@@ -33,6 +34,55 @@ function siteKpi(input, env = process.env) {
33
34
  }
34
35
  };
35
36
  }
37
+ /** The file that `siteStamps` adds to the root of a built site. The input `data-stamps` of `reusable-pages.yml` names it. */
38
+ const STAMPS_FILE = "data-stamps.json";
39
+ /** The mark in the HTML of a measured page. `siteStamps` replaces it with the stamp line of the page. */
40
+ const STAMP_MARK = "<!-- data-stamp -->";
41
+ /**
42
+ * This function gives the Vite plugin of the data stamps of a site. The plugin puts the stamp line in each measured page.
43
+ * It also adds `data-stamps.json` to the root of the site. So the page and the freshness gate read one record.
44
+ *
45
+ * A fault stops the build. A fault is a bad stamp, a mark with no stamp or a stamp with no mark. A stamp of a page that the build does not hold is also a fault.
46
+ *
47
+ * @param input - The stamps, or a function that reads them at the time of the build.
48
+ */
49
+ function siteStamps(input) {
50
+ let stamps;
51
+ const read = () => {
52
+ if (stamps !== void 0) return stamps;
53
+ const parsed = parseDataStamps(typeof input === "function" ? input() : input);
54
+ if (!parsed.ok) throw new Error(`The data stamps are not good:\n- ${parsed.faults.join("\n- ")}`);
55
+ stamps = parsed.stamps;
56
+ return stamps;
57
+ };
58
+ const seen = /* @__PURE__ */ new Set();
59
+ return {
60
+ name: "site-pages-stamps",
61
+ apply: "build",
62
+ transformIndexHtml: {
63
+ order: "pre",
64
+ handler(html, { path }) {
65
+ const name = path.replace(/^\//, "");
66
+ const stamp = read().pages.find((page) => page.page === name);
67
+ const marked = html.includes(STAMP_MARK);
68
+ if (stamp === void 0 && marked) throw new Error(`The page ${name} has the mark of a data stamp, and the stamps have no page ${name}.`);
69
+ if (stamp === void 0) return html;
70
+ if (!marked) throw new Error(`The stamps have the page ${name}, and the page has no mark ${STAMP_MARK}.`);
71
+ seen.add(name);
72
+ return html.replace(STAMP_MARK, renderDataStamp(stamp));
73
+ }
74
+ },
75
+ generateBundle() {
76
+ const missing = read().pages.map((page) => page.page).filter((page) => !seen.has(page));
77
+ if (missing.length > 0) throw new Error(`The stamps have pages that the build does not hold: ${missing.join(", ")}.`);
78
+ this.emitFile({
79
+ type: "asset",
80
+ fileName: STAMPS_FILE,
81
+ source: `${JSON.stringify(read(), null, 2)}\n`
82
+ });
83
+ }
84
+ };
85
+ }
36
86
  /**
37
87
  * This function gives the script that sets the saved theme before the first paint, so a dark page
38
88
  * does not show white for a moment. The script reads `storageKey`, as `applyTheme` writes it.
@@ -95,6 +145,6 @@ function siteShell(options) {
95
145
  }
96
146
  };
97
147
  }
98
- export { buildInfo, siteBase, siteKpi, siteShell, themeScript };
148
+ export { STAMPS_FILE, STAMP_MARK, buildInfo, siteBase, siteKpi, siteShell, siteStamps, themeScript };
99
149
 
100
150
  //# sourceMappingURL=vite.js.map
package/build/vite.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * The build side of the shell of a site: a Vite plugin and its parts. Import this module from\n * `@mark1russell7/site-pages/vite`. It is for `vite.config.ts`, not for a browser.\n *\n * This module does not import Vite. The types of the plugin are the small part that it uses, so the\n * package keeps no dependency. A test examines that Vite accepts the plugin.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { BuildInfo } from \"./chrome.ts\";\nimport { headCommit } from \"./data/git.ts\";\nimport { KPI_FILE, kpiFile, kpiText, type KpiInput } from \"./data/kpi.ts\";\n\nexport type { BuildInfo } from \"./chrome.ts\";\n\n/** The environment variables that this module reads. */\nexport type SiteEnv = Readonly<Record<string, string | undefined>>;\n\n/** The options of `siteShell`. */\nexport interface SiteShellOptions {\n /** The `localStorage` key of the theme. Give the same key to `setupTheme` in the page. */\n readonly themeKey: string;\n /**\n * The file with the version for `__BUILD__`, relative to the folder of the site. The default is\n * `../package.json`. The value `false` gives a build with no version.\n */\n readonly packageFile?: string | false | undefined;\n}\n\n/** The value of `build.target` in a Vite config. */\nexport type SiteTarget = string | string[] | false;\n\n/** The part of the Vite config that `siteShell` reads. */\nexport interface SiteShellConfigInput {\n readonly root?: string | undefined;\n readonly base?: string | undefined;\n readonly build?: { readonly target?: SiteTarget | undefined } | null | undefined;\n}\n\n/** The part of the Vite config that `siteShell` sets. Vite merges it into the config of the site. */\nexport interface SiteShellConfig {\n readonly base: string;\n readonly build: { readonly target: SiteTarget };\n readonly define: Readonly<Record<string, string>>;\n}\n\n/** A tag for the `head` of each HTML page. */\nexport interface SiteShellTag {\n readonly tag: \"script\";\n readonly children: string;\n readonly injectTo: \"head\";\n}\n\n/** The Vite plugin that `siteShell` gives. */\nexport interface SiteShellPlugin {\n readonly name: string;\n config(config: SiteShellConfigInput): SiteShellConfig;\n transformIndexHtml(): SiteShellTag[];\n}\n\n/** The input of `siteKpi`. Without a commit, the plugin takes `GITHUB_SHA`, else the head of the repository. */\nexport type SiteKpiInput = Omit<KpiInput, \"commit\"> & { readonly commit?: string | undefined };\n\n/** The part of the context of a Rollup hook that `siteKpi` uses. */\nexport interface SiteKpiContext {\n emitFile(file: { type: \"asset\"; fileName: string; source: string }): string;\n}\n\n/** The Vite plugin that `siteKpi` gives. */\nexport interface SiteKpiPlugin {\n readonly name: string;\n readonly apply: \"build\";\n generateBundle(this: SiteKpiContext): void;\n}\n\n/**\n * This function gives the Vite plugin that adds `kpi.json` to the root of a built site. A fault in the data stops the build.\n *\n * @param input - The data, or a function that reads it at the time of the build.\n * @param env - The environment variables. A test gives its own.\n */\nexport function siteKpi(input: SiteKpiInput | (() => SiteKpiInput), env: SiteEnv = process.env): SiteKpiPlugin {\n return {\n name: \"site-pages-kpi\",\n apply: \"build\",\n generateBundle() {\n const data = typeof input === \"function\" ? input() : input;\n const commit = data.commit ?? env[\"GITHUB_SHA\"] ?? headCommit(process.cwd()) ?? \"\";\n this.emitFile({ type: \"asset\", fileName: KPI_FILE, source: kpiText(kpiFile({ ...data, commit })) });\n },\n };\n}\n\n/**\n * This function gives the script that sets the saved theme before the first paint, so a dark page\n * does not show white for a moment. The script reads `storageKey`, as `applyTheme` writes it.\n *\n * @remarks The text goes in an inline `script` element. Thus the function escapes each `<` of the\n * key, so a key cannot close the element.\n */\nexport function themeScript(storageKey: string): string {\n const key = JSON.stringify(storageKey).replaceAll(\"<\", \"\\\\u003c\");\n return `try{const t=localStorage.getItem(${key});if(t===\"light\"||t===\"dark\")document.documentElement.dataset.theme=t}catch{}`;\n}\n\n/**\n * This function gives the `base` of Vite from `SITE_BASE`, with one `/` at the end. The Pages\n * workflow sets that variable to `/<name>`. A local build has no such variable, and gets `/`.\n */\nexport function siteBase(env: SiteEnv = process.env): string {\n const base = env[\"SITE_BASE\"];\n return base ? `${base.replace(/\\/$/, \"\")}/` : \"/\";\n}\n\n/** This function gives the data of a build. Outside CI, `GITHUB_SHA` is not set, and `sha` is `local`. */\nexport function buildInfo(version?: string, env: SiteEnv = process.env, now: Date = new Date()): BuildInfo {\n return { sha: (env[\"GITHUB_SHA\"] ?? \"local\").slice(0, 7), date: now.toISOString().slice(0, 10), version };\n}\n\n/** This function reads the field `version` of a `package.json` file. */\nfunction versionIn(file: string): string | undefined {\n return (JSON.parse(readFileSync(file, \"utf8\")) as { version?: string }).version;\n}\n\n/**\n * This function makes the Vite plugin of a site. The plugin does these four operations:\n *\n * - It sets `base` from `SITE_BASE`.\n * - It sets `build.target` to `es2022`.\n * - It defines `__BUILD__`, the value that `showBuild` shows.\n * - It adds `themeScript` to the `head` of each HTML page.\n *\n * A `base` or a `build.target` in the config of the site has priority. The other values of `build`\n * and `define` in that config stay, because Vite merges them.\n */\nexport function siteShell(options: SiteShellOptions): SiteShellPlugin {\n return {\n name: \"site-pages-shell\",\n config(config) {\n const packageFile = options.packageFile ?? \"../package.json\";\n const version = packageFile === false ? undefined : versionIn(resolve(config.root ?? \".\", packageFile));\n return {\n base: config.base ?? siteBase(),\n build: { target: config.build?.target ?? \"es2022\" },\n define: { __BUILD__: JSON.stringify(buildInfo(version)) },\n };\n },\n transformIndexHtml() {\n return [{ tag: \"script\", children: themeScript(options.themeKey), injectTo: \"head\" }];\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkFA,SAAgB,QAAQ,OAA4C,MAAe,QAAQ,KAAoB;CAC7G,OAAO;EACL,MAAM;EACN,OAAO;EACP,iBAAiB;GACf,MAAM,OAAO,OAAO,UAAU,aAAa,MAAM,IAAI;GACrD,MAAM,SAAS,KAAK,UAAU,IAAI,iBAAiB,WAAW,QAAQ,IAAI,CAAC,KAAK;GAChF,KAAK,SAAS;IAAE,MAAM;IAAS,UAAU;IAAU,QAAQ,QAAQ,QAAQ;KAAE,GAAG;KAAM;IAAO,CAAC,CAAC;GAAE,CAAC;EACpG;CACF;AACF;;;;;;;;AASA,SAAgB,YAAY,YAA4B;CAEtD,OAAO,oCADK,KAAK,UAAU,UAAU,CAAC,CAAC,WAAW,KAAK,SACV,EAAE;AACjD;;;;;AAMA,SAAgB,SAAS,MAAe,QAAQ,KAAa;CAC3D,MAAM,OAAO,IAAI;CACjB,OAAO,OAAO,GAAG,KAAK,QAAQ,OAAO,EAAE,EAAE,KAAK;AAChD;;AAGA,SAAgB,UAAU,SAAkB,MAAe,QAAQ,KAAK,sBAAY,IAAI,KAAK,GAAc;CACzG,OAAO;EAAE,MAAM,IAAI,iBAAiB,QAAA,CAAS,MAAM,GAAG,CAAC;EAAG,MAAM,IAAI,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;EAAG;CAAQ;AAC1G;;AAGA,SAAS,UAAU,MAAkC;CACnD,OAAQ,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC,CAAC,CAA0B;AAC1E;;;;;;;;;;;;AAaA,SAAgB,UAAU,SAA4C;CACpE,OAAO;EACL,MAAM;EACN,OAAO,QAAQ;GACb,MAAM,cAAc,QAAQ,eAAe;GAC3C,MAAM,UAAU,gBAAgB,QAAQ,KAAA,IAAY,UAAU,QAAQ,OAAO,QAAQ,KAAK,WAAW,CAAC;GACtG,OAAO;IACL,MAAM,OAAO,QAAQ,SAAS;IAC9B,OAAO,EAAE,QAAQ,OAAO,OAAO,UAAU,SAAS;IAClD,QAAQ,EAAE,WAAW,KAAK,UAAU,UAAU,OAAO,CAAC,EAAE;GAC1D;EACF;EACA,qBAAqB;GACnB,OAAO,CAAC;IAAE,KAAK;IAAU,UAAU,YAAY,QAAQ,QAAQ;IAAG,UAAU;GAAO,CAAC;EACtF;CACF;AACF"}
1
+ {"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * The build side of the shell of a site: a Vite plugin and its parts. Import this module from\n * `@mark1russell7/site-pages/vite`. It is for `vite.config.ts`, not for a browser.\n *\n * This module does not import Vite. The types of the plugin are the small part that it uses, so the\n * package keeps no dependency. A test examines that Vite accepts the plugin.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { BuildInfo } from \"./chrome.ts\";\nimport { parseDataStamps, type DataStamps } from \"./check/freshness.ts\";\nimport { headCommit } from \"./data/git.ts\";\nimport { KPI_FILE, kpiFile, kpiText, type KpiInput } from \"./data/kpi.ts\";\nimport { renderDataStamp } from \"./render/data-stamp.ts\";\n\nexport type { BuildInfo } from \"./chrome.ts\";\n\n/** The environment variables that this module reads. */\nexport type SiteEnv = Readonly<Record<string, string | undefined>>;\n\n/** The options of `siteShell`. */\nexport interface SiteShellOptions {\n /** The `localStorage` key of the theme. Give the same key to `setupTheme` in the page. */\n readonly themeKey: string;\n /**\n * The file with the version for `__BUILD__`, relative to the folder of the site. The default is\n * `../package.json`. The value `false` gives a build with no version.\n */\n readonly packageFile?: string | false | undefined;\n}\n\n/** The value of `build.target` in a Vite config. */\nexport type SiteTarget = string | string[] | false;\n\n/** The part of the Vite config that `siteShell` reads. */\nexport interface SiteShellConfigInput {\n readonly root?: string | undefined;\n readonly base?: string | undefined;\n readonly build?: { readonly target?: SiteTarget | undefined } | null | undefined;\n}\n\n/** The part of the Vite config that `siteShell` sets. Vite merges it into the config of the site. */\nexport interface SiteShellConfig {\n readonly base: string;\n readonly build: { readonly target: SiteTarget };\n readonly define: Readonly<Record<string, string>>;\n}\n\n/** A tag for the `head` of each HTML page. */\nexport interface SiteShellTag {\n readonly tag: \"script\";\n readonly children: string;\n readonly injectTo: \"head\";\n}\n\n/** The Vite plugin that `siteShell` gives. */\nexport interface SiteShellPlugin {\n readonly name: string;\n config(config: SiteShellConfigInput): SiteShellConfig;\n transformIndexHtml(): SiteShellTag[];\n}\n\n/** The input of `siteKpi`. Without a commit, the plugin takes `GITHUB_SHA`, else the head of the repository. */\nexport type SiteKpiInput = Omit<KpiInput, \"commit\"> & { readonly commit?: string | undefined };\n\n/** The part of the context of a Rollup hook that `siteKpi` uses. */\nexport interface SiteKpiContext {\n emitFile(file: { type: \"asset\"; fileName: string; source: string }): string;\n}\n\n/** The Vite plugin that `siteKpi` gives. */\nexport interface SiteKpiPlugin {\n readonly name: string;\n readonly apply: \"build\";\n generateBundle(this: SiteKpiContext): void;\n}\n\n/**\n * This function gives the Vite plugin that adds `kpi.json` to the root of a built site. A fault in the data stops the build.\n *\n * @param input - The data, or a function that reads it at the time of the build.\n * @param env - The environment variables. A test gives its own.\n */\nexport function siteKpi(input: SiteKpiInput | (() => SiteKpiInput), env: SiteEnv = process.env): SiteKpiPlugin {\n return {\n name: \"site-pages-kpi\",\n apply: \"build\",\n generateBundle() {\n const data = typeof input === \"function\" ? input() : input;\n const commit = data.commit ?? env[\"GITHUB_SHA\"] ?? headCommit(process.cwd()) ?? \"\";\n this.emitFile({ type: \"asset\", fileName: KPI_FILE, source: kpiText(kpiFile({ ...data, commit })) });\n },\n };\n}\n\n/** The file that `siteStamps` adds to the root of a built site. The input `data-stamps` of `reusable-pages.yml` names it. */\nexport const STAMPS_FILE = \"data-stamps.json\";\n\n/** The mark in the HTML of a measured page. `siteStamps` replaces it with the stamp line of the page. */\nexport const STAMP_MARK = \"<!-- data-stamp -->\";\n\n/** The part of the context of a page that `siteStamps` uses. */\nexport interface SiteStampsPage {\n /** The path of the page from the root of the site, with a `/` at the start. */\n readonly path: string;\n}\n\n/** The Vite plugin that `siteStamps` gives. */\nexport interface SiteStampsPlugin {\n readonly name: string;\n readonly apply: \"build\";\n readonly transformIndexHtml: { readonly order: \"pre\"; handler(html: string, page: SiteStampsPage): string };\n generateBundle(this: SiteKpiContext): void;\n}\n\n/**\n * This function gives the Vite plugin of the data stamps of a site. The plugin puts the stamp line in each measured page.\n * It also adds `data-stamps.json` to the root of the site. So the page and the freshness gate read one record.\n *\n * A fault stops the build. A fault is a bad stamp, a mark with no stamp or a stamp with no mark. A stamp of a page that the build does not hold is also a fault.\n *\n * @param input - The stamps, or a function that reads them at the time of the build.\n */\nexport function siteStamps(input: DataStamps | (() => DataStamps)): SiteStampsPlugin {\n let stamps: DataStamps | undefined;\n const read = (): DataStamps => {\n if (stamps !== undefined) return stamps;\n const parsed = parseDataStamps(typeof input === \"function\" ? input() : input);\n if (!parsed.ok) throw new Error(`The data stamps are not good:\\n- ${parsed.faults.join(\"\\n- \")}`);\n stamps = parsed.stamps;\n return stamps;\n };\n const seen = new Set<string>();\n return {\n name: \"site-pages-stamps\",\n apply: \"build\",\n transformIndexHtml: {\n order: \"pre\",\n handler(html, { path }) {\n const name = path.replace(/^\\//, \"\");\n const stamp = read().pages.find((page) => page.page === name);\n const marked = html.includes(STAMP_MARK);\n if (stamp === undefined && marked) throw new Error(`The page ${name} has the mark of a data stamp, and the stamps have no page ${name}.`);\n if (stamp === undefined) return html;\n if (!marked) throw new Error(`The stamps have the page ${name}, and the page has no mark ${STAMP_MARK}.`);\n seen.add(name);\n return html.replace(STAMP_MARK, renderDataStamp(stamp));\n },\n },\n generateBundle() {\n const missing = read()\n .pages.map((page) => page.page)\n .filter((page) => !seen.has(page));\n if (missing.length > 0) throw new Error(`The stamps have pages that the build does not hold: ${missing.join(\", \")}.`);\n this.emitFile({ type: \"asset\", fileName: STAMPS_FILE, source: `${JSON.stringify(read(), null, 2)}\\n` });\n },\n };\n}\n\n/**\n * This function gives the script that sets the saved theme before the first paint, so a dark page\n * does not show white for a moment. The script reads `storageKey`, as `applyTheme` writes it.\n *\n * @remarks The text goes in an inline `script` element. Thus the function escapes each `<` of the\n * key, so a key cannot close the element.\n */\nexport function themeScript(storageKey: string): string {\n const key = JSON.stringify(storageKey).replaceAll(\"<\", \"\\\\u003c\");\n return `try{const t=localStorage.getItem(${key});if(t===\"light\"||t===\"dark\")document.documentElement.dataset.theme=t}catch{}`;\n}\n\n/**\n * This function gives the `base` of Vite from `SITE_BASE`, with one `/` at the end. The Pages\n * workflow sets that variable to `/<name>`. A local build has no such variable, and gets `/`.\n */\nexport function siteBase(env: SiteEnv = process.env): string {\n const base = env[\"SITE_BASE\"];\n return base ? `${base.replace(/\\/$/, \"\")}/` : \"/\";\n}\n\n/** This function gives the data of a build. Outside CI, `GITHUB_SHA` is not set, and `sha` is `local`. */\nexport function buildInfo(version?: string, env: SiteEnv = process.env, now: Date = new Date()): BuildInfo {\n return { sha: (env[\"GITHUB_SHA\"] ?? \"local\").slice(0, 7), date: now.toISOString().slice(0, 10), version };\n}\n\n/** This function reads the field `version` of a `package.json` file. */\nfunction versionIn(file: string): string | undefined {\n return (JSON.parse(readFileSync(file, \"utf8\")) as { version?: string }).version;\n}\n\n/**\n * This function makes the Vite plugin of a site. The plugin does these four operations:\n *\n * - It sets `base` from `SITE_BASE`.\n * - It sets `build.target` to `es2022`.\n * - It defines `__BUILD__`, the value that `showBuild` shows.\n * - It adds `themeScript` to the `head` of each HTML page.\n *\n * A `base` or a `build.target` in the config of the site has priority. The other values of `build`\n * and `define` in that config stay, because Vite merges them.\n */\nexport function siteShell(options: SiteShellOptions): SiteShellPlugin {\n return {\n name: \"site-pages-shell\",\n config(config) {\n const packageFile = options.packageFile ?? \"../package.json\";\n const version = packageFile === false ? undefined : versionIn(resolve(config.root ?? \".\", packageFile));\n return {\n base: config.base ?? siteBase(),\n build: { target: config.build?.target ?? \"es2022\" },\n define: { __BUILD__: JSON.stringify(buildInfo(version)) },\n };\n },\n transformIndexHtml() {\n return [{ tag: \"script\", children: themeScript(options.themeKey), injectTo: \"head\" }];\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/site-pages",
4
- "version": "0.2.7",
4
+ "version": "0.2.9",
5
5
  "description": "A release timeline page and a live status page for the site of a GitHub repository: JSON data and standalone HTML, made at deploy time.",
6
6
  "keywords": [
7
7
  "github-pages",
@@ -1 +0,0 @@
1
- {"version":3,"file":"kpi.d.ts","names":[],"sources":["../../src/data/types.ts","../../src/data/kpi.ts"],"mappings":";;;;;UAMiB;EACf;EACA;;;;;;KAWU;;UAGK;;EAEf;;EAEA;;EAEA;;;UAIe;EACf;;EAEA;EACA,MAAM;;EAEN,SAAS;;EAET,OAAO;;;UAIQ;EACf;EACA;;EAEA;;EAEA;EACA;EACA;;;KAIU;EAAmB;EAAiB,UAAU;;EAAsB;EAAkB;;;UAGjF;EACf;EACA;EACA;;;UAIe;EACf,OAAO;EACP;;;KAQU;;UAGK;EACf;EACA;;EAEA;EACA,aAAa;EACb;EACA;;EAEA;;EAEA;;EAEA;EACA,OAAO;;;KAIG;;UAGK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;;UAIO;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;;;KAIU;EACN;EAAiB,cAAc;EAAoB,QAAQ;EAAc,QAAQ;;EACjF;EAAkB;;;UAGP;;EAEf;EACA;EACA;;EAEA;;;UAIe;;EAEf;EACA;;;UAIe;EACf;;EAEA;;EAEA;;EAEA;EACA,UAAU;;EAEV,UAAU;;;UAIK;;EAEf;;EAEA;;EAEA;IAAU;IAAwB;;EAClC,OAAO;EACP,cAAc;EACd,QAAQ;;EAER;IAAU;IAAoB;IAAiB,SAAS;;;EAExD,MAAM;;;cCvKK;;cAGA;;KAGD,WAAW,KAAK;;cAef,iBAAiB;WACnB;EAET,YAAY;;;;;;;;iBA4CE,YAAY,gBAAgB;;;;;;iBAgC5B,QAAQ,OAAO,UAAU,eAAe;;iBASxC,QAAQ,MAAM;;;;;;iBASd,QAAQ,cAAc,eAAe;;;;;;iBAkBrC,SAAS,aAAa,OAAO,UAAU"}
@@ -1,226 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { mkdirSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- // src/data/git.ts
5
- /**
6
- * The git source: the tags with their dates, the commit of the build and the GitHub remote. Each
7
- * function gives an empty result when git or the repository is not available.
8
- */
9
- /** This function starts git in a folder and gives its output, or null when git fails. */
10
- function git(repoDir, ...args) {
11
- try {
12
- return execFileSync("git", args, {
13
- cwd: repoDir,
14
- encoding: "utf8",
15
- stdio: [
16
- "ignore",
17
- "pipe",
18
- "ignore"
19
- ],
20
- windowsHide: true
21
- });
22
- } catch {
23
- return null;
24
- }
25
- }
26
- /** This function tells if the clone is shallow. A shallow clone has not all the tags and commits. */
27
- function isShallow(repoDir) {
28
- return git(repoDir, "rev-parse", "--is-shallow-repository")?.trim() === "true";
29
- }
30
- /** This function gives each tag of the repository with its date. An annotated tag has its own date. */
31
- function gitTagDates(repoDir) {
32
- const output = git(repoDir, "for-each-ref", "--format=%(refname:strip=2) %(creatordate:iso-strict)", "refs/tags");
33
- if (!output) return [];
34
- const tags = [];
35
- for (const line of output.split(/\r?\n/)) {
36
- const match = /^(\S+) (\S+)$/.exec(line.trim());
37
- if (match?.[1] && match[2]) tags.push({
38
- tag: match[1],
39
- date: match[2]
40
- });
41
- }
42
- return tags;
43
- }
44
- /**
45
- * This function gives the date of the oldest commit that added a version heading to a changelog,
46
- * or null. The path of the changelog starts at `repoDir`.
47
- */
48
- function headingDate(repoDir, version, file) {
49
- const dates = (git(repoDir, "log", "--format=%cI", `-S## ${version}`, "--", file) ?? "").trim().split(/\r?\n/).filter(Boolean);
50
- return dates[dates.length - 1] ?? null;
51
- }
52
- /** This function gives the full SHA of `HEAD`, or null. */
53
- function headCommit(repoDir) {
54
- const sha = git(repoDir, "rev-parse", "HEAD")?.trim();
55
- return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null;
56
- }
57
- /** This function gives "owner/name" of the GitHub remote "origin", or null. */
58
- function originRepository(repoDir) {
59
- const url = git(repoDir, "remote", "get-url", "origin")?.trim() ?? "";
60
- const match = /github\.com[:/]([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(url);
61
- return match ? `${match[1]}/${match[2]}` : null;
62
- }
63
- // src/data/kpi.ts
64
- /**
65
- * The file `kpi.json` at the root of a site, format `kpi/1`. A reader outside the site takes the measures of the project
66
- * from this file. Thus a number on a progress page comes from a record and not from a person.
67
- *
68
- * The field `at` is the time of the data and not the time of the build. Thus the writer has no default for it.
69
- * A measure can have its own `at` when its data is from another time than the rest of the file.
70
- */
71
- /** The name of the format. A new format gets a new name, so that an old reader refuses it. */
72
- const KPI_FORMAT = "kpi/1";
73
- /** The name of the file at the root of the built site. */
74
- const KPI_FILE = "kpi.json";
75
- /** The form of a project and a measure. A double hyphen separates fields in the file name of a reading. */
76
- const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
77
- /** The form of a time in UTC, with or without seconds. */
78
- const UTC_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?Z$/;
79
- /** The form of a full commit. */
80
- const COMMIT = /^[0-9a-f]{40}$/;
81
- /** The clock difference between two computers that the check accepts, in milliseconds. */
82
- const CLOCK_MARGIN = 3e5;
83
- /** An error in the data of `kpi.json`. The field `problems` has one line for each fault. */
84
- var KpiError = class extends Error {
85
- problems;
86
- constructor(problems) {
87
- super(`${KPI_FILE} is not correct: ${problems.join(" ")}`);
88
- this.name = "KpiError";
89
- this.problems = problems;
90
- }
91
- };
92
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
93
- const isText = (value) => typeof value === "string" && value.trim() !== "";
94
- /** This function gives the fault of a time of the data, or an empty list. The name tells which field it is. */
95
- function timeProblems(at, name, now) {
96
- if (typeof at !== "string" || !UTC_TIME.test(at) || Number.isNaN(Date.parse(at))) return [`${name} is not a time in UTC.`];
97
- return Date.parse(at) > now + CLOCK_MARGIN ? [`The time of the data ${at} is after the time of the check.`] : [];
98
- }
99
- /** This function gives the faults of one measure. */
100
- function measureProblems(value, index, seen, now) {
101
- if (!isRecord(value)) return [`Measure ${index} is not an object.`];
102
- const { id, value: amount, unit, at } = value;
103
- const problems = [];
104
- if (typeof id !== "string" || !SLUG.test(id)) problems.push(`Measure ${index} has no id of lowercase words with single hyphens.`);
105
- else if (seen.has(id)) problems.push(`The id ${id} occurs two times.`);
106
- else seen.add(id);
107
- if (typeof amount !== "number" || !Number.isFinite(amount)) problems.push(`Measure ${index} has no finite number as its value.`);
108
- if (unit !== void 0 && !isText(unit)) problems.push(`Measure ${index} has a unit that is not a text.`);
109
- if (at !== void 0) problems.push(...timeProblems(at, `The field at of measure ${index}`, now));
110
- return problems;
111
- }
112
- /** This function gives the faults of the list of engines. */
113
- function engineProblems(value) {
114
- if (value === void 0) return [];
115
- if (!Array.isArray(value)) return ["The field engines is not a list."];
116
- return value.flatMap((engine, index) => isRecord(engine) && isText(engine["name"]) && isText(engine["version"]) ? [] : [`Engine ${index} has no name and version.`]);
117
- }
118
- /**
119
- * This function gives the faults of a parsed `kpi.json`, or an empty list.
120
- *
121
- * @param value - The parsed file.
122
- * @param now - The time of the check, in milliseconds. A time of the data after this time is a fault.
123
- */
124
- function kpiProblems(value, now = Date.now()) {
125
- if (!isRecord(value)) return ["The file is not an object."];
126
- const problems = [];
127
- if (value["format"] !== "kpi/1") problems.push(`The format is not ${KPI_FORMAT}.`);
128
- const { project, at, commit, measures } = value;
129
- if (typeof project !== "string" || !SLUG.test(project)) problems.push("The project is not lowercase words with single hyphens.");
130
- problems.push(...timeProblems(at, "The field at", now));
131
- if (typeof commit !== "string" || !COMMIT.test(commit)) problems.push("The commit is not a full commit.");
132
- if (!Array.isArray(measures) || measures.length === 0) problems.push("The file has no measures.");
133
- else {
134
- const seen = /* @__PURE__ */ new Set();
135
- problems.push(...measures.flatMap((measure, index) => measureProblems(measure, index, seen, now)));
136
- }
137
- problems.push(...engineProblems(value["engines"]));
138
- return problems;
139
- }
140
- /** This function gives a copy of a measure with only its known fields. */
141
- function cleanMeasure({ id, value, unit, at }) {
142
- return {
143
- id,
144
- value,
145
- ...unit === void 0 ? {} : { unit },
146
- ...at === void 0 ? {} : { at }
147
- };
148
- }
149
- /** This function gives a copy of an engine with only its known fields. */
150
- function cleanEngine({ name, version }) {
151
- return {
152
- name,
153
- version
154
- };
155
- }
156
- /**
157
- * This function gives the checked content of `kpi.json`. The file has only its known fields, in a fixed order.
158
- *
159
- * @throws KpiError when the data is not correct
160
- */
161
- function kpiFile(input, now) {
162
- const problems = kpiProblems({
163
- format: KPI_FORMAT,
164
- ...input
165
- }, now);
166
- if (problems.length > 0) throw new KpiError(problems);
167
- const { project, at, commit, measures, engines } = input;
168
- const file = {
169
- format: KPI_FORMAT,
170
- project,
171
- at,
172
- commit,
173
- measures: measures.map(cleanMeasure)
174
- };
175
- return engines === void 0 ? file : {
176
- ...file,
177
- engines: engines.map(cleanEngine)
178
- };
179
- }
180
- /** This function gives the text of `kpi.json`. */
181
- function kpiText(file) {
182
- return `${JSON.stringify(file, null, 2)}\n`;
183
- }
184
- /**
185
- * This function reads the text of a `kpi.json`.
186
- *
187
- * @throws KpiError when the text is not correct
188
- */
189
- function readKpi(text, now) {
190
- let value;
191
- try {
192
- value = JSON.parse(text);
193
- } catch (error) {
194
- throw new KpiError([`The file is not JSON: ${error instanceof Error ? error.message : String(error)}`]);
195
- }
196
- const problems = kpiProblems(value, now);
197
- if (problems.length > 0) throw new KpiError(problems);
198
- const { project, at, commit, measures, engines } = value;
199
- return kpiFile(engines === void 0 ? {
200
- project,
201
- at,
202
- commit,
203
- measures
204
- } : {
205
- project,
206
- at,
207
- commit,
208
- measures,
209
- engines
210
- }, now);
211
- }
212
- /**
213
- * This function writes `kpi.json` into the built output folder of a site, and gives the path of the file.
214
- *
215
- * @throws KpiError when the data is not correct
216
- */
217
- function writeKpi(out, input, now) {
218
- const text = kpiText(kpiFile(input, now));
219
- mkdirSync(out, { recursive: true });
220
- const file = join(out, KPI_FILE);
221
- writeFileSync(file, text);
222
- return file;
223
- }
224
- export { kpiProblems as a, writeKpi as c, headingDate as d, isShallow as f, kpiFile as i, gitTagDates as l, KPI_FORMAT as n, kpiText as o, originRepository as p, KpiError as r, readKpi as s, KPI_FILE as t, headCommit as u };
225
-
226
- //# sourceMappingURL=kpi.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"kpi.js","names":[],"sources":["../../src/data/git.ts","../../src/data/kpi.ts"],"sourcesContent":["/**\n * The git source: the tags with their dates, the commit of the build and the GitHub remote. Each\n * function gives an empty result when git or the repository is not available.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n/** A git tag and the ISO date of its creation. */\nexport interface TagDate {\n tag: string;\n date: string;\n}\n\n/** This function starts git in a folder and gives its output, or null when git fails. */\nexport function git(repoDir: string, ...args: string[]): string | null {\n try {\n return execFileSync(\"git\", args, { cwd: repoDir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], windowsHide: true });\n } catch {\n return null;\n }\n}\n\n/** This function tells if the clone is shallow. A shallow clone has not all the tags and commits. */\nexport function isShallow(repoDir: string): boolean {\n return git(repoDir, \"rev-parse\", \"--is-shallow-repository\")?.trim() === \"true\";\n}\n\n/** This function gives each tag of the repository with its date. An annotated tag has its own date. */\nexport function gitTagDates(repoDir: string): TagDate[] {\n const output = git(repoDir, \"for-each-ref\", \"--format=%(refname:strip=2) %(creatordate:iso-strict)\", \"refs/tags\");\n if (!output) return [];\n const tags: TagDate[] = [];\n for (const line of output.split(/\\r?\\n/)) {\n const match = /^(\\S+) (\\S+)$/.exec(line.trim());\n if (match?.[1] && match[2]) tags.push({ tag: match[1], date: match[2] });\n }\n return tags;\n}\n\n/**\n * This function gives the date of the oldest commit that added a version heading to a changelog,\n * or null. The path of the changelog starts at `repoDir`.\n */\nexport function headingDate(repoDir: string, version: string, file: string): string | null {\n const output = git(repoDir, \"log\", \"--format=%cI\", `-S## ${version}`, \"--\", file);\n const dates = (output ?? \"\").trim().split(/\\r?\\n/).filter(Boolean);\n return dates[dates.length - 1] ?? null;\n}\n\n/** This function gives the full SHA of `HEAD`, or null. */\nexport function headCommit(repoDir: string): string | null {\n const sha = git(repoDir, \"rev-parse\", \"HEAD\")?.trim();\n return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null;\n}\n\n/** This function gives \"owner/name\" of the GitHub remote \"origin\", or null. */\nexport function originRepository(repoDir: string): string | null {\n const url = git(repoDir, \"remote\", \"get-url\", \"origin\")?.trim() ?? \"\";\n const match = /github\\.com[:/]([^/]+)\\/(.+?)(?:\\.git)?\\/?$/.exec(url);\n return match ? `${match[1]}/${match[2]}` : null;\n}\n","/**\n * The file `kpi.json` at the root of a site, format `kpi/1`. A reader outside the site takes the measures of the project\n * from this file. Thus a number on a progress page comes from a record and not from a person.\n *\n * The field `at` is the time of the data and not the time of the build. Thus the writer has no default for it.\n * A measure can have its own `at` when its data is from another time than the rest of the file.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { KpiEngine, KpiFile, KpiMeasure } from \"./types.ts\";\n\n/** The name of the format. A new format gets a new name, so that an old reader refuses it. */\nexport const KPI_FORMAT = \"kpi/1\";\n\n/** The name of the file at the root of the built site. */\nexport const KPI_FILE = \"kpi.json\";\n\n/** The data of `kpi.json`, without the name of the format. */\nexport type KpiInput = Omit<KpiFile, \"format\">;\n\n/** The form of a project and a measure. A double hyphen separates fields in the file name of a reading. */\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/** The form of a time in UTC, with or without seconds. */\nconst UTC_TIME = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?Z$/;\n\n/** The form of a full commit. */\nconst COMMIT = /^[0-9a-f]{40}$/;\n\n/** The clock difference between two computers that the check accepts, in milliseconds. */\nconst CLOCK_MARGIN = 5 * 60 * 1000;\n\n/** An error in the data of `kpi.json`. The field `problems` has one line for each fault. */\nexport class KpiError extends Error {\n readonly problems: readonly string[];\n\n constructor(problems: readonly string[]) {\n super(`${KPI_FILE} is not correct: ${problems.join(\" \")}`);\n this.name = \"KpiError\";\n this.problems = problems;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isText = (value: unknown): value is string => typeof value === \"string\" && value.trim() !== \"\";\n\n/** This function gives the fault of a time of the data, or an empty list. The name tells which field it is. */\nfunction timeProblems(at: unknown, name: string, now: number): string[] {\n if (typeof at !== \"string\" || !UTC_TIME.test(at) || Number.isNaN(Date.parse(at))) return [`${name} is not a time in UTC.`];\n return Date.parse(at) > now + CLOCK_MARGIN ? [`The time of the data ${at} is after the time of the check.`] : [];\n}\n\n/** This function gives the faults of one measure. */\nfunction measureProblems(value: unknown, index: number, seen: Set<string>, now: number): string[] {\n if (!isRecord(value)) return [`Measure ${index} is not an object.`];\n const { id, value: amount, unit, at } = value;\n const problems: string[] = [];\n if (typeof id !== \"string\" || !SLUG.test(id)) problems.push(`Measure ${index} has no id of lowercase words with single hyphens.`);\n else if (seen.has(id)) problems.push(`The id ${id} occurs two times.`);\n else seen.add(id);\n if (typeof amount !== \"number\" || !Number.isFinite(amount)) problems.push(`Measure ${index} has no finite number as its value.`);\n if (unit !== undefined && !isText(unit)) problems.push(`Measure ${index} has a unit that is not a text.`);\n if (at !== undefined) problems.push(...timeProblems(at, `The field at of measure ${index}`, now));\n return problems;\n}\n\n/** This function gives the faults of the list of engines. */\nfunction engineProblems(value: unknown): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) return [\"The field engines is not a list.\"];\n return value.flatMap((engine: unknown, index) => (isRecord(engine) && isText(engine[\"name\"]) && isText(engine[\"version\"]) ? [] : [`Engine ${index} has no name and version.`]));\n}\n\n/**\n * This function gives the faults of a parsed `kpi.json`, or an empty list.\n *\n * @param value - The parsed file.\n * @param now - The time of the check, in milliseconds. A time of the data after this time is a fault.\n */\nexport function kpiProblems(value: unknown, now: number = Date.now()): string[] {\n if (!isRecord(value)) return [\"The file is not an object.\"];\n const problems: string[] = [];\n if (value[\"format\"] !== KPI_FORMAT) problems.push(`The format is not ${KPI_FORMAT}.`);\n const { project, at, commit, measures } = value;\n if (typeof project !== \"string\" || !SLUG.test(project)) problems.push(\"The project is not lowercase words with single hyphens.\");\n problems.push(...timeProblems(at, \"The field at\", now));\n if (typeof commit !== \"string\" || !COMMIT.test(commit)) problems.push(\"The commit is not a full commit.\");\n if (!Array.isArray(measures) || measures.length === 0) problems.push(\"The file has no measures.\");\n else {\n const seen = new Set<string>();\n problems.push(...measures.flatMap((measure: unknown, index) => measureProblems(measure, index, seen, now)));\n }\n problems.push(...engineProblems(value[\"engines\"]));\n return problems;\n}\n\n/** This function gives a copy of a measure with only its known fields. */\nfunction cleanMeasure({ id, value, unit, at }: KpiMeasure): KpiMeasure {\n return { id, value, ...(unit === undefined ? {} : { unit }), ...(at === undefined ? {} : { at }) };\n}\n\n/** This function gives a copy of an engine with only its known fields. */\nfunction cleanEngine({ name, version }: KpiEngine): KpiEngine {\n return { name, version };\n}\n\n/**\n * This function gives the checked content of `kpi.json`. The file has only its known fields, in a fixed order.\n *\n * @throws KpiError when the data is not correct\n */\nexport function kpiFile(input: KpiInput, now?: number): KpiFile {\n const problems = kpiProblems({ format: KPI_FORMAT, ...input }, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = input;\n const file: KpiFile = { format: KPI_FORMAT, project, at, commit, measures: measures.map(cleanMeasure) };\n return engines === undefined ? file : { ...file, engines: engines.map(cleanEngine) };\n}\n\n/** This function gives the text of `kpi.json`. */\nexport function kpiText(file: KpiFile): string {\n return `${JSON.stringify(file, null, 2)}\\n`;\n}\n\n/**\n * This function reads the text of a `kpi.json`.\n *\n * @throws KpiError when the text is not correct\n */\nexport function readKpi(text: string, now?: number): KpiFile {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch (error) {\n throw new KpiError([`The file is not JSON: ${error instanceof Error ? error.message : String(error)}`]);\n }\n const problems = kpiProblems(value, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = value as KpiFile;\n return kpiFile(engines === undefined ? { project, at, commit, measures } : { project, at, commit, measures, engines }, now);\n}\n\n/**\n * This function writes `kpi.json` into the built output folder of a site, and gives the path of the file.\n *\n * @throws KpiError when the data is not correct\n */\nexport function writeKpi(out: string, input: KpiInput, now?: number): string {\n const text = kpiText(kpiFile(input, now));\n mkdirSync(out, { recursive: true });\n const file = join(out, KPI_FILE);\n writeFileSync(file, text);\n return file;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/render/data-stamp.ts"],"sourcesContent":["/**\n * The line that a measured page shows for its data: the run, its date and its engine versions, and a run in flight.\n */\n\nimport type { DataRun, PageStamp } from \"../check/freshness.ts\";\nimport { escapeHtml, link, listText } from \"./html.ts\";\n\n/** This function gives an engine and its version in the short form, without the npm scope. */\nfunction engineText(name: string, version: string): string {\n return `${name.slice(name.indexOf(\"/\") + 1)} ${version}`;\n}\n\n/** This function gives the html of one run: a link to the run, the day of its start and each engine. */\nfunction runHtml(run: DataRun): string {\n const engines = Object.entries(run.engines).map(([name, version]) => escapeHtml(engineText(name, version)));\n return `run ${link(run.url, escapeHtml(run.id))} of ${escapeHtml(run.date.slice(0, 10))}, on ${listText(engines)}`;\n}\n\n/** This function gives the html line of the stamp of a page. A page shows this line with its data. */\nexport function renderDataStamp(stamp: PageStamp): string {\n const data = `<span>The data comes from ${listText(stamp.runs.map(runHtml))}.</span>`;\n const flight = stamp.inFlight === undefined ? \"\" : ` <span>A newer run is in flight: ${runHtml(stamp.inFlight)}.</span>`;\n return `<p class=\"data-stamp\">${data}${flight}</p>`;\n}\n"],"mappings":";;;;AAQA,SAAS,WAAW,MAAc,SAAyB;CACzD,OAAO,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC,EAAE,GAAG;AACjD;;AAGA,SAAS,QAAQ,KAAsB;CACrC,MAAM,UAAU,OAAO,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,aAAa,WAAW,WAAW,MAAM,OAAO,CAAC,CAAC;CAC1G,OAAO,OAAO,KAAK,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,MAAM,WAAW,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,OAAO,SAAS,OAAO;AACjH;;AAGA,SAAgB,gBAAgB,OAA0B;CAGxD,OAAO,yBAAyB,6BAFU,SAAS,MAAM,KAAK,IAAI,OAAO,CAAC,EAAE,YAC7D,MAAM,aAAa,KAAA,IAAY,KAAK,oCAAoC,QAAQ,MAAM,QAAQ,EAAE,UACjE;AAChD"}