cursedbelt 3.0.2 → 4.1.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/dist/react/components/ListToolbar.d.ts +94 -0
- package/dist/react/components/ListToolbar.d.ts.map +1 -0
- package/dist/react/components/ListToolbar.js +66 -0
- package/dist/react/components/ListToolbar.js.map +1 -0
- package/dist/react/file-tree/FileTree.d.ts +282 -3
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +799 -131
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +151 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts +187 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts.map +1 -0
- package/dist/react/file-tree/fileTreeSelection.js +264 -0
- package/dist/react/file-tree/fileTreeSelection.js.map +1 -0
- package/dist/react/file-tree/index.d.ts +1 -0
- package/dist/react/file-tree/index.d.ts.map +1 -1
- package/dist/react/file-tree/index.js +8 -0
- package/dist/react/file-tree/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/form.d.ts +3 -0
- package/dist/react/lib/form.d.ts.map +1 -1
- package/dist/react/lib/form.js +43 -5
- package/dist/react/lib/form.js.map +1 -1
- package/dist/react/media/DenseGalleryShell.d.ts +19 -3
- package/dist/react/media/DenseGalleryShell.d.ts.map +1 -1
- package/dist/react/media/DenseGalleryShell.js +55 -15
- package/dist/react/media/DenseGalleryShell.js.map +1 -1
- package/dist/react/media/FocusViewer.d.ts.map +1 -1
- package/dist/react/media/FocusViewer.js +14 -14
- package/dist/react/media/FocusViewer.js.map +1 -1
- package/dist/react/media/FrameGrabber.d.ts +23 -0
- package/dist/react/media/FrameGrabber.d.ts.map +1 -1
- package/dist/react/media/FrameGrabber.js +67 -1
- package/dist/react/media/FrameGrabber.js.map +1 -1
- package/dist/react/media/MediaResize.d.ts +51 -19
- package/dist/react/media/MediaResize.d.ts.map +1 -1
- package/dist/react/media/MediaResize.js +20 -13
- package/dist/react/media/MediaResize.js.map +1 -1
- package/dist/react/media/VideoChapterEditor.d.ts +67 -0
- package/dist/react/media/VideoChapterEditor.d.ts.map +1 -0
- package/dist/react/media/VideoChapterEditor.js +148 -0
- package/dist/react/media/VideoChapterEditor.js.map +1 -0
- package/dist/react/media/VideoPlayer.d.ts +46 -4
- package/dist/react/media/VideoPlayer.d.ts.map +1 -1
- package/dist/react/media/VideoPlayer.js +124 -11
- package/dist/react/media/VideoPlayer.js.map +1 -1
- package/dist/react/media/VideoTurner.d.ts +15 -2
- package/dist/react/media/VideoTurner.d.ts.map +1 -1
- package/dist/react/media/VideoTurner.js +29 -2
- package/dist/react/media/VideoTurner.js.map +1 -1
- package/dist/react/media/denseGallery.css +104 -46
- package/dist/react/media/hlsSource.d.ts +57 -0
- package/dist/react/media/hlsSource.d.ts.map +1 -1
- package/dist/react/media/hlsSource.js +177 -1
- package/dist/react/media/hlsSource.js.map +1 -1
- package/dist/react/media/index.d.ts +2 -0
- package/dist/react/media/index.d.ts.map +1 -1
- package/dist/react/media/index.js +6 -0
- package/dist/react/media/index.js.map +1 -1
- package/dist/react/media/mediaResize.css +127 -54
- package/dist/react/media/pictureEditor.css +52 -6
- package/dist/react/media/videoChapterEditor.css +203 -0
- package/dist/react/media/videoChapters.d.ts +117 -0
- package/dist/react/media/videoChapters.d.ts.map +1 -0
- package/dist/react/media/videoChapters.js +95 -0
- package/dist/react/media/videoChapters.js.map +1 -0
- package/dist/react/media-gallery/GalleryTable.d.ts +52 -1
- package/dist/react/media-gallery/GalleryTable.d.ts.map +1 -1
- package/dist/react/media-gallery/GalleryTable.js +40 -9
- package/dist/react/media-gallery/GalleryTable.js.map +1 -1
- package/dist/react/media-gallery/MediaGallery.d.ts +107 -4
- package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
- package/dist/react/media-gallery/MediaGallery.js +484 -104
- package/dist/react/media-gallery/MediaGallery.js.map +1 -1
- package/dist/react/media-gallery/MediaMetaEditor.d.ts +48 -0
- package/dist/react/media-gallery/MediaMetaEditor.d.ts.map +1 -0
- package/dist/react/media-gallery/MediaMetaEditor.js +66 -0
- package/dist/react/media-gallery/MediaMetaEditor.js.map +1 -0
- package/dist/react/media-gallery/index.d.ts +2 -0
- package/dist/react/media-gallery/index.d.ts.map +1 -1
- package/dist/react/media-gallery/index.js +5 -0
- package/dist/react/media-gallery/index.js.map +1 -1
- package/dist/react/media-gallery/mediaGallery.css +180 -4
- package/dist/react/media-gallery/mediaMeta.d.ts +62 -0
- package/dist/react/media-gallery/mediaMeta.d.ts.map +1 -0
- package/dist/react/media-gallery/mediaMeta.js +56 -0
- package/dist/react/media-gallery/mediaMeta.js.map +1 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts +77 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts.map +1 -1
- package/dist/react/media-gallery/playbackPreferences.js +42 -0
- package/dist/react/media-gallery/playbackPreferences.js.map +1 -1
- package/dist/react/media-gallery/types.d.ts +34 -0
- package/dist/react/media-gallery/types.d.ts.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts +29 -0
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -0
- package/dist/styles-areas/activity-bar.css +30 -0
- package/dist/styles-areas/analytics.css +672 -0
- package/dist/styles-areas/auth.css +154 -0
- package/dist/styles-areas/calendar.css +416 -0
- package/dist/styles-areas/chart-table.css +41 -0
- package/dist/styles-areas/charts.css +89 -0
- package/dist/styles-areas/chat.css +364 -0
- package/dist/styles-areas/clipboard.css +26 -0
- package/dist/styles-areas/code-editor.css +130 -0
- package/dist/styles-areas/companion-link.css +70 -0
- package/dist/styles-areas/core.css +2503 -0
- package/dist/styles-areas/dashboard-grid.css +84 -0
- package/dist/styles-areas/data-table.css +657 -0
- package/dist/styles-areas/deep-link.css +26 -0
- package/dist/styles-areas/diff-viewer.css +30 -0
- package/dist/styles-areas/disk-usage.css +262 -0
- package/dist/styles-areas/emoji.css +154 -0
- package/dist/styles-areas/fields.css +582 -0
- package/dist/styles-areas/file-tree.css +33 -0
- package/dist/styles-areas/filter-rail.css +235 -0
- package/dist/styles-areas/folder-tree.css +219 -0
- package/dist/styles-areas/keep-awake.css +26 -0
- package/dist/styles-areas/layout-engine.css +239 -0
- package/dist/styles-areas/markdown.css +87 -0
- package/dist/styles-areas/master-detail.css +239 -0
- package/dist/styles-areas/master-lock.css +26 -0
- package/dist/styles-areas/media-gallery.css +351 -0
- package/dist/styles-areas/media.css +658 -0
- package/dist/styles-areas/nav.css +488 -0
- package/dist/styles-areas/notifications.css +245 -0
- package/dist/styles-areas/overlays.css +133 -0
- package/dist/styles-areas/palette.css +140 -0
- package/dist/styles-areas/pdf-viewer.css +47 -0
- package/dist/styles-areas/problem-detail.css +97 -0
- package/dist/styles-areas/rich-text.css +167 -0
- package/dist/styles-areas/sharing.css +153 -0
- package/dist/styles-areas/spreadsheet.css +37 -0
- package/dist/styles-areas/stats.css +281 -0
- package/dist/styles-areas/test-report.css +133 -0
- package/dist/styles-areas/virtual.css +30 -0
- package/dist/styles-areas/wizard.css +381 -0
- package/dist/styles-areas/workbench.css +543 -0
- package/dist/styles-areas/workbook-viewer.css +44 -0
- package/dist/styles-static.css +34 -23
- package/dist/styles.css +34 -23
- package/package.json +130 -32
- package/scripts/checkDistExports.ts +67 -0
- package/scripts/cssRules.ts +125 -0
- package/scripts/fixtureAppCss.ts +344 -0
- package/scripts/generateAreaStyles.ts +253 -0
- package/scripts/guardrailsEnforce.spec.ts +62 -0
- package/scripts/guardrailsEnforce.ts +67 -3
- package/scripts/paths.ts +106 -0
- package/scripts/styleAreas.ts +585 -0
- package/scripts/verify.ts +5 -0
- package/src/barrelsReachNoOptionalPeer.spec.ts +117 -9
- package/src/docsMatchTheSplit.spec.ts +98 -0
- package/src/fixtureAppCss.spec.ts +177 -0
- package/src/namedSubpathsResolve.spec.ts +35 -0
- package/src/publishShape.spec.ts +67 -0
- package/src/react/components/ListToolbar.spec.tsx +172 -0
- package/src/react/components/ListToolbar.tsx +151 -0
- package/src/react/file-tree/FileTree.spec.tsx +1009 -0
- package/src/react/file-tree/FileTree.tsx +1459 -309
- package/src/react/file-tree/fileTree.css +151 -0
- package/src/react/file-tree/fileTreeSelection.spec.ts +327 -0
- package/src/react/file-tree/fileTreeSelection.ts +321 -0
- package/src/react/file-tree/index.ts +21 -0
- package/src/react/index.ts +1 -0
- package/src/react/lib/form.ts +51 -8
- package/src/react/markdown/MarkdownDoc.spec.tsx +1 -1
- package/src/react/media/DenseGalleryShell.tsx +108 -36
- package/src/react/media/FocusViewer.tsx +15 -8
- package/src/react/media/FrameGrabber.spec.tsx +175 -0
- package/src/react/media/FrameGrabber.tsx +67 -0
- package/src/react/media/MediaResize.spec.tsx +84 -14
- package/src/react/media/MediaResize.tsx +51 -19
- package/src/react/media/VideoChapterEditor.tsx +428 -0
- package/src/react/media/VideoPlayer.spec.tsx +111 -1
- package/src/react/media/VideoPlayer.tsx +158 -11
- package/src/react/media/VideoTurner.spec.tsx +67 -0
- package/src/react/media/VideoTurner.tsx +70 -15
- package/src/react/media/denseGallery.css +104 -46
- package/src/react/media/hlsSource.recovery.spec.ts +213 -0
- package/src/react/media/hlsSource.spec.ts +8 -0
- package/src/react/media/hlsSource.ts +189 -1
- package/src/react/media/index.ts +14 -0
- package/src/react/media/mediaResize.css +127 -54
- package/src/react/media/pictureEditor.css +52 -6
- package/src/react/media/videoChapterEditor.css +203 -0
- package/src/react/media/videoChapters.spec.ts +176 -0
- package/src/react/media/videoChapters.ts +181 -0
- package/src/react/media-gallery/GalleryTable.tsx +93 -3
- package/src/react/media-gallery/MediaGallery.spec.tsx +1156 -10
- package/src/react/media-gallery/MediaGallery.tsx +813 -95
- package/src/react/media-gallery/MediaMetaEditor.tsx +237 -0
- package/src/react/media-gallery/galleryTable.spec.ts +41 -1
- package/src/react/media-gallery/index.ts +7 -0
- package/src/react/media-gallery/mediaGallery.css +180 -4
- package/src/react/media-gallery/mediaMeta.spec.ts +95 -0
- package/src/react/media-gallery/mediaMeta.ts +101 -0
- package/src/react/media-gallery/playbackPreferences.spec.ts +172 -0
- package/src/react/media-gallery/playbackPreferences.ts +100 -0
- package/src/react/media-gallery/types.ts +34 -0
- package/src/shippedFilesAreTracked.spec.ts +69 -0
- package/src/styles-areas/activity-bar.css +30 -0
- package/src/styles-areas/analytics.css +672 -0
- package/src/styles-areas/auth.css +154 -0
- package/src/styles-areas/calendar.css +416 -0
- package/src/styles-areas/chart-table.css +41 -0
- package/src/styles-areas/charts.css +89 -0
- package/src/styles-areas/chat.css +364 -0
- package/src/styles-areas/clipboard.css +26 -0
- package/src/styles-areas/code-editor.css +130 -0
- package/src/styles-areas/companion-link.css +70 -0
- package/src/styles-areas/core.css +2503 -0
- package/src/styles-areas/dashboard-grid.css +84 -0
- package/src/styles-areas/data-table.css +657 -0
- package/src/styles-areas/deep-link.css +26 -0
- package/src/styles-areas/diff-viewer.css +30 -0
- package/src/styles-areas/disk-usage.css +262 -0
- package/src/styles-areas/emoji.css +154 -0
- package/src/styles-areas/fields.css +582 -0
- package/src/styles-areas/file-tree.css +33 -0
- package/src/styles-areas/filter-rail.css +235 -0
- package/src/styles-areas/folder-tree.css +219 -0
- package/src/styles-areas/keep-awake.css +26 -0
- package/src/styles-areas/layout-engine.css +239 -0
- package/src/styles-areas/markdown.css +87 -0
- package/src/styles-areas/master-detail.css +239 -0
- package/src/styles-areas/master-lock.css +26 -0
- package/src/styles-areas/media-gallery.css +351 -0
- package/src/styles-areas/media.css +658 -0
- package/src/styles-areas/nav.css +488 -0
- package/src/styles-areas/notifications.css +245 -0
- package/src/styles-areas/overlays.css +133 -0
- package/src/styles-areas/palette.css +140 -0
- package/src/styles-areas/pdf-viewer.css +47 -0
- package/src/styles-areas/problem-detail.css +97 -0
- package/src/styles-areas/rich-text.css +167 -0
- package/src/styles-areas/sharing.css +153 -0
- package/src/styles-areas/spreadsheet.css +37 -0
- package/src/styles-areas/stats.css +281 -0
- package/src/styles-areas/test-report.css +133 -0
- package/src/styles-areas/virtual.css +30 -0
- package/src/styles-areas/wizard.css +381 -0
- package/src/styles-areas/workbench.css +543 -0
- package/src/styles-areas/workbook-viewer.css +44 -0
- package/src/styles-static.css +34 -23
- package/src/styles.css +34 -23
- package/src/stylesAreas.spec.ts +247 -0
- package/src/stylesUtilitiesMatches.spec.ts +6 -77
- package/src/testFilesRunInParallel.spec.ts +79 -0
- package/src/typecheckCachesAreSeparate.spec.ts +128 -0
- package/src/verifyGraph.spec.ts +6 -0
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
// run, so these stay few and visible. A malformed/unrecognized-category directive is
|
|
148
148
|
// ignored (its violations still fire) and a warning is printed.
|
|
149
149
|
|
|
150
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
150
|
+
import { type Dirent, existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
151
151
|
import { basename, extname, join, relative } from "node:path";
|
|
152
152
|
|
|
153
153
|
const IGNORE_DIR_NAMES = new Set(["node_modules", "dist", "dist-deploy", ".git", "build", "coverage"]);
|
|
@@ -179,7 +179,15 @@ interface Rule {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
export function walk(dir: string, out: string[] = []): string[] {
|
|
182
|
-
|
|
182
|
+
// 🔴 `Dirent[]`, never `ReturnType<typeof readdirSync>`. `readdirSync` is OVERLOADED, and
|
|
183
|
+
// `ReturnType` of an overloaded function is the LAST overload — which @types/node 24 made
|
|
184
|
+
// `Dirent<NonSharedBuffer>[]` when `Dirent` became generic, while the call below returns
|
|
185
|
+
// the string-encoding overload's `Dirent<string>[]`. The annotation was always a guess at
|
|
186
|
+
// an overload set somebody else maintains; a release reordered it and the guess became
|
|
187
|
+
// five compile errors that every consumer of `cursedbelt/guardrails` inherited (task 172).
|
|
188
|
+
// Bare `Dirent` is `Dirent<string>` under >=24's default type argument and is the plain
|
|
189
|
+
// non-generic class before that, so this spelling is correct on both.
|
|
190
|
+
let entries: Dirent[];
|
|
183
191
|
try {
|
|
184
192
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
185
193
|
} catch {
|
|
@@ -201,13 +209,69 @@ export function walk(dir: string, out: string[] = []): string[] {
|
|
|
201
209
|
return out;
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
/** Index just past the string/template literal opening at `start`. Quoted attribute values
|
|
213
|
+
* are skipped so a `//` inside one reads as the URL it is (`src="https://…"`) rather than
|
|
214
|
+
* as the start of a comment. `'`/`"` stop at a newline — an unterminated quote must not
|
|
215
|
+
* swallow the rest of the file; a backtick may span lines and nests `${…}`. */
|
|
216
|
+
function skipQuoted(source: string, start: number): number {
|
|
217
|
+
const quote = source[start];
|
|
218
|
+
let i = start + 1;
|
|
219
|
+
if (quote !== "`") {
|
|
220
|
+
while (i < source.length && source[i] !== quote && source[i] !== "\n") {
|
|
221
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
222
|
+
}
|
|
223
|
+
return Math.min(i + 1, source.length);
|
|
224
|
+
}
|
|
225
|
+
let depth = 0;
|
|
226
|
+
while (i < source.length) {
|
|
227
|
+
if (source[i] === "\\") {
|
|
228
|
+
i += 2;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (depth === 0 && source[i] === "`") return i + 1;
|
|
232
|
+
if (source.slice(i, i + 2) === "${") {
|
|
233
|
+
depth++;
|
|
234
|
+
i += 2;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (depth > 0 && source[i] === "}") depth--;
|
|
238
|
+
i++;
|
|
239
|
+
}
|
|
240
|
+
return i;
|
|
241
|
+
}
|
|
242
|
+
|
|
204
243
|
/** Slice from a `<button` (or similar) match to the end of its opening tag, tolerating
|
|
205
|
-
* `{...}` JS expressions in attribute values that may themselves contain
|
|
244
|
+
* `{...}` JS expressions in attribute values that may themselves contain `>`
|
|
245
|
+
* (`onClick={() => …}`, which works because that `>` sits inside braces).
|
|
246
|
+
*
|
|
247
|
+
* JSX permits line and block comments INSIDE an attribute list, and a comment is prose —
|
|
248
|
+
* so it may contain an angle bracket ("a radio-like list rather than a `<select>`").
|
|
249
|
+
* Those are skipped the same way `blankComments` skips them, because a `>` written in prose
|
|
250
|
+
* used to end the tag here and make every attribute below it invisible — the
|
|
251
|
+
* `guardrails-ignore` opt-out included. `blankComments` cannot simply be run first: the
|
|
252
|
+
* opt-out checks read this slice and must see the ORIGINAL comment text (see the
|
|
253
|
+
* no-raw-action-button check below), so the skip only suppresses TERMINATION; the comment
|
|
254
|
+
* stays in what is returned. */
|
|
206
255
|
function extractOpeningTag(source: string, startIndex: number): string {
|
|
207
256
|
let i = startIndex;
|
|
208
257
|
let braceDepth = 0;
|
|
209
258
|
while (i < source.length) {
|
|
259
|
+
const two = source.slice(i, i + 2);
|
|
260
|
+
if (two === "//") {
|
|
261
|
+
const end = source.indexOf("\n", i);
|
|
262
|
+
i = end === -1 ? source.length : end;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (two === "/*") {
|
|
266
|
+
const end = source.indexOf("*/", i + 2);
|
|
267
|
+
i = end === -1 ? source.length : end + 2;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
210
270
|
const ch = source[i];
|
|
271
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
272
|
+
i = skipQuoted(source, i);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
211
275
|
if (ch === "{") braceDepth++;
|
|
212
276
|
else if (ch === "}") braceDepth--;
|
|
213
277
|
else if (ch === ">" && braceDepth <= 0) {
|
package/scripts/paths.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* `bun run paths` — the generation's whole-tree laws, run over THIS repo's tree.
|
|
4
|
+
*
|
|
5
|
+
* {@link TOOLS} is the list: `check-paths.ts` (the one law — exactly one file in a generation
|
|
6
|
+
* may contain a literal path) and `check-doc-citations.ts` (every `docs/<name>.md` this repo
|
|
7
|
+
* points a reader at must exist, with the sections it names). Both are generation-scoped rules
|
|
8
|
+
* a repo proves about ITSELF — rule 3, a repo's gate proves that repo — both are located the
|
|
9
|
+
* same way, and both have to fail this step rather than be skipped.
|
|
10
|
+
*
|
|
11
|
+
* ## 🔴 Why this is a wrapper and not `bun ../../tools/check-paths.ts .`
|
|
12
|
+
*
|
|
13
|
+
* That spelling assumes this checkout sits at a fixed depth under the generation root, and a
|
|
14
|
+
* WORKTREE never does: the working agreement puts worktrees outside every generation by
|
|
15
|
+
* construction, under the machine-wide worktrees root. Measured 2026-09-17 in a worktree,
|
|
16
|
+
* with no changes to any script — `error: Module not found "../../tools/check-paths.ts"`,
|
|
17
|
+
* and the gate stops on its FIRST step. Seventeen packages spelled it that way, and the failure
|
|
18
|
+
* mode is not the error — it is the workaround: an agent that cannot run its repo's gate moves
|
|
19
|
+
* back into the shared checkout another agent is holding, or lands without a green gate — and
|
|
20
|
+
* the second one is invisible afterwards.
|
|
21
|
+
*
|
|
22
|
+
* {@link findForgeRoot} walks up for `forge.env`, the same marker `tools/check-paths.ts` and
|
|
23
|
+
* the runner both use, so the three cannot disagree. {@link viaWorktree} is the worktree case:
|
|
24
|
+
* a worktree's `.git` is a FILE pointing at the primary checkout's git directory, so the
|
|
25
|
+
* primary is discoverable with no environment variable and no guess.
|
|
26
|
+
*
|
|
27
|
+
* 🔴 **No generation, no pass.** Answering "fine" because the check could not be located is how
|
|
28
|
+
* a rule stops being enforced without anybody deciding to stop enforcing it — so every failure
|
|
29
|
+
* to locate exits 1, and says which of the two walks came back empty.
|
|
30
|
+
*
|
|
31
|
+
* `tools/check-paths.ts` is what keeps this spelling honest across the roster: a repo whose
|
|
32
|
+
* `paths` script reaches a tool through a relative `../` module path fails the whole-generation
|
|
33
|
+
* run, which is the check that this whole file is the fix for.
|
|
34
|
+
*/
|
|
35
|
+
import { spawnSync } from "node:child_process";
|
|
36
|
+
import { existsSync } from "node:fs";
|
|
37
|
+
import { dirname, join, resolve } from "node:path";
|
|
38
|
+
import { fileURLToPath } from "node:url";
|
|
39
|
+
|
|
40
|
+
/** The generation's tools this repo runs over its own tree, in the order they should fail. */
|
|
41
|
+
const TOOLS = ["check-paths.ts", "check-doc-citations.ts"];
|
|
42
|
+
|
|
43
|
+
/** How far up either walk looks before giving up — a bound, so a misuse is `null` in ms. */
|
|
44
|
+
const MAX_HOPS = 16;
|
|
45
|
+
|
|
46
|
+
/** Walk up from `from` for a directory containing `marker`, or `null`. */
|
|
47
|
+
function walkUp(from: string, marker: string): string | null {
|
|
48
|
+
let dir = resolve(from);
|
|
49
|
+
for (let hops = 0; hops < MAX_HOPS; hops++) {
|
|
50
|
+
if (existsSync(join(dir, marker))) return dir;
|
|
51
|
+
const up = dirname(dir);
|
|
52
|
+
if (up === dir) return null;
|
|
53
|
+
dir = up;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The directory holding `forge.env` above `from`, or `null` when that is not inside a generation. */
|
|
59
|
+
function findForgeRoot(from: string): string | null {
|
|
60
|
+
return walkUp(from, "forge.env");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The generation above the checkout this WORKTREE was cut from, or `null` on anything unexpected. */
|
|
64
|
+
function viaWorktree(from: string): string | null {
|
|
65
|
+
try {
|
|
66
|
+
const git = spawnSync("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
|
|
67
|
+
cwd: from,
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
timeout: 5_000,
|
|
70
|
+
});
|
|
71
|
+
if (git.status !== 0) return null;
|
|
72
|
+
const gitDir = (git.stdout ?? "").trim();
|
|
73
|
+
if (!gitDir || !gitDir.startsWith("/")) return null;
|
|
74
|
+
// `<primary>/.git` → `<primary>`, then the ordinary walk up.
|
|
75
|
+
return findForgeRoot(dirname(gitDir));
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
82
|
+
/** This checkout — the nearest ancestor holding a `package.json`, so it is right in a worktree. */
|
|
83
|
+
const repo = walkUp(here, "package.json");
|
|
84
|
+
const root = findForgeRoot(here) ?? viaWorktree(here);
|
|
85
|
+
|
|
86
|
+
if (!repo) {
|
|
87
|
+
console.error(`✗ no package.json above ${here}, so there is no repo to check.`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
if (!root) {
|
|
91
|
+
console.error("✗ this checkout is not inside a generation and is not a worktree of one, so");
|
|
92
|
+
console.error(` tools/{${TOOLS.join(",")}} cannot be located. Run it from a checkout under a`);
|
|
93
|
+
console.error(" generation, or from a worktree cut from one — a skipped check is not a passed one.");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const name of TOOLS) {
|
|
98
|
+
const tool = join(root, "tools", name);
|
|
99
|
+
if (!existsSync(tool)) {
|
|
100
|
+
console.error(`✗ ${tool} does not exist. The generation at ${root} has no ${name},`);
|
|
101
|
+
console.error(" so this repo cannot prove it obeys that law. That is a failure, not a skip.");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const result = spawnSync("bun", [tool, repo, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
105
|
+
if ((result.status ?? 1) !== 0) process.exit(result.status ?? 1);
|
|
106
|
+
}
|