🔍 fix: apply the code review — two visible regressions, and the gates behind them
Nine review angles, fourteen verified findings, all but the byte-table generator applied. The two that mattered most were regressions of mine: - a:hover repainted button-styled anchors, and in dark theme --accent-text equals --accent-solid — hovering the hero CTA drew its label in its own background colour. Verified invisible before (computed color == computed background) and distinct after: 10.39:1 dark, 6.90:1 light. The buttons and the skip link now re-assert their label colours on hover. - Footer links had lost every non-hover affordance: the text-decoration:none carve-out plus body-coloured links left a 2.37:1 shade difference as the only cue. The carve-out is deleted — .nav-links a and .brand already declare none themselves — and the accent rule is back on the footer. Also on the page: #ref-filter, the one text input, moves to --border-ui (it still had the 1.68:1 border the token's own comment calls decorative); focusable code surfaces get the --accent-on-code ring at -2px offset, inside the .code overflow clip; #output .out-err drops #ff8095, the last surviving colour of the deleted indigo palette; the two rgba(255,106,126) washes become color-mix over --err-line so a grep for the token finds them. The theme machinery loses a whole block: the dark media query is guarded with :not([data-theme="light"]), so an explicit light toggle falls through to the bare :root palette and the 21-token hand-copy in [data-theme="light"] is gone. Verified in all four system/toggle combinations. The page stops contradicting the repo: it claimed cereale/min "cannot tree-shake — nothing left to shake" while src/treeshake.test.ts proves the opposite on every run. Corrected here and in FRAMEWORKS.md, with the measured figures (1,837 vs 1,996 bytes for one decorator). The release facts the page was hand-bumping — both tgz names, "0.4.0 lives in the repository", the sixty-eight — now fill from meta.js/the bundle like the version badge always has. The workflows close three holes: - The docs sync gate was blind to NEW untracked build outputs (git diff does not report them; demonstrated). Both workflows now run check:docs-sync, one shared script that fails on anything porcelain reports — which also ends the copy-paste divergence between them. - pages.yml deploys on CI succeeding on main (workflow_run) instead of on the push itself, so a deploy implies green tests, not just in-sync docs. The deploy job refuses refs other than main, closing the workflow_dispatch any-branch deploy, and the build job drops pages/id-token — npm postinstall scripts no longer run alongside an OIDC grant. - The Junie action is pinned to the commit behind v1.7.4 rather than the tag, which is the immutability the previous comment promised but a mutable ref cannot deliver. Verified: every fix confirmed in a rendered browser in both themes; 72 contrast pairs still pass; no overflow at 20 widths; 268 tests, build, check:types, check:docs, actionlint all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
This commit is contained in:
@@ -47,7 +47,5 @@ jobs:
|
|||||||
- name: Landing page loads nothing from the network
|
- name: Landing page loads nothing from the network
|
||||||
run: npm run check:docs
|
run: npm run check:docs
|
||||||
- name: Landing page bundle is in sync with src/
|
- name: Landing page bundle is in sync with src/
|
||||||
run: |
|
# Also fails on NEW untracked files under docs/ — a plain `git diff` does not.
|
||||||
npm run build:docs
|
run: npm run check:docs-sync
|
||||||
git diff --exit-code -- docs/ \
|
|
||||||
|| (echo "docs/ is stale — run 'npm run build:docs' and commit the result" && exit 1)
|
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Review the pull request
|
- name: Review the pull request
|
||||||
# Pinned to the newest release rather than the moving v1 tag, so the version
|
# Pinned to the commit behind release v1.7.4. A tag is a mutable ref the
|
||||||
# running is the version reviewed here. Bump deliberately.
|
# publisher can repoint; only the SHA guarantees the version running is the
|
||||||
uses: JetBrains/junie-github-action@v1.7.4
|
# version that was reviewed — this workflow handles a repo secret. Bump by
|
||||||
|
# resolving the new release's commit, not by moving the tag name alone.
|
||||||
|
uses: JetBrains/junie-github-action@c2ae82fc9fbe0eb81942ceb3d9bd3f89a6b17b95 # v1.7.4
|
||||||
with:
|
with:
|
||||||
junie_api_key: ${{ secrets.JUNIE_API_KEY }}
|
junie_api_key: ${{ secrets.JUNIE_API_KEY }}
|
||||||
# Built-in structured review prompt, as opposed to a free-form instruction.
|
# Built-in structured review prompt, as opposed to a free-form instruction.
|
||||||
|
|||||||
+30
-23
@@ -4,30 +4,28 @@ name: Deploy Pages
|
|||||||
# directly, so the page is rebuilt from src/ and checked before it goes live instead
|
# directly, so the page is rebuilt from src/ and checked before it goes live instead
|
||||||
# of after.
|
# of after.
|
||||||
#
|
#
|
||||||
# REQUIRES A ONE-TIME SETTING. Settings → Pages → Build and deployment → Source must
|
# Triggered by CI completing on main rather than by the push itself, so a deploy
|
||||||
# be "GitHub Actions", not "Deploy from a branch". Until it is, the deploy job fails
|
# implies the full suite passed — type-check, lint, tests, build, entry points, docs.
|
||||||
# with "Resource not accessible by integration" — the workflow is correct, the
|
# A push that breaks a test turns main red and never reaches Pages; the previous
|
||||||
# repository is still configured to serve the branch. The switch cannot be made from
|
# wiring deployed on any docs push, green CI or not. workflow_dispatch stays as the
|
||||||
# here: it needs a token with administration:write, which GITHUB_TOKEN is not.
|
# manual escape hatch, and the deploy job refuses any ref that is not main.
|
||||||
#
|
#
|
||||||
# It is reversible. Setting Source back to a branch restores the old behaviour and
|
# REQUIRES A ONE-TIME SETTING. Settings → Pages → Build and deployment → Source must
|
||||||
# this workflow simply stops being able to deploy.
|
# be "GitHub Actions", not "Deploy from a branch". Until it is, the first run fails —
|
||||||
|
# in the build job at configure-pages if Pages was never enabled, or in the deploy
|
||||||
|
# job with "Resource not accessible by integration" if Pages still serves a branch.
|
||||||
|
# Either way the workflow is correct; the repository setting is what needs to move.
|
||||||
|
# The switch cannot be made from here: it needs administration:write, which
|
||||||
|
# GITHUB_TOKEN is not. It is reversible — setting Source back to a branch restores
|
||||||
|
# the old behaviour and this workflow simply stops being able to deploy.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_run:
|
||||||
|
workflows: [ CI ]
|
||||||
|
types: [ completed ]
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
# docs/ holds both the page and its generated bundle, so a src/ change only
|
|
||||||
# matters here once it has been rebuilt into docs/ — which is what CI enforces.
|
|
||||||
paths:
|
|
||||||
- 'docs/**'
|
|
||||||
- '.github/workflows/pages.yml'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
# Never cancel a deploy in flight: a half-published site is worse than a stale one.
|
# Never cancel a deploy in flight: a half-published site is worse than a stale one.
|
||||||
# Queue instead, so the last push wins without interrupting the one already going out.
|
# Queue instead, so the last push wins without interrupting the one already going out.
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -38,6 +36,12 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: Build and check
|
name: Build and check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# workflow_run fires on failure too — deploying is the one thing that must not.
|
||||||
|
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
|
||||||
|
# This job runs third-party code (npm postinstall scripts, the build toolchain),
|
||||||
|
# so it gets read-only. The Pages/OIDC grants live on the deploy job alone.
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
@@ -46,12 +50,10 @@ jobs:
|
|||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Rebuild the page bundle from src/
|
|
||||||
run: npm run build:docs
|
|
||||||
- name: The committed page is in sync with src/
|
- name: The committed page is in sync with src/
|
||||||
run: |
|
# Rebuilds from src/ and fails on any difference, new untracked files included.
|
||||||
git diff --exit-code -- docs/ \
|
# Same script CI runs, so the two workflows cannot drift apart.
|
||||||
|| (echo "docs/ is stale — run 'npm run build:docs' and commit the result" && exit 1)
|
run: npm run check:docs-sync
|
||||||
- name: The page loads nothing from the network
|
- name: The page loads nothing from the network
|
||||||
run: npm run check:docs
|
run: npm run check:docs
|
||||||
- uses: actions/configure-pages@v5
|
- uses: actions/configure-pages@v5
|
||||||
@@ -63,6 +65,11 @@ jobs:
|
|||||||
name: Deploy
|
name: Deploy
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# workflow_dispatch can be pointed at any branch; production only ever serves main.
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|||||||
+4
-3
@@ -331,9 +331,10 @@ await esbuild.build({
|
|||||||
`cereale/min` is the whole library flattened into one minified ES module (33.9 KB, 9.6 KB
|
`cereale/min` is the whole library flattened into one minified ES module (33.9 KB, 9.6 KB
|
||||||
gzipped) for import maps, `<script type="module">`, Deno and Workers.
|
gzipped) for import maps, `<script type="module">`, Deno and Workers.
|
||||||
|
|
||||||
**If you are using a bundler, do not use it.** It is the whole library in one file, so nothing
|
**If you are using a bundler, prefer the default entry.** The flat file keeps its
|
||||||
can be dropped from it. The default entry point tree-shakes — one decorator costs about 1.8 KB
|
`/*#__PURE__*/` annotations, so a bundler can still drop the rules you did not import — pinned
|
||||||
against 26 KB for everything — and produces a smaller result in any real application. See
|
by `src/treeshake.test.ts` — but the per-module build shakes slightly leaner (1,837 bytes
|
||||||
|
against 1,996 for one decorator through esbuild) and is the canonical route. See
|
||||||
[Bundle size](README.md#bundle-size).
|
[Bundle size](README.md#bundle-size).
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|||||||
+27
-26
@@ -27,9 +27,10 @@
|
|||||||
which already owns amber. Two amber families would make "our brand" and "needs
|
which already owns amber. Two amber families would make "our brand" and "needs
|
||||||
your attention" the same colour.
|
your attention" the same colour.
|
||||||
|
|
||||||
Every token below that differs between themes must be written in ALL FOUR places:
|
Every token below that differs between themes must be written in THREE places: this
|
||||||
this block, the prefers-color-scheme block, [data-theme="light"] and
|
block (the light palette), the prefers-color-scheme block, and [data-theme="dark"].
|
||||||
[data-theme="dark"]. Miss one and the toggle silently serves the light value. */
|
The dark media block is guarded with :not([data-theme="light"]), so an explicit light
|
||||||
|
toggle falls straight through to these values — there is no light copy to keep in sync. */
|
||||||
:root {
|
:root {
|
||||||
--bg: #faf7f0;
|
--bg: #faf7f0;
|
||||||
--bg-raised: #fffdf7;
|
--bg-raised: #fffdf7;
|
||||||
@@ -95,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root:not([data-theme="light"]) {
|
||||||
--bg: #12100b;
|
--bg: #12100b;
|
||||||
--bg-raised: #1a1710;
|
--bg-raised: #1a1710;
|
||||||
--bg-sunken: #16130d;
|
--bg-sunken: #16130d;
|
||||||
@@ -120,16 +121,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The toggle wins over the media query in both directions. */
|
/* An explicit light choice: the guarded media block above no longer matches, so the
|
||||||
|
bare :root palette wins on its own. Only the UA hint needs stating. */
|
||||||
:root[data-theme="light"] {
|
:root[data-theme="light"] {
|
||||||
--bg: #faf7f0; --bg-raised: #fffdf7; --bg-sunken: #f2ede1;
|
|
||||||
--text: #1a1712; --text-muted: #5c5346; --text-faint: #6e6353;
|
|
||||||
--border: #e6dfd1; --border-strong: #cfc5b2; --border-ui: #8e8269;
|
|
||||||
--accent: #8a6238; --accent-text: #7a5530; --accent-soft: #f1e9dc;
|
|
||||||
--accent-solid: #6b4a28; --on-accent: #fffdf7;
|
|
||||||
--bad: #a82820; --bad-soft: #faebe7; --ok: #256b3d; --ok-soft: #e8f2e9;
|
|
||||||
--warn: #8a5a05; --warn-soft: #fbf1dc;
|
|
||||||
--shadow: 0 1px 2px rgba(40, 30, 14, .05), 0 8px 24px -12px rgba(40, 30, 14, .20);
|
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
:root[data-theme="dark"] {
|
:root[data-theme="dark"] {
|
||||||
@@ -173,7 +167,6 @@ p { margin: 0 0 1rem; }
|
|||||||
--accent-text and --text-muted sit 1.14:1 apart — colour alone would lose them. */
|
--accent-text and --text-muted sit 1.14:1 apart — colour alone would lose them. */
|
||||||
a { color: var(--text); text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: .16em; }
|
a { color: var(--text); text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: .16em; }
|
||||||
a:hover { color: var(--accent-text); text-decoration-color: currentColor; }
|
a:hover { color: var(--accent-text); text-decoration-color: currentColor; }
|
||||||
.nav-links a, .foot-grid a, .brand { text-decoration: none; }
|
|
||||||
code, kbd, pre { font-family: var(--mono); }
|
code, kbd, pre { font-family: var(--mono); }
|
||||||
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
|
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
|
||||||
|
|
||||||
@@ -200,6 +193,7 @@ section { padding-block: clamp(3rem, 6vw, 5.5rem); }
|
|||||||
background: var(--accent-solid); color: var(--on-accent); padding: .6rem 1rem; border-radius: 0 0 var(--radius) 0;
|
background: var(--accent-solid); color: var(--on-accent); padding: .6rem 1rem; border-radius: 0 0 var(--radius) 0;
|
||||||
}
|
}
|
||||||
.skip:focus { left: 0; }
|
.skip:focus { left: 0; }
|
||||||
|
.skip:hover { color: var(--on-accent); }
|
||||||
.sr-only {
|
.sr-only {
|
||||||
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
||||||
overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
|
overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
|
||||||
@@ -249,9 +243,12 @@ header.nav {
|
|||||||
padding: .75rem 1.15rem; border-radius: var(--radius); text-decoration: none; cursor: pointer; border: 1px solid transparent;
|
padding: .75rem 1.15rem; border-radius: var(--radius); text-decoration: none; cursor: pointer; border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
.btn-primary { background: var(--accent-solid); color: var(--on-accent); box-shadow: var(--shadow); }
|
.btn-primary { background: var(--accent-solid); color: var(--on-accent); box-shadow: var(--shadow); }
|
||||||
.btn-primary:hover { filter: brightness(1.08); }
|
/* Re-assert label colours on hover: the base a:hover (0,1,1) outranks these classes
|
||||||
|
(0,1,0), and in dark theme --accent-text equals --accent-solid — a hovered label
|
||||||
|
painted in its own background. */
|
||||||
|
.btn-primary:hover { filter: brightness(1.08); color: var(--on-accent); }
|
||||||
.btn-secondary { background: var(--bg-raised); color: var(--text); border-color: var(--border-ui); }
|
.btn-secondary { background: var(--bg-raised); color: var(--text); border-color: var(--border-ui); }
|
||||||
.btn-secondary:hover { border-color: var(--text-faint); }
|
.btn-secondary:hover { border-color: var(--text-faint); color: var(--text); }
|
||||||
.fact-row {
|
.fact-row {
|
||||||
display: flex; flex-wrap: wrap; gap: .4rem .5rem; margin-top: 1.5rem;
|
display: flex; flex-wrap: wrap; gap: .4rem .5rem; margin-top: 1.5rem;
|
||||||
font-size: .8125rem; color: var(--text-muted);
|
font-size: .8125rem; color: var(--text-muted);
|
||||||
@@ -290,7 +287,7 @@ header.nav {
|
|||||||
|
|
||||||
/* The page's one visual device: the line the compiler refuses. */
|
/* The page's one visual device: the line the compiler refuses. */
|
||||||
.ln--error {
|
.ln--error {
|
||||||
background: rgba(255, 106, 126, .09);
|
background: color-mix(in srgb, var(--err-line) 9%, transparent);
|
||||||
text-decoration: underline wavy var(--err-line);
|
text-decoration: underline wavy var(--err-line);
|
||||||
text-decoration-skip-ink: none;
|
text-decoration-skip-ink: none;
|
||||||
text-underline-offset: .32em;
|
text-underline-offset: .32em;
|
||||||
@@ -298,7 +295,7 @@ header.nav {
|
|||||||
.tsc-error {
|
.tsc-error {
|
||||||
display: flex; gap: .6rem; align-items: flex-start;
|
display: flex; gap: .6rem; align-items: flex-start;
|
||||||
margin: 0; padding: .7rem .9rem; border-top: 1px solid var(--code-border);
|
margin: 0; padding: .7rem .9rem; border-top: 1px solid var(--code-border);
|
||||||
background: rgba(255, 106, 126, .08); color: var(--err-text);
|
background: color-mix(in srgb, var(--err-line) 8%, transparent); color: var(--err-text);
|
||||||
font: 500 .78125rem/1.5 var(--mono);
|
font: 500 .78125rem/1.5 var(--mono);
|
||||||
}
|
}
|
||||||
.tsc-error .mark { color: var(--err-line); flex-shrink: 0; }
|
.tsc-error .mark { color: var(--err-line); flex-shrink: 0; }
|
||||||
@@ -348,13 +345,16 @@ header.nav {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
#editor:focus { outline: 2px solid var(--accent-on-code); outline-offset: -2px; }
|
#editor:focus { outline: 2px solid var(--accent-on-code); outline-offset: -2px; }
|
||||||
|
/* Scrollable code samples are keyboard-focusable in Chromium; the page-level ring is
|
||||||
|
unreadable on the dark slab and its +2px offset would be clipped by .code overflow. */
|
||||||
|
.code :focus-visible, #output:focus-visible { outline: 2px solid var(--accent-on-code); outline-offset: -2px; }
|
||||||
#output {
|
#output {
|
||||||
flex: 1; min-height: 400px; margin: 0; overflow: auto;
|
flex: 1; min-height: 400px; margin: 0; overflow: auto;
|
||||||
background: var(--code-bg); color: var(--code-text); border: 1px solid var(--code-border);
|
background: var(--code-bg); color: var(--code-text); border: 1px solid var(--code-border);
|
||||||
border-top: none; border-radius: 0 0 var(--radius-lg) var(--radius-lg);
|
border-top: none; border-radius: 0 0 var(--radius-lg) var(--radius-lg);
|
||||||
padding: 1.1rem 1.15rem; font: .84375rem/1.75 var(--mono); white-space: pre-wrap; word-break: break-word;
|
padding: 1.1rem 1.15rem; font: .84375rem/1.75 var(--mono); white-space: pre-wrap; word-break: break-word;
|
||||||
}
|
}
|
||||||
#output .out-err { color: #ff8095; }
|
#output .out-err { color: var(--err-text); }
|
||||||
#output .out-dim { color: var(--code-faint); }
|
#output .out-dim { color: var(--code-faint); }
|
||||||
.pg-note { font-size: .8125rem; color: var(--text-muted); margin: .9rem 0 0; }
|
.pg-note { font-size: .8125rem; color: var(--text-muted); margin: .9rem 0 0; }
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ td.note { white-space: normal; color: var(--text-muted); font-size: .875rem; }
|
|||||||
.ref-bar { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; margin-bottom: 1.5rem; }
|
.ref-bar { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; margin-bottom: 1.5rem; }
|
||||||
#ref-filter {
|
#ref-filter {
|
||||||
flex: 1; min-width: 210px; font: .9375rem var(--sans); padding: .6rem .85rem;
|
flex: 1; min-width: 210px; font: .9375rem var(--sans); padding: .6rem .85rem;
|
||||||
border: 1px solid var(--border-strong); border-radius: var(--radius);
|
border: 1px solid var(--border-ui); border-radius: var(--radius);
|
||||||
background: var(--bg-raised); color: var(--text);
|
background: var(--bg-raised); color: var(--text);
|
||||||
}
|
}
|
||||||
#ref-count { font-size: .875rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
|
#ref-count { font-size: .875rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
|
||||||
@@ -817,7 +817,7 @@ export default defineConfig({
|
|||||||
<p class="compare-label"><span class="pill pill-bad">not on npm yet</span> installing it today</p>
|
<p class="compare-label"><span class="pill pill-bad">not on npm yet</span> installing it today</p>
|
||||||
<div class="code">
|
<div class="code">
|
||||||
<div class="code-head"><span class="name">shell</span></div>
|
<div class="code-head"><span class="name">shell</span></div>
|
||||||
<pre><code data-lang="text">git clone https://github.com/avalon-vanguard/cereale
|
<pre><code data-lang="text" id="install-shell">git clone https://github.com/avalon-vanguard/cereale
|
||||||
cd cereale
|
cd cereale
|
||||||
npm install && npm run build
|
npm install && npm run build
|
||||||
npm pack # → cereale-0.4.0.tgz
|
npm pack # → cereale-0.4.0.tgz
|
||||||
@@ -851,9 +851,10 @@ npm install ../cereale/cereale-0.4.0.tgz</code></pre>
|
|||||||
</script></code></pre>
|
</script></code></pre>
|
||||||
</div>
|
</div>
|
||||||
<p class="pg-note">
|
<p class="pg-note">
|
||||||
<strong>If you are using a bundler, do not use it.</strong> It is the whole library in one
|
<strong>If you are using a bundler, prefer the default entry.</strong> The flat file keeps
|
||||||
file, so nothing can be dropped from it. The default entry point tree-shakes; this one
|
its purity annotations, so a bundler can still drop the rules you did not import — that
|
||||||
cannot, because there is nothing left to shake.
|
is pinned by a test — but the per-module build shakes slightly leaner (1,837 bytes
|
||||||
|
against 1,996 for one decorator) and is the canonical route.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -866,7 +867,7 @@ npm install ../cereale/cereale-0.4.0.tgz</code></pre>
|
|||||||
<p class="eyebrow">Bundle size</p>
|
<p class="eyebrow">Bundle size</p>
|
||||||
<h2>You pay for the decorators you name</h2>
|
<h2>You pay for the decorators you name</h2>
|
||||||
<p class="lede">
|
<p class="lede">
|
||||||
Sixty-eight decorators is a lot to ship to a browser, so none of the ones you did not
|
<b class="js-dec-count">68</b> decorators is a lot to ship to a browser, so none of the ones you did not
|
||||||
import are shipped. Minified bytes, measured through all three bundlers.
|
import are shipped. Minified bytes, measured through all three bundlers.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -998,7 +999,7 @@ npm install ../cereale/cereale-0.4.0.tgz</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
<div class="note-item">
|
<div class="note-item">
|
||||||
<h3>It is not on npm yet</h3>
|
<h3>It is not on npm yet</h3>
|
||||||
<p>0.4.0 lives in the repository. <code class="inline-code">npm install cereale</code> does not
|
<p><span class="js-version">0.4.0</span> lives in the repository. <code class="inline-code">npm install cereale</code> does not
|
||||||
resolve to this library — build it from source until it is published.</p>
|
resolve to this library — build it from source until it is published.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,6 +39,22 @@
|
|||||||
if (versionEl && meta.version) versionEl.textContent = 'v' + meta.version;
|
if (versionEl && meta.version) versionEl.textContent = 'v' + meta.version;
|
||||||
var nodeEl = document.getElementById('node-req');
|
var nodeEl = document.getElementById('node-req');
|
||||||
if (nodeEl && meta.node) nodeEl.textContent = meta.node.replace('>=', '≥').replace('.0.0', '');
|
if (nodeEl && meta.node) nodeEl.textContent = meta.node.replace('>=', '≥').replace('.0.0', '');
|
||||||
|
if (meta.version) {
|
||||||
|
Array.prototype.forEach.call(document.querySelectorAll('.js-version'), function (el) {
|
||||||
|
el.textContent = meta.version;
|
||||||
|
});
|
||||||
|
// The install snippet names the tarball npm pack produces; keep it tied to the
|
||||||
|
// same package.json fact the badge uses instead of hand-bumping it each release.
|
||||||
|
var shell = document.getElementById('install-shell');
|
||||||
|
if (shell) {
|
||||||
|
shell.textContent = shell.textContent.replace(/cereale-[\d.]+\.tgz/g, 'cereale-' + meta.version + '.tgz');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decoratorCount) {
|
||||||
|
Array.prototype.forEach.call(document.querySelectorAll('.js-dec-count'), function (el) {
|
||||||
|
el.textContent = String(decoratorCount);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------- highlighting */
|
/* ------------------------------------------------------- highlighting */
|
||||||
var TOKENS = [
|
var TOKENS = [
|
||||||
|
|||||||
+2
-1
@@ -50,7 +50,8 @@
|
|||||||
"verify": "npm run type-check && npm run lint && npm run test && npm run build && npm run check:types && npm run check:docs",
|
"verify": "npm run type-check && npm run lint && npm run test && npm run build && npm run check:types && npm run check:docs",
|
||||||
"prepublishOnly": "npm run verify",
|
"prepublishOnly": "npm run verify",
|
||||||
"check:docs": "node scripts/check-docs.mjs",
|
"check:docs": "node scripts/check-docs.mjs",
|
||||||
"check:types": "node scripts/check-types.mjs"
|
"check:types": "node scripts/check-types.mjs",
|
||||||
|
"check:docs-sync": "npm run build:docs && node scripts/check-docs-sync.mjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Fails if docs/ differs from what `npm run build:docs` just produced — including NEW
|
||||||
|
* files. That last part is the reason this exists: `git diff --exit-code -- docs/` only
|
||||||
|
* reports modifications to tracked files, so a build-script change whose only effect is
|
||||||
|
* an additional output (a sourcemap, a second vendor asset) passed the old gate silently
|
||||||
|
* and would have been deployed without ever being committed or reviewed.
|
||||||
|
*
|
||||||
|
* Run after build:docs (the check:docs-sync npm script chains them). Shared by ci.yml
|
||||||
|
* and pages.yml so the two workflows cannot drift into enforcing different notions of
|
||||||
|
* "in sync" — they already had, before this was extracted.
|
||||||
|
*/
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
|
|
||||||
|
const out = execFileSync('git', ['status', '--porcelain', '--', 'docs/'], { encoding: 'utf8' }).trim();
|
||||||
|
|
||||||
|
if (out) {
|
||||||
|
console.error(out);
|
||||||
|
console.error("docs/ is stale — run 'npm run build:docs' and commit the result");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log('docs/ matches src/ — nothing modified, nothing untracked.');
|
||||||
Reference in New Issue
Block a user