explico 0.13.1 → 0.15.0
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/CHANGELOG.md +990 -1
- package/README.md +200 -30
- package/dist/compiler/astro.d.ts +15 -2
- package/dist/compiler/astro.js +40 -11
- package/dist/compiler/cli-main.d.ts +100 -0
- package/dist/compiler/cli-main.js +433 -0
- package/dist/compiler/cli-reference.d.ts +50 -0
- package/dist/compiler/cli-reference.js +213 -0
- package/dist/compiler/cli-verbs.d.ts +45 -0
- package/dist/compiler/cli-verbs.js +224 -0
- package/dist/compiler/cli-verify.d.ts +51 -0
- package/dist/compiler/cli-verify.js +151 -0
- package/dist/compiler/cli.d.ts +408 -24
- package/dist/compiler/cli.js +1328 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/content-release.d.ts +49 -0
- package/dist/compiler/content-release.js +63 -0
- package/dist/compiler/content-schemas.d.ts +2 -2
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +105 -1
- package/dist/compiler/decks.d.ts +161 -0
- package/dist/compiler/decks.js +235 -0
- package/dist/compiler/exports/combined-html.d.ts +5 -1
- package/dist/compiler/exports/deck-fit.d.ts +55 -0
- package/dist/compiler/exports/deck-fit.js +90 -0
- package/dist/compiler/exports/export-chapters.d.ts +37 -6
- package/dist/compiler/exports/export-chapters.js +64 -5
- package/dist/compiler/exports/export-pages.d.ts +30 -3
- package/dist/compiler/exports/export-pages.js +24 -1
- package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/dist/compiler/exports/latex/cache.d.ts +78 -0
- package/dist/compiler/exports/latex/cache.js +129 -0
- package/dist/compiler/exports/latex/capture.d.ts +10 -1
- package/dist/compiler/exports/latex/capture.js +3 -1
- package/dist/compiler/exports/latex/compile.d.ts +13 -2
- package/dist/compiler/exports/latex/compile.js +12 -1
- package/dist/compiler/exports/latex/deck.d.ts +92 -0
- package/dist/compiler/exports/latex/deck.js +349 -0
- package/dist/compiler/exports/latex/document.d.ts +51 -6
- package/dist/compiler/exports/latex/document.js +71 -12
- package/dist/compiler/exports/latex/explico-common.sty +198 -0
- package/dist/compiler/exports/latex/explico.cls +13 -183
- package/dist/compiler/exports/latex/serialize.d.ts +60 -4
- package/dist/compiler/exports/latex/serialize.js +4 -2
- package/dist/compiler/exports/latex-export.d.ts +24 -2
- package/dist/compiler/exports/latex-export.js +107 -26
- package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
- package/dist/compiler/exports/markdown/serialize.js +85 -8
- package/dist/compiler/exports/markdown-export.d.ts +6 -1
- package/dist/compiler/exports/markdown-export.js +50 -11
- package/dist/compiler/exports/standalone-html.d.ts +10 -5
- package/dist/compiler/exports/standalone-html.js +3 -3
- package/dist/compiler/exports/static-tree.d.ts +38 -1
- package/dist/compiler/exports/static-tree.js +120 -4
- package/dist/compiler/knowledge-version.d.ts +26 -2
- package/dist/compiler/knowledge-version.js +27 -17
- package/dist/compiler/lesson-derivation.d.ts +143 -6
- package/dist/compiler/lesson-derivation.js +145 -13
- package/dist/compiler/manifest.d.ts +133 -4
- package/dist/compiler/manifest.js +364 -58
- package/dist/compiler/page-versions.d.ts +184 -0
- package/dist/compiler/page-versions.js +217 -0
- package/dist/compiler/render-diagnostics.d.ts +5 -1
- package/dist/compiler/resolution-report.d.ts +24 -6
- package/dist/compiler/resolution-report.js +22 -4
- package/dist/compiler/validate-content.d.ts +7 -0
- package/dist/compiler/validate-content.js +160 -0
- package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
- package/dist/curriculum/competency-graph.d.ts +54 -0
- package/dist/curriculum/competency-graph.js +41 -0
- package/dist/reference/base-library.d.mts +8 -0
- package/dist/reference/base-library.mjs +19 -0
- package/dist/reference/consistency.d.ts +8 -4
- package/dist/reference/consistency.js +82 -26
- package/dist/reference/deck-remark.d.mts +123 -0
- package/dist/reference/deck-remark.mjs +600 -0
- package/dist/reference/deck-source.d.ts +107 -0
- package/dist/reference/deck-source.js +140 -0
- package/dist/reference/decks-validate.d.ts +133 -0
- package/dist/reference/decks-validate.js +359 -0
- package/dist/reference/derived-entries.d.ts +5 -1
- package/dist/reference/diagram-fit.d.ts +6 -2
- package/dist/reference/diagram-fit.js +5 -1
- package/dist/reference/figure-symbols.d.ts +23 -6
- package/dist/reference/figure-symbols.js +51 -15
- package/dist/reference/float-name.d.ts +43 -0
- package/dist/reference/float-name.js +58 -0
- package/dist/reference/folds-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-indices.js +5 -4
- package/dist/reference/gate-math.d.ts +29 -2
- package/dist/reference/gate-math.js +99 -18
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/lab-katex-validate.d.ts +10 -6
- package/dist/reference/lab-katex-validate.js +17 -9
- package/dist/reference/lab-math-scope.js +29 -4
- package/dist/reference/math-glyphs.d.mts +25 -0
- package/dist/reference/math-glyphs.mjs +189 -25
- package/dist/reference/page-notation.d.ts +22 -3
- package/dist/reference/page-notation.js +32 -10
- package/dist/reference/references.d.ts +14 -1
- package/dist/reference/references.js +13 -0
- package/dist/reference/registry-scope.d.mts +33 -0
- package/dist/reference/registry-scope.mjs +34 -0
- package/dist/reference/registry.js +29 -10
- package/dist/reference/rehype-deck.d.mts +6 -0
- package/dist/reference/rehype-deck.mjs +286 -0
- package/dist/reference/rehype-heading-anchors.d.mts +20 -0
- package/dist/reference/rehype-heading-anchors.mjs +48 -6
- package/dist/reference/remark-notation.mjs +249 -40
- package/dist/reference/source-lines.d.mts +12 -0
- package/dist/reference/source-lines.mjs +65 -0
- package/dist/reference/types.d.ts +28 -0
- package/dist/rules/catalog.d.ts +21 -4
- package/dist/rules/catalog.js +237 -2
- package/dist/rules/course-rules.d.ts +5 -1
- package/dist/rules/course-rules.js +17 -3
- package/dist/rules/course-rules.schema.json +12 -0
- package/dist/rules/in-effect.d.ts +13 -3
- package/dist/rules/in-effect.js +34 -10
- package/dist/rules/rules.schema.json +280 -1
- package/dist/rules/settings.d.ts +1 -1
- package/dist/session/PreferenceStore.d.ts +6 -0
- package/dist/session/PreferenceStore.js +18 -1
- package/dist/session/deck-memory.d.ts +116 -0
- package/dist/session/deck-memory.js +143 -0
- package/dist/session/reading-memory.d.ts +96 -0
- package/dist/session/reading-memory.js +133 -0
- package/docs/VERSIONING.md +21 -18
- package/docs/authoring/README.md +31 -9
- package/docs/authoring/agents.md +72 -18
- package/docs/authoring/cli-reference.json +2172 -0
- package/docs/authoring/cli.md +887 -0
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +612 -0
- package/docs/authoring/migrating.md +1195 -16
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +56 -3
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +40 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
- package/docs/course-agent/skills/release-content/SKILL.md +26 -0
- package/docs/diagram-scaling.md +148 -0
- package/docs/privacy.md +59 -0
- package/package.json +8 -2
- package/src/compiler/astro.ts +60 -21
- package/src/compiler/cli-main.ts +557 -0
- package/src/compiler/cli-reference.ts +285 -0
- package/src/compiler/cli-verbs.ts +270 -0
- package/src/compiler/cli-verify.ts +241 -0
- package/src/compiler/cli.ts +2119 -170
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/content-release.ts +91 -0
- package/src/compiler/course-config.ts +267 -0
- package/src/compiler/decks.ts +455 -0
- package/src/compiler/exports/deck-fit.ts +127 -0
- package/src/compiler/exports/export-chapters.ts +105 -6
- package/src/compiler/exports/export-pages.ts +46 -2
- package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/src/compiler/exports/latex/cache.ts +183 -0
- package/src/compiler/exports/latex/capture.ts +5 -2
- package/src/compiler/exports/latex/compile.ts +18 -0
- package/src/compiler/exports/latex/deck.ts +419 -0
- package/src/compiler/exports/latex/document.ts +117 -21
- package/src/compiler/exports/latex/explico-common.sty +198 -0
- package/src/compiler/exports/latex/explico.cls +13 -183
- package/src/compiler/exports/latex/serialize.ts +25 -21
- package/src/compiler/exports/latex/texlive-packages.txt +6 -2
- package/src/compiler/exports/latex-export.ts +187 -45
- package/src/compiler/exports/markdown/serialize.ts +118 -9
- package/src/compiler/exports/markdown-export.ts +56 -11
- package/src/compiler/exports/standalone-html.ts +8 -7
- package/src/compiler/exports/static-tree.ts +175 -3
- package/src/compiler/knowledge-version.ts +56 -21
- package/src/compiler/lesson-derivation.ts +282 -16
- package/src/compiler/manifest.ts +568 -63
- package/src/compiler/page-versions.ts +401 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +215 -0
- package/src/components/CourseContents.astro +124 -6
- package/src/components/CurriculumMap.astro +49 -1
- package/src/components/FloatIndex.astro +1 -1
- package/src/components/PartOverview.astro +51 -5
- package/src/components/assessments/AssessmentSet.astro +41 -15
- package/src/components/block-heading.ts +15 -6
- package/src/components/card-carry.ts +7 -4
- package/src/components/card-manager.ts +14 -2
- package/src/components/card-memory.ts +13 -9
- package/src/components/deck/DeckEnhancer.astro +13 -0
- package/src/components/deck/deck-context.ts +104 -0
- package/src/components/deck/deck-controller.ts +1287 -0
- package/src/components/deck/deck-map.ts +91 -0
- package/src/components/deck-lane.ts +163 -0
- package/src/components/diagram/Diagram.astro +131 -86
- package/src/components/disclosure/persisted-disclosure.ts +9 -1
- package/src/components/figure/Figure.astro +7 -2
- package/src/components/figure/FloatCaption.astro +5 -3
- package/src/components/fold/Fold.astro +2 -2
- package/src/components/notation/NotationFormula.astro +81 -11
- package/src/components/notation/NotationGlossary.astro +132 -1
- package/src/components/notation/NotationLayer.astro +15 -7
- package/src/components/starlight/AboutPanel.astro +186 -3
- package/src/components/starlight/CollapsibleSections.astro +24 -3
- package/src/components/starlight/DiagramEnhancer.astro +5 -1
- package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
- package/src/components/starlight/LayoutHeader.astro +17 -0
- package/src/components/starlight/LayoutPageTitle.astro +110 -0
- package/src/components/starlight/LayoutSidebar.astro +807 -21
- package/src/components/starlight/LayoutSocialIcons.astro +62 -6
- package/src/components/starlight/LessonExportActions.astro +27 -9
- package/src/components/starlight/LessonFooter.astro +59 -0
- package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
- package/src/components/starlight/TextSizeControl.astro +24 -13
- package/src/components/starlight/page-marks.ts +139 -0
- package/src/components/starlight/section-summary.ts +1 -1
- package/src/components/starlight/sidebar-editions.ts +245 -0
- package/src/components/table/Table.astro +15 -7
- package/src/content-config.ts +87 -1
- package/src/curriculum/competency-graph.ts +95 -0
- package/src/reference/base-library.mjs +19 -0
- package/src/reference/consistency.ts +137 -39
- package/src/reference/deck-remark.mjs +600 -0
- package/src/reference/deck-source.ts +273 -0
- package/src/reference/decks-validate.ts +604 -0
- package/src/reference/diagram-fit.ts +18 -4
- package/src/reference/diagram-layout.ts +3 -1
- package/src/reference/figure-symbols.ts +108 -31
- package/src/reference/folds-validate.ts +1 -1
- package/src/reference/gate-indices.ts +5 -3
- package/src/reference/gate-math.ts +169 -25
- package/src/reference/lab-katex-validate.ts +30 -15
- package/src/reference/lab-math-scope.ts +34 -3
- package/src/reference/math-glyphs.mjs +189 -25
- package/src/reference/page-notation.ts +69 -15
- package/src/reference/references.ts +28 -0
- package/src/reference/registry-scope.mjs +34 -0
- package/src/reference/registry.ts +41 -11
- package/src/reference/rehype-deck.mjs +286 -0
- package/src/reference/rehype-heading-anchors.mjs +48 -6
- package/src/reference/remark-notation.mjs +249 -40
- package/src/reference/source-lines.mjs +65 -0
- package/src/reference/types.ts +28 -0
- package/src/rules/catalog.ts +274 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +58 -11
- package/src/session/PreferenceStore.ts +23 -0
- package/src/session/deck-memory.ts +263 -0
- package/src/session/reading-memory.ts +243 -0
- package/src/styles/deck.css +1150 -0
- package/src/styles/global.css +958 -27
- package/src/styles/zones.css +277 -0
- /package/src/{components → reference}/float-name.ts +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `content verify` — the one gate before a course's work is handed back:
|
|
3
|
+
* `validate:content` → `content check --changed` → `astro check` → the
|
|
4
|
+
* course's tests → `astro build`, and with `--e2e` its browser tests. Each
|
|
5
|
+
* step runs as its own process (the course's own scripts, so whatever they
|
|
6
|
+
* do is what runs), its whole output goes to a log under
|
|
7
|
+
* `node_modules/.cache/explico/verify/`, and the run prints one line per step
|
|
8
|
+
* and the tail of each failed step's log.
|
|
9
|
+
*
|
|
10
|
+
* A pass records the tree it left (git HEAD, every change against it, every
|
|
11
|
+
* untracked file's content; ignored files are no input) and whether it ran
|
|
12
|
+
* the browser tests. A rerun on the same tree that asks for no more answers
|
|
13
|
+
* from the record without running anything, so "run it again to be sure"
|
|
14
|
+
* costs nothing. This repository's `scripts/agent/verify` is the same gate
|
|
15
|
+
* for the engine and the playground.
|
|
16
|
+
*/
|
|
17
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
19
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { join, relative } from 'node:path';
|
|
21
|
+
|
|
22
|
+
export type VerifyStepName = 'validate:content' | 'check --changed' | 'astro check' | 'tests' | 'astro build' | 'e2e';
|
|
23
|
+
|
|
24
|
+
export interface VerifyStep {
|
|
25
|
+
readonly name: VerifyStepName;
|
|
26
|
+
readonly status: 'pass' | 'fail' | 'skipped';
|
|
27
|
+
/** The command it ran, as it would be typed; absent for a skipped step. */
|
|
28
|
+
readonly command?: string;
|
|
29
|
+
/** Why it was skipped, or why it could not start. */
|
|
30
|
+
readonly reason?: string;
|
|
31
|
+
readonly seconds?: number;
|
|
32
|
+
/** Course-relative path of its whole output. */
|
|
33
|
+
readonly log?: string;
|
|
34
|
+
/** The last lines of the output of a failed step. */
|
|
35
|
+
readonly tail?: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface VerifyReport {
|
|
39
|
+
readonly ok: boolean;
|
|
40
|
+
/** True when the tree is unchanged since a pass that covered as much: nothing ran, `steps` are that pass's. */
|
|
41
|
+
readonly cached: boolean;
|
|
42
|
+
/** When the recorded pass ran, for a cached answer (ISO 8601). */
|
|
43
|
+
readonly since?: string;
|
|
44
|
+
/** The hash of the tree the run left, or `null` outside a git repository (then nothing is recorded). */
|
|
45
|
+
readonly tree: string | null;
|
|
46
|
+
/** Whether the browser tests were part of the run. */
|
|
47
|
+
readonly e2e: boolean;
|
|
48
|
+
readonly steps: readonly VerifyStep[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface VerifyOptions {
|
|
52
|
+
readonly e2e?: boolean;
|
|
53
|
+
readonly force?: boolean;
|
|
54
|
+
/** The course root; the working directory by default. */
|
|
55
|
+
readonly cwd?: string;
|
|
56
|
+
/** Called as each step starts and ends, for a progress line. */
|
|
57
|
+
readonly onStep?: (event: { readonly phase: 'start'; readonly name: VerifyStepName } | { readonly phase: 'end'; readonly step: VerifyStep }) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const VERIFY_DIR = join('node_modules', '.cache', 'explico', 'verify');
|
|
61
|
+
const RECORD = 'last-pass.json';
|
|
62
|
+
const TAIL_LINES = 20;
|
|
63
|
+
|
|
64
|
+
interface PassRecord {
|
|
65
|
+
readonly tree: string;
|
|
66
|
+
readonly e2e: boolean;
|
|
67
|
+
readonly when: string;
|
|
68
|
+
readonly steps: readonly VerifyStep[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The working tree as the gate sees it, in one hash; `null` outside git. */
|
|
72
|
+
export function verifyTreeHash(cwd: string): string | null {
|
|
73
|
+
try {
|
|
74
|
+
const git = (args: string[]) =>
|
|
75
|
+
execFileSync('git', args, { cwd, maxBuffer: 1 << 30, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
76
|
+
const hash = createHash('sha1');
|
|
77
|
+
hash.update(git(['rev-parse', 'HEAD']));
|
|
78
|
+
hash.update(git(['diff', 'HEAD', '--binary']));
|
|
79
|
+
const untracked = git(['ls-files', '--others', '--exclude-standard', '-z']).toString('utf8').split('\0').filter(Boolean).sort();
|
|
80
|
+
for (const file of untracked) {
|
|
81
|
+
hash.update(`\0${file}\0`);
|
|
82
|
+
if (existsSync(join(cwd, file))) hash.update(readFileSync(join(cwd, file)));
|
|
83
|
+
}
|
|
84
|
+
return hash.digest('hex');
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** How to run a package script with the manager that started this process (`npm_execpath`), pnpm otherwise. */
|
|
91
|
+
function scriptCommand(script: string, args: readonly string[]): { file: string; argv: string[]; display: string } {
|
|
92
|
+
const execpath = process.env.npm_execpath ?? '';
|
|
93
|
+
const manager = /pnpm/.test(execpath) ? 'pnpm' : /yarn/.test(execpath) ? 'yarn' : /npm/.test(execpath) ? 'npm' : 'pnpm';
|
|
94
|
+
// npm hands a script its arguments only after `--`; pnpm and yarn pass them as they are.
|
|
95
|
+
const tail = manager === 'npm' && args.length > 0 ? ['run', script, '--', ...args] : ['run', script, ...args];
|
|
96
|
+
const display = `${manager} ${tail.join(' ')}`;
|
|
97
|
+
if (execpath === '' || !execpath.includes(manager)) return { file: manager, argv: tail, display };
|
|
98
|
+
return /\.[cm]?js$/.test(execpath)
|
|
99
|
+
? { file: process.execPath, argv: [execpath, ...tail], display }
|
|
100
|
+
: { file: execpath, argv: tail, display };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function binCommand(cwd: string, bin: string, args: readonly string[]): { file: string; argv: string[]; display: string } {
|
|
104
|
+
return { file: join(cwd, 'node_modules', '.bin', process.platform === 'win32' ? `${bin}.cmd` : bin), argv: [...args], display: [bin, ...args].join(' ') };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function slug(name: VerifyStepName): string {
|
|
108
|
+
return name.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function tailOf(file: string): string[] {
|
|
112
|
+
const lines = readFileSync(file, 'utf8').split('\n').map((line) => line.trimEnd()).filter((line) => line !== '');
|
|
113
|
+
return lines.slice(-TAIL_LINES);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Run every step, record a pass, and say what happened; never throws for a failing step. */
|
|
117
|
+
export function runVerify(options: VerifyOptions = {}): VerifyReport {
|
|
118
|
+
const cwd = options.cwd ?? process.cwd();
|
|
119
|
+
const e2e = options.e2e ?? false;
|
|
120
|
+
const dir = join(cwd, VERIFY_DIR);
|
|
121
|
+
const recordFile = join(dir, RECORD);
|
|
122
|
+
|
|
123
|
+
if (!options.force && existsSync(recordFile)) {
|
|
124
|
+
try {
|
|
125
|
+
const record = JSON.parse(readFileSync(recordFile, 'utf8')) as PassRecord;
|
|
126
|
+
const tree = verifyTreeHash(cwd);
|
|
127
|
+
if (tree !== null && record.tree === tree && (record.e2e || !e2e)) {
|
|
128
|
+
return { ok: true, cached: true, since: record.when, tree, e2e: record.e2e, steps: record.steps };
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
// an unreadable record is no record
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
mkdirSync(dir, { recursive: true });
|
|
136
|
+
const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')) as { scripts?: Record<string, string> };
|
|
137
|
+
const scripts = pkg.scripts ?? {};
|
|
138
|
+
const has = (path: string) => existsSync(join(cwd, path));
|
|
139
|
+
const playwrightConfig = ['playwright.config.ts', 'playwright.config.mts', 'playwright.config.js', 'playwright.config.mjs'].some(has);
|
|
140
|
+
|
|
141
|
+
type Plan = { name: VerifyStepName; command?: ReturnType<typeof scriptCommand>; reason?: string; fail?: boolean };
|
|
142
|
+
const plans: Plan[] = [
|
|
143
|
+
scripts['validate:content'] === undefined
|
|
144
|
+
? { name: 'validate:content', reason: 'package.json has no validate:content script', fail: true }
|
|
145
|
+
: { name: 'validate:content', command: scriptCommand('validate:content', []) },
|
|
146
|
+
scripts.content === undefined
|
|
147
|
+
? { name: 'check --changed', reason: 'package.json has no content script', fail: true }
|
|
148
|
+
: { name: 'check --changed', command: scriptCommand('content', ['check', '--changed']) },
|
|
149
|
+
// Without @astrojs/check, `astro check` stops to ask whether to install it.
|
|
150
|
+
has(join('node_modules', '@astrojs', 'check', 'package.json'))
|
|
151
|
+
? { name: 'astro check', command: binCommand(cwd, 'astro', ['check']) }
|
|
152
|
+
: { name: 'astro check', reason: '@astrojs/check is not installed (pnpm add -D @astrojs/check typescript)' },
|
|
153
|
+
scripts.test === undefined
|
|
154
|
+
? { name: 'tests', reason: 'package.json has no test script' }
|
|
155
|
+
: { name: 'tests', command: scriptCommand('test', []) },
|
|
156
|
+
{ name: 'astro build', command: binCommand(cwd, 'astro', ['build']) },
|
|
157
|
+
];
|
|
158
|
+
if (e2e) {
|
|
159
|
+
plans.push(
|
|
160
|
+
scripts['test:e2e'] !== undefined
|
|
161
|
+
? { name: 'e2e', command: scriptCommand('test:e2e', []) }
|
|
162
|
+
: playwrightConfig
|
|
163
|
+
? { name: 'e2e', command: binCommand(cwd, 'playwright', ['test']) }
|
|
164
|
+
: { name: 'e2e', reason: 'no test:e2e script and no playwright.config' },
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const steps: VerifyStep[] = [];
|
|
169
|
+
for (const plan of plans) {
|
|
170
|
+
options.onStep?.({ phase: 'start', name: plan.name });
|
|
171
|
+
let step: VerifyStep;
|
|
172
|
+
if (plan.command === undefined) {
|
|
173
|
+
step = { name: plan.name, status: plan.fail ? 'fail' : 'skipped', reason: plan.reason! };
|
|
174
|
+
} else {
|
|
175
|
+
const log = join(dir, `${slug(plan.name)}.log`);
|
|
176
|
+
const fd = openSync(log, 'w');
|
|
177
|
+
const started = Date.now();
|
|
178
|
+
let result;
|
|
179
|
+
try {
|
|
180
|
+
result = spawnSync(plan.command.file, plan.command.argv, {
|
|
181
|
+
cwd,
|
|
182
|
+
stdio: ['ignore', fd, fd],
|
|
183
|
+
shell: process.platform === 'win32',
|
|
184
|
+
env: process.env,
|
|
185
|
+
});
|
|
186
|
+
} finally {
|
|
187
|
+
closeSync(fd);
|
|
188
|
+
}
|
|
189
|
+
const seconds = Math.round((Date.now() - started) / 100) / 10;
|
|
190
|
+
const passed = result.error === undefined && result.status === 0;
|
|
191
|
+
step = {
|
|
192
|
+
name: plan.name,
|
|
193
|
+
status: passed ? 'pass' : 'fail',
|
|
194
|
+
command: plan.command.display,
|
|
195
|
+
seconds,
|
|
196
|
+
log: relative(cwd, log),
|
|
197
|
+
...(result.error === undefined ? {} : { reason: result.error.message }),
|
|
198
|
+
...(passed ? {} : { tail: tailOf(log) }),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
steps.push(step);
|
|
202
|
+
options.onStep?.({ phase: 'end', step });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const ok = steps.every((step) => step.status !== 'fail');
|
|
206
|
+
// The tree the steps left: validate:content may rewrite tracked files (content/versions.json).
|
|
207
|
+
const tree = verifyTreeHash(cwd);
|
|
208
|
+
if (ok && tree !== null) {
|
|
209
|
+
const record: PassRecord = { tree, e2e, when: new Date().toISOString(), steps };
|
|
210
|
+
writeFileSync(recordFile, `${JSON.stringify(record, null, 2)}\n`);
|
|
211
|
+
}
|
|
212
|
+
return { ok, cached: false, tree, e2e, steps };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The digest after a run: failed steps' tails, then one verdict line. */
|
|
216
|
+
export function formatVerifyReport(report: VerifyReport): string {
|
|
217
|
+
if (report.cached) {
|
|
218
|
+
return `verify: PASS, unchanged since ${report.since} (${report.e2e ? 'with' : 'without'} e2e); --force reruns\n`;
|
|
219
|
+
}
|
|
220
|
+
const out: string[] = [];
|
|
221
|
+
for (const step of report.steps) {
|
|
222
|
+
if (step.status !== 'fail') continue;
|
|
223
|
+
out.push('', `${step.name} failed${step.reason === undefined ? '' : `: ${step.reason}`}${step.log === undefined ? '' : ` — last lines of ${step.log}:`}`);
|
|
224
|
+
for (const line of step.tail ?? []) out.push(` ${line}`);
|
|
225
|
+
}
|
|
226
|
+
const failed = report.steps.filter((step) => step.status === 'fail').map((step) => step.name);
|
|
227
|
+
out.push(
|
|
228
|
+
'',
|
|
229
|
+
failed.length === 0
|
|
230
|
+
? `verify: PASS${report.tree === null ? ' (not a git repository: nothing recorded)' : ''}`
|
|
231
|
+
: `verify: FAIL — ${failed.join(', ')}; whole output under ${VERIFY_DIR}/`,
|
|
232
|
+
);
|
|
233
|
+
return `${out.join('\n').replace(/^\n/, '')}\n`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** One progress line per step: ` astro build ok 41.2s`. */
|
|
237
|
+
export function formatVerifyStep(step: VerifyStep): string {
|
|
238
|
+
const verdict = step.status === 'pass' ? 'ok' : step.status === 'fail' ? 'FAIL' : 'skipped';
|
|
239
|
+
const detail = step.seconds !== undefined ? `${step.seconds.toFixed(1)}s` : (step.reason ?? '');
|
|
240
|
+
return ` ${step.name.padEnd(18)} ${verdict.padEnd(7)} ${detail}\n`;
|
|
241
|
+
}
|