@rtorcato/repo-tooling 4.8.0 → 5.0.1

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.
@@ -1,106 +0,0 @@
1
- // Canonical sync-changelog for @rtorcato/* docs sites (shipped by
2
- // @rtorcato/repo-tooling — copy via `repo-tooling copy docusaurus-sync-changelog`).
3
- //
4
- // Builds the docs site's changelog page from **GitHub Releases**, not from
5
- // CHANGELOG.md. The output file is gitignored — it is regenerated on every docs
6
- // build (wire it into the docs app's `build`/`start` scripts; pnpm 8 doesn't run
7
- // `pre*` hooks reliably, so chain it explicitly).
8
- //
9
- // Why Releases and not the file: the shipped semantic-release github preset
10
- // does not use @semantic-release/git, because the `code-scanning-main` ruleset
11
- // rejects the release commit with GH013 (see tooling/semantic-release/github.mjs
12
- // and rtorcato/repo-tooling#417). Nothing commits CHANGELOG.md back, so it is
13
- // frozen at whatever the last release with that plugin wrote — and it freezes at
14
- // a *real* release, which is why it reads as current. @semantic-release/github
15
- // writes Releases from the same notes that used to build CHANGELOG.md, so
16
- // concatenating their bodies reproduces the old page and cannot go stale.
17
- //
18
- // The target defaults to Docusaurus's `apps/docs/docs/changelog.md`. Override
19
- // with the CHANGELOG_TARGET env var (path relative to the repo root) for other
20
- // layouts — e.g. a Fumadocs content root:
21
- // CHANGELOG_TARGET=apps/web/content/docs/changelog.mdx node scripts/sync-changelog.mjs
22
- // The title/description frontmatter is framework-neutral (both Docusaurus and
23
- // Fumadocs read it), so only the path changes between frameworks.
24
- //
25
- // The docs workflow must rebuild on `release: types: [published]` — with no
26
- // release commit landing on the default branch, a `push` trigger never fires.
27
-
28
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
29
- import { dirname, resolve } from 'node:path'
30
- import { fileURLToPath } from 'node:url'
31
-
32
- const here = dirname(fileURLToPath(import.meta.url))
33
- const repoRoot = resolve(here, '..')
34
- const target = resolve(repoRoot, process.env.CHANGELOG_TARGET ?? 'apps/docs/docs/changelog.md')
35
-
36
- /** `owner/name` — from CI, else from package.json `repository`. */
37
- function resolveRepo() {
38
- if (process.env.GITHUB_REPOSITORY) return process.env.GITHUB_REPOSITORY
39
- const pkg = JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8'))
40
- const url = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url
41
- const match = /github\.com[/:]([^/]+\/[^/.]+)/.exec(url ?? '')
42
- if (!match) throw new Error('no GITHUB_REPOSITORY and no github repository in package.json')
43
- return match[1]
44
- }
45
-
46
- const frontmatter = `---
47
- title: Changelog
48
- description: Release notes, generated by semantic-release and published to GitHub Releases.
49
- ---
50
-
51
- `
52
-
53
- const footer = (repo, note) =>
54
- `\n\n---\n\n${note}Every entry above is generated by semantic-release and published to [GitHub Releases](https://github.com/${repo}/releases). Anything older than the first GitHub Release is recorded in [CHANGELOG.md](https://github.com/${repo}/blob/main/CHANGELOG.md).\n`
55
-
56
- async function fetchReleases(repo) {
57
- const headers = { Accept: 'application/vnd.github+json' }
58
- // Optional: lifts the 60/hr unauthenticated rate limit. CI passes it; local
59
- // builds work fine without it at a typical repo's release count.
60
- // ponytail: one page of 100. Paginate when a repo outgrows it.
61
- const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN
62
- if (token) headers.Authorization = `Bearer ${token}`
63
-
64
- const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=100`, { headers })
65
- if (!res.ok) throw new Error(`GitHub API ${res.status} ${res.statusText}`)
66
- return res.json()
67
- }
68
-
69
- function render(releases) {
70
- // The API does not guarantee date order, and a release body already carries
71
- // its own version heading and date — so sort here and emit bodies verbatim.
72
- const sorted = releases
73
- .filter((r) => !r.draft && r.body?.trim())
74
- .sort((a, b) => new Date(b.published_at) - new Date(a.published_at))
75
-
76
- if (sorted.length === 0) throw new Error('no releases with a body')
77
- return sorted.map((r) => r.body.trim()).join('\n\n')
78
- }
79
-
80
- let repo = ''
81
- let body
82
- try {
83
- repo = resolveRepo()
84
- body = render(await fetchReleases(repo)) + footer(repo, '')
85
- console.log(`sync-changelog: built from ${repo} GitHub Releases`)
86
- } catch (error) {
87
- // Never fail the docs build on a network blip — fall back to the frozen file
88
- // and say so on the page rather than shipping a silently truncated one.
89
- console.warn(`sync-changelog: ${error.message} — falling back to CHANGELOG.md`)
90
- try {
91
- body =
92
- readFileSync(resolve(repoRoot, 'CHANGELOG.md'), 'utf8').trim() +
93
- (repo
94
- ? footer(
95
- repo,
96
- '**This page was built from the repository CHANGELOG.md because GitHub Releases could not be reached, and may be out of date.** '
97
- )
98
- : '')
99
- } catch {
100
- body = '# Changelog\n\nNo releases recorded yet.\n'
101
- }
102
- }
103
-
104
- mkdirSync(dirname(target), { recursive: true })
105
- writeFileSync(target, frontmatter + body)
106
- console.log(`sync-changelog: wrote ${target}`)
@@ -1,79 +0,0 @@
1
- /**
2
- * Shared @rtorcato Docusaurus design tokens (shipped by @rtorcato/repo-tooling —
3
- * copy via `repo-tooling copy docusaurus-theme-tokens`).
4
- *
5
- * The Geist font + navy-surface design-token base every sibling docs site
6
- * shares. `@import` this from your `src/css/custom.css`, then override the
7
- * accent (`--ifm-color-primary*` and `--jt-accent*`) with your brand colour:
8
- *
9
- * @import "./_jt-tokens.css";
10
- * :root { --ifm-color-primary: #F38020; --jt-accent: #F38020; }
11
- * [data-theme=dark]{ --ifm-color-primary: #ff9a4d; --jt-accent: #ff9a4d; }
12
- *
13
- * The default accent below is Docusaurus's neutral green — meant to be replaced.
14
- */
15
-
16
- /* ---------- Fonts ---------- */
17
- @import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&family=Geist+Mono:wght@400;500;600&display=swap");
18
-
19
- /* ---------- Light (default) ---------- */
20
- :root {
21
- --ifm-color-primary: #2e8555;
22
-
23
- --ifm-font-family-base: "Geist", system-ui, -apple-system, sans-serif;
24
- --ifm-font-family-monospace: "Geist Mono", ui-monospace, SFMono-Regular, monospace;
25
- --ifm-heading-font-weight: 800;
26
- --ifm-code-font-size: 92%;
27
-
28
- --ifm-background-color: #ffffff;
29
- --ifm-background-surface-color: #ffffff;
30
- --ifm-navbar-background-color: rgba(255, 255, 255, 0.8);
31
- --ifm-heading-color: #0a0e16;
32
- --ifm-font-color-base: #374253;
33
- --ifm-toc-border-color: rgba(12, 18, 28, 0.1);
34
- --ifm-code-background: #f3f6f9;
35
-
36
- /* Design tokens (surfaces/text/borders are shared; accent is per-project). */
37
- --jt-accent: var(--ifm-color-primary);
38
- --jt-on-accent: #ffffff;
39
- --jt-bg: #ffffff;
40
- --jt-bg2: #f6f8fb;
41
- --jt-surface: #ffffff;
42
- --jt-surface2: #f3f6f9;
43
- --jt-code-bg: #f3f6f9;
44
- --jt-border: rgba(12, 18, 28, 0.1);
45
- --jt-border2: rgba(12, 18, 28, 0.16);
46
- --jt-heading: #0a0e16;
47
- --jt-text: #374253;
48
- --jt-muted: #5b6675;
49
- --jt-faint: #8a95a5;
50
- --jt-shadow: 0 22px 50px -26px rgba(20, 28, 44, 0.3);
51
- }
52
-
53
- /* ---------- Dark ---------- */
54
- [data-theme="dark"] {
55
- --ifm-color-primary: #25c2a0;
56
-
57
- --ifm-background-color: #0a0e16;
58
- --ifm-background-surface-color: #111824;
59
- --ifm-navbar-background-color: #0a0e16;
60
- --ifm-heading-color: #ffffff;
61
- --ifm-font-color-base: #dfe5ee;
62
- --ifm-toc-border-color: rgba(255, 255, 255, 0.08);
63
- --ifm-code-background: #0d131d;
64
-
65
- --jt-accent: var(--ifm-color-primary);
66
- --jt-on-accent: #0a0e16;
67
- --jt-bg: #0a0e16;
68
- --jt-bg2: #0b101a;
69
- --jt-surface: #111824;
70
- --jt-surface2: #0d131d;
71
- --jt-code-bg: #0c111b;
72
- --jt-border: rgba(255, 255, 255, 0.08);
73
- --jt-border2: rgba(255, 255, 255, 0.14);
74
- --jt-heading: #ffffff;
75
- --jt-text: #dfe5ee;
76
- --jt-muted: #94a1b4;
77
- --jt-faint: #647184;
78
- --jt-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.75);
79
- }
@@ -1,390 +0,0 @@
1
- /**
2
- * Shared @rtorcato Docusaurus component theme (shipped by @rtorcato/repo-tooling —
3
- * copy via `repo-tooling copy docusaurus-theme`).
4
- *
5
- * The accent-agnostic component-override sheet every sibling docs site shares:
6
- * navbar, doc cards, sidebar, TOC, pagination, footer, code blocks, markdown
7
- * tables, buttons, and the hardened navy dark surfaces. Every rule references
8
- * the `--jt-*` / `--ifm-*` tokens from `_jt-tokens.css` (see
9
- * `copy docusaurus-theme-tokens`) — it defines NO accent colours.
10
- *
11
- * Usage in your `src/css/custom.css`:
12
- *
13
- * @import "./_jt-tokens.css"; // shared design tokens
14
- * @import "./theme.css"; // this file — shared component styling
15
- * :root { --ifm-color-primary: #YOURHEX; --jt-accent: #YOURHEX; ... }
16
- * [data-theme=dark]{ --ifm-color-primary: #YOURHEX; --jt-accent: #YOURHEX; ... }
17
- * ::selection { background: rgba(R, G, B, 0.24); } // accent — keep local
18
- *
19
- * The single-flat mobile drawer (the `.navbar-sidebar__items` transform lock +
20
- * the repo-namespaced `.xx-mobile-menu` rules) is NOT here — it ships with the
21
- * Navbar/MobileSidebar swizzle files, since its class prefix is per-repo.
22
- */
23
-
24
- /* ---------- Navbar ---------- */
25
- .navbar {
26
- border-bottom: 1px solid var(--jt-border);
27
- box-shadow: none;
28
- }
29
- .navbar__link--active {
30
- color: var(--jt-heading);
31
- }
32
-
33
- /* Configuration Reference / index cards (generated-index). Match the clean
34
- "install box" look — near-page-bg fill + thin border instead of infima's dull
35
- surface fill — with an accent-border hover. Full description (no one-line
36
- `text--truncate` clamp) and equal card heights per row. */
37
- .theme-doc-card-container {
38
- height: 100%;
39
- background: var(--jt-code-bg);
40
- border: 1px solid var(--jt-border);
41
- border-radius: 14px;
42
- box-shadow: none;
43
- transition:
44
- border-color 0.15s ease,
45
- background 0.15s ease,
46
- transform 0.15s ease;
47
- }
48
- .theme-doc-card-container:hover {
49
- background: var(--jt-surface2);
50
- border-color: var(--jt-accent-border);
51
- transform: translateY(-2px);
52
- }
53
- .theme-doc-card-description.text--truncate {
54
- white-space: normal;
55
- overflow: visible;
56
- text-overflow: unset;
57
- }
58
-
59
- /* ---------- Backgrounds ---------- */
60
- html,
61
- body,
62
- #__docusaurus,
63
- .main-wrapper {
64
- background: var(--ifm-background-color);
65
- }
66
- /* Sidebar container: match page background, clean 1px right border, and
67
- ensure a long sidebar list scrolls without clipping the page footer. The
68
- sticky positioning + bounded height come from Infima defaults; the explicit
69
- overflow rule here defends against any inner container with overflow: hidden. */
70
- .theme-doc-sidebar-container {
71
- background: var(--ifm-background-color) !important;
72
- border-right: 1px solid var(--jt-border) !important;
73
- }
74
- .theme-doc-sidebar-container > div,
75
- .theme-doc-sidebar-container nav,
76
- .theme-doc-sidebar-menu,
77
- .menu,
78
- .menu__list {
79
- background: var(--ifm-background-color);
80
- }
81
- /* The sticky inner wrapper that holds the scrollable menu list. */
82
- .theme-doc-sidebar-container > div:first-child {
83
- height: 100%;
84
- overflow-y: auto;
85
- overscroll-behavior: contain;
86
- }
87
-
88
- /* ---------- Sidebar ---------- */
89
- .menu {
90
- font-size: 14px;
91
- background: transparent;
92
- padding: 16px 12px;
93
- }
94
- .menu__list .menu__list {
95
- padding-left: 8px;
96
- }
97
- .menu__link {
98
- border-radius: 8px;
99
- color: var(--jt-muted);
100
- line-height: 1.4;
101
- }
102
- .menu__link:hover {
103
- background: var(--jt-surface2);
104
- color: var(--jt-heading);
105
- }
106
- .menu__link--active:not(.menu__link--sublist) {
107
- color: var(--jt-accent);
108
- background: var(--jt-accent-soft);
109
- font-weight: 600;
110
- }
111
- .menu__list-item-collapsible .menu__link {
112
- font-weight: 600;
113
- color: var(--jt-heading);
114
- background: transparent;
115
- }
116
- .menu__list-item-collapsible .menu__link--active {
117
- color: var(--jt-accent);
118
- background: transparent;
119
- }
120
- .menu__list-item-collapsible:hover {
121
- background: var(--jt-surface2);
122
- border-radius: 8px;
123
- }
124
- .menu__caret:hover {
125
- background: transparent;
126
- }
127
-
128
- /* ---------- Table of contents ---------- */
129
- .table-of-contents__link--active {
130
- color: var(--jt-accent);
131
- font-weight: 600;
132
- }
133
- .table-of-contents .table-of-contents__link:hover {
134
- color: var(--jt-heading);
135
- }
136
-
137
- /* ---------- Pagination ---------- */
138
- .pagination-nav__link {
139
- border: 1px solid var(--jt-border);
140
- border-radius: 8px;
141
- background: var(--jt-surface);
142
- }
143
- .pagination-nav__link:hover {
144
- border-color: var(--jt-accent-border);
145
- background: var(--jt-surface2);
146
- }
147
-
148
- /* ---------- Footer ---------- */
149
- .footer,
150
- .footer--dark {
151
- --ifm-footer-background-color: var(--jt-bg2);
152
- --ifm-footer-color: var(--jt-text);
153
- --ifm-footer-link-color: var(--jt-text);
154
- --ifm-footer-title-color: var(--jt-faint);
155
- border-top: 1px solid var(--jt-border);
156
- }
157
- .footer__title {
158
- font-size: 12px;
159
- font-weight: 700;
160
- letter-spacing: 0.08em;
161
- text-transform: uppercase;
162
- color: var(--jt-faint);
163
- }
164
- .footer__link-item {
165
- color: var(--jt-text);
166
- }
167
- .footer__link-item:hover {
168
- color: var(--jt-accent);
169
- text-decoration: none;
170
- }
171
- .footer__copyright {
172
- color: var(--jt-faint);
173
- font-size: 13px;
174
- text-align: left;
175
- }
176
- .footer__col {
177
- font-size: 14px;
178
- }
179
- /* Hide the external-link "↗" SVG Docusaurus auto-appends to footer href links —
180
- the design uses clean text labels only. The class is CSS-module hashed
181
- (iconExternalLink_<hash>), so we target by attribute prefix. */
182
- .footer__link-item svg,
183
- .footer__link-item [class*="iconExternalLink"] {
184
- display: none;
185
- }
186
-
187
- /* ---------- Code ----------
188
- Every surface and text colour here comes from the theme tokens, which already
189
- flip between light and dark — the values that used to be hardcoded (#0c111b,
190
- #dfe5ee, #94a1b4) were simply the dark side of tokens that existed all along.
191
-
192
- This pairs with `prism.theme: vsLight` / `prism.darkTheme: vsDark` in
193
- docusaurus.config.ts. The two must move together (#324): vsDark tokens on a
194
- light surface are pale and unreadable, which is why the background was pinned
195
- dark in both modes in the first place. If a fenced block still renders dark in
196
- light mode, that config is the thing to check. */
197
- code {
198
- background: var(--jt-code-bg);
199
- border: 1px solid var(--jt-border);
200
- border-radius: 4px;
201
- padding: 1px 6px;
202
- font-size: var(--ifm-code-font-size);
203
- font-family: var(--ifm-font-family-monospace);
204
- }
205
- .theme-code-block,
206
- div[class*="codeBlockContainer"],
207
- pre[class*="language-"] {
208
- border: 1px solid var(--jt-border);
209
- border-radius: 12px;
210
- /* !important beats the inline background the Prism theme sets on the <pre>. */
211
- background: var(--jt-code-bg) !important;
212
- }
213
- .theme-code-block code,
214
- pre[class*="language-"] code {
215
- background: transparent !important;
216
- border: none !important;
217
- /* Base colour for anything Prism does not tokenise. */
218
- color: var(--jt-text);
219
- }
220
- div[class*="codeBlockTitle"] {
221
- background: var(--jt-surface2) !important;
222
- color: var(--jt-muted) !important;
223
- border-bottom: 1px solid var(--jt-border);
224
- }
225
- /* In dark the title sits a shade *below* the block; in light there is no token
226
- darker than --jt-code-bg, so the bottom border does the separating instead. */
227
- [data-theme="dark"] div[class*="codeBlockTitle"] {
228
- background: var(--jt-bg) !important;
229
- }
230
- [data-theme="light"] .theme-code-block,
231
- [data-theme="light"] div[class*="codeBlockContainer"],
232
- [data-theme="light"] pre[class*="language-"] {
233
- border-color: var(--jt-border2);
234
- box-shadow: 0 8px 24px -18px rgba(12, 18, 28, 0.5);
235
- }
236
-
237
- /* ---------- Markdown tables ----------
238
- Rounded container, horizontal row dividers only (no heavy cell gridlines). */
239
- .markdown table,
240
- .theme-doc-markdown table,
241
- article table {
242
- display: table;
243
- width: 100%;
244
- border-collapse: separate;
245
- border-spacing: 0;
246
- margin: 22px 0;
247
- border: 1px solid var(--jt-border);
248
- border-radius: 12px;
249
- overflow: hidden;
250
- font-size: 14px;
251
- }
252
- .markdown table thead,
253
- article table thead {
254
- background: var(--jt-bg2);
255
- }
256
- .markdown table thead tr,
257
- article table thead tr {
258
- border: none;
259
- background: transparent;
260
- }
261
- .markdown table th,
262
- article table th {
263
- text-align: left;
264
- font-weight: 700;
265
- font-size: 12px;
266
- letter-spacing: 0.06em;
267
- text-transform: uppercase;
268
- color: var(--jt-faint);
269
- border: none;
270
- border-bottom: 1px solid var(--jt-border);
271
- padding: 14px 18px;
272
- }
273
- .markdown table tbody tr,
274
- article table tbody tr {
275
- background: transparent !important;
276
- border: none;
277
- }
278
- .markdown table td,
279
- article table td {
280
- border: none;
281
- border-bottom: 1px solid var(--jt-border);
282
- padding: 13px 18px;
283
- vertical-align: middle;
284
- color: var(--jt-text);
285
- }
286
- .markdown table tbody tr:last-child td,
287
- article table tbody tr:last-child td {
288
- border-bottom: none;
289
- }
290
- .markdown table td:first-child,
291
- article table td:first-child {
292
- font-weight: 600;
293
- color: var(--jt-heading);
294
- white-space: nowrap;
295
- }
296
- .markdown table td code,
297
- .markdown table th code,
298
- article table td code {
299
- background: transparent !important;
300
- border: none !important;
301
- padding: 0 !important;
302
- font-size: 13px;
303
- }
304
- .markdown table td:nth-child(2) code,
305
- article table td:nth-child(2) code {
306
- color: var(--jt-accent);
307
- }
308
- .markdown table td:nth-child(3),
309
- article table td:nth-child(3) {
310
- color: var(--jt-muted);
311
- line-height: 1.9;
312
- }
313
- .markdown table td:nth-child(3) code,
314
- article table td:nth-child(3) code {
315
- color: var(--jt-muted);
316
- }
317
-
318
- /* ---------- Buttons ---------- */
319
- .button--primary {
320
- background: var(--jt-accent-fill);
321
- border-color: var(--jt-accent-fill);
322
- color: var(--jt-on-accent);
323
- font-weight: 700;
324
- }
325
- .button--primary:hover {
326
- background: var(--ifm-color-primary-light);
327
- border-color: var(--ifm-color-primary-light);
328
- color: var(--jt-on-accent);
329
- }
330
- .button--secondary {
331
- background: transparent;
332
- border-color: var(--jt-border2);
333
- color: var(--jt-heading);
334
- }
335
-
336
- /* ============================================================
337
- HARDENED DARK SURFACES
338
- Infima's dark defaults are grey (#1b1b1d) and the footer is
339
- slate. These explicit overrides force the navy palette even
340
- if a default loads after this file.
341
- ============================================================ */
342
- [data-theme="dark"] {
343
- --ifm-background-color: #0a0e16;
344
- --ifm-background-surface-color: #111824;
345
- --ifm-navbar-background-color: #0a0e16;
346
- --ifm-footer-background-color: #0b101a;
347
- }
348
- [data-theme="dark"],
349
- [data-theme="dark"] body,
350
- [data-theme="dark"] #__docusaurus,
351
- [data-theme="dark"] .main-wrapper,
352
- [data-theme="dark"] .navbar,
353
- [data-theme="dark"] main {
354
- background-color: #0a0e16;
355
- }
356
- /* Footer follows the theme: soft grey panel in light mode, page-matching navy
357
- in dark mode. Selectors are theme-scoped so the config's `style: 'dark'`
358
- (which adds .footer--dark in BOTH modes) can't drag the light-mode footer
359
- into a dark slate. */
360
- html[data-theme="light"] .footer,
361
- html[data-theme="light"] .footer--dark {
362
- background-color: var(--jt-bg2) !important;
363
- border-top: 1px solid var(--jt-border);
364
- }
365
- html[data-theme="light"] .footer__link-item {
366
- color: var(--jt-text) !important;
367
- }
368
- html[data-theme="light"] .footer__title {
369
- color: var(--jt-faint) !important;
370
- }
371
- html[data-theme="light"] .footer__copyright {
372
- color: var(--jt-faint) !important;
373
- }
374
- html[data-theme="dark"] .footer,
375
- html[data-theme="dark"] .footer--dark {
376
- background-color: #0a0e16 !important;
377
- border-top: 1px solid var(--jt-border);
378
- }
379
- html[data-theme="dark"] .footer__link-item {
380
- color: var(--jt-text) !important;
381
- }
382
- html[data-theme="dark"] .footer__title {
383
- color: var(--jt-faint) !important;
384
- }
385
- html[data-theme="dark"] .footer__copyright {
386
- color: var(--jt-faint) !important;
387
- }
388
- .footer__link-item:hover {
389
- color: var(--jt-accent) !important;
390
- }