Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Bulk reformats. `git config blame.ignoreRevsFile .git-blame-ignore-revs`
a484cdd7cec5851f1d65c068a7de69e21d30a388 # prettier, 14 files
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
directory: '/'
schedule:
interval: weekly
open-pull-requests-limit: 5
Expand All @@ -20,7 +20,7 @@ updates:
update-types: [minor, patch]

- package-ecosystem: github-actions
directory: "/"
directory: '/'
schedule:
interval: weekly
open-pull-requests-limit: 3
31 changes: 31 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Build output and vendored trees — formatting these is noise.
node_modules
.next
dist
build
out
coverage
.turbo
.vercel
*.min.js
*.min.css

# Generated during a build, so it is absent locally and present in CI — which
# makes a clean local --check no evidence at all. Contentlayer's output also
# uses import assertions, which prettier's parser rejects outright.
.contentlayer
.astro
.svelte-kit
storybook-static
test-results
playwright-report

# Lockfiles are generated; prettier would rewrite them wholesale.
package-lock.json
pnpm-lock.yaml
yarn.lock

# Markdown is deliberately out of scope for now. Prettier rewraps prose, which
# is where it is most opinionated and least useful, and it would bury the real
# diff. Remove this line when you want docs formatted too.
*.md
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"trailingComma": "all",
"arrowParens": "always",
"endOfLine": "lf"
}
8 changes: 4 additions & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Flat config (ESLint 9). Recommended presets only — same reasoning as
// limitkit: a small library does not need a bespoke rule set to maintain.
// The floor is "lint runs and can fail", not "lint encodes taste".
import js from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import js from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
{
Expand All @@ -21,4 +21,4 @@ export default tseslint.config(
files: ['test/**/*.js'],
languageOptions: { globals: { ...globals.node, ...globals.nodeBuiltin } },
},
)
);
23 changes: 20 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@
"lint": "eslint .",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "node --test test/*.test.js",
"verify": "npm run lint && npm run typecheck && npm run build && npm test",
"prepare": "npm run build"
"verify": "npm run format:check && npm run lint && npm run typecheck && npm run build && npm test",
"prepare": "npm run build",
"format": "prettier --write .",
"format:check": "prettier --check ."
},
"dependencies": {
"zod": "^4.0.0"
Expand All @@ -58,6 +60,7 @@
"@types/react": "^19.0.0",
"eslint": "^9.39.5",
"globals": "^15.15.0",
"prettier": "3.9.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "^5.8.2",
Expand Down
8 changes: 5 additions & 3 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ export function validateSite(
if (result.success) return { success: true, data: result.data };
return {
success: false,
errors: result.error.issues.map(issue => `${issue.path.join('.') || '(root)'}: ${issue.message}`),
errors: result.error.issues.map(
(issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`,
),
};
}

/** Nav entries in page order. Pages without a `navLabel` are omitted. */
export function siteNavItems(pages: SitePage[]): SiteNavItem[] {
return pages
.filter((page): page is SitePage & { navLabel: string } => Boolean(page.navLabel))
.map(page => ({ path: page.path, label: page.navLabel }));
.map((page) => ({ path: page.path, label: page.navLabel }));
}

/**
Expand All @@ -39,7 +41,7 @@ export function pageRendersOwnHeader(page: SitePage): boolean {
/** @returns the page at this path, or null. */
export function sitePageAt(pages: SitePage[], path: string): SitePage | null {
const normalised = path.replace(/^\/+|\/+$/g, '');
return pages.find(page => page.path === normalised) ?? null;
return pages.find((page) => page.path === normalised) ?? null;
}

/** An in-site link. Root is '/', everything else '/segment'. */
Expand Down
2 changes: 1 addition & 1 deletion src/react/SiteNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function SiteNav({ items, currentPath, Link = DefaultLink }: Props) {
aria-label="Sections"
className="scrollbar-hide -mx-1 flex flex-nowrap items-center gap-x-1 overflow-x-auto"
>
{items.map(item => {
{items.map((item) => {
const isCurrent = item.path === currentPath;
return (
<Link
Expand Down
2 changes: 1 addition & 1 deletion src/react/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ export interface LinkProps {

export type LinkLike = ComponentType<LinkProps>;

export const DefaultLink: LinkLike = props => createElement('a', props);
export const DefaultLink: LinkLike = (props) => createElement('a', props);
12 changes: 4 additions & 8 deletions src/react/sections/BusinessSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ export function FeatureSection({
<section id={section.anchor} className="scroll-mt-24">
{section.heading && <SectionHeading index={index}>{section.heading}</SectionHeading>}
<SectionBody>
<div
className={
section.image ? 'grid items-center gap-10 lg:grid-cols-2' : 'max-w-prose'
}
>
<div className={section.image ? 'grid items-center gap-10 lg:grid-cols-2' : 'max-w-prose'}>
{section.image && (
// Plain <img>: the package cannot depend on a framework's image
// component, and a generated site's images are already sized by
Expand Down Expand Up @@ -87,7 +83,7 @@ export function ContactSection({
{section.blurb && <div className="sm:pl-10">{<Blurb>{section.blurb}</Blurb>}</div>}
<SectionBody>
<div className="grid gap-6 md:grid-cols-2">
{section.locations.map(location => (
{section.locations.map((location) => (
<div
key={location.name}
className="rounded-2xl border border-subtle bg-surface-raised p-6 sm:p-8"
Expand Down Expand Up @@ -115,7 +111,7 @@ export function ContactSection({
)}
{location.hours && location.hours.length > 0 && (
<ul className="mt-4 space-y-1 text-sm text-fg-muted">
{location.hours.map(line => (
{location.hours.map((line) => (
<li key={line}>{line}</li>
))}
</ul>
Expand Down Expand Up @@ -144,7 +140,7 @@ export function FaqSection({
{section.heading && <SectionHeading index={index}>{section.heading}</SectionHeading>}
<SectionBody>
<div className="max-w-prose space-y-4">
{section.items.map(item => (
{section.items.map((item) => (
<details
key={item.question}
className="group rounded-xl border border-subtle bg-surface-raised open:border-accent"
Expand Down
7 changes: 5 additions & 2 deletions src/react/sections/DataSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,18 @@ export function TableSection({ section }: { section: Extract<SiteSection, { kind
)}
{section.blurb && <Blurb>{section.blurb}</Blurb>}
<div className="mt-4 overflow-x-auto">
<table className="w-full table-fixed border-collapse text-left" style={{ minWidth: '560px' }}>
<table
className="w-full table-fixed border-collapse text-left"
style={{ minWidth: '560px' }}
>
<colgroup>
{section.columns.map((_, i) => (
<col key={i} style={{ width: i === 0 ? '34%' : `${restWidth}%` }} />
))}
</colgroup>
<thead>
<tr className="border-b border-strong">
{section.columns.map(column => (
{section.columns.map((column) => (
<th
key={column}
scope="col"
Expand Down
2 changes: 1 addition & 1 deletion src/react/sections/FigureSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function StatsSection({
<section>
{section.heading && <SectionHeading index={index}>{section.heading}</SectionHeading>}
<dl className="mt-6 grid grid-cols-1 divide-y divide-subtle border-y border-subtle sm:grid-cols-3 sm:divide-x sm:divide-y-0">
{section.stats.map(stat => (
{section.stats.map((stat) => (
<div key={stat.label} className="px-0 py-6 sm:px-6 sm:first:pl-0">
<dt className="font-mono text-xs uppercase tracking-caps text-fg-tertiary">
{stat.label}
Expand Down
4 changes: 2 additions & 2 deletions src/react/sections/ProseSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function CardsSection({
{section.blurb && <div className="sm:pl-10">{<Blurb>{section.blurb}</Blurb>}</div>}
<SectionBody>
<div className={`grid grid-cols-1 gap-x-8 gap-y-9 sm:grid-cols-2 ${columns}`}>
{section.cards.map(card => (
{section.cards.map((card) => (
<article key={card.title} className="flex h-full flex-col border-t border-strong pt-4">
{card.icon && (
<span aria-hidden className="mb-2 text-3xl">
Expand Down Expand Up @@ -83,7 +83,7 @@ export function DefinitionsSection({
{section.blurb && <div className="sm:pl-10">{<Blurb>{section.blurb}</Blurb>}</div>}
<SectionBody>
<dl className="divide-y divide-subtle border-y border-subtle">
{section.items.map(item => (
{section.items.map((item) => (
<div key={item.term} className="grid grid-cols-1 gap-1 py-4 sm:grid-cols-3 sm:gap-6">
<dt className="text-sm font-semibold text-fg-primary">{item.term}</dt>
<dd className="text-sm leading-relaxed text-fg-secondary sm:col-span-2">
Expand Down
2 changes: 1 addition & 1 deletion src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export const siteSpecSchema = z
// actually click — checked per page, where the fragments live. Hero
// actions get the same check when they point at a fragment.
const anchors = new Set(
page.sections.flatMap(s => ('anchor' in s && s.anchor ? [s.anchor] : [])),
page.sections.flatMap((s) => ('anchor' in s && s.anchor ? [s.anchor] : [])),
);
page.sections.forEach((section, j) => {
if (section.kind !== 'hero' || !section.actions) return;
Expand Down
14 changes: 12 additions & 2 deletions test/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@ import assert from 'node:assert/strict';
import { siteNavItems, pageRendersOwnHeader, sitePageAt, href } from 'sitekit';

const PAGES = [
{ path: '', navLabel: 'Home', title: 'Home', sections: [{ kind: 'hero', statement: 'Hi', lead: [] }] },
{ path: 'menu', navLabel: 'Menu', title: 'Menu', sections: [{ kind: 'prose', paragraphs: ['x'] }] },
{
path: '',
navLabel: 'Home',
title: 'Home',
sections: [{ kind: 'hero', statement: 'Hi', lead: [] }],
},
{
path: 'menu',
navLabel: 'Menu',
title: 'Menu',
sections: [{ kind: 'prose', paragraphs: ['x'] }],
},
{ path: 'imprint', title: 'Imprint', sections: [{ kind: 'prose', paragraphs: ['x'] }] },
];

Expand Down
7 changes: 1 addition & 6 deletions test/provenance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';

import {
provenanceSchema,
inferredPaths,
notFoundPaths,
assertDeliverable,
} from 'sitekit';
import { provenanceSchema, inferredPaths, notFoundPaths, assertDeliverable } from 'sitekit';

const PROV = {
'chrome.name': { kind: 'scraped', url: 'https://cafe-beispiel.ch/' },
Expand Down
Loading