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: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ bun run size # Size budgets (after build)
- `src/adapters/`: cmdk, base-ui, react-router and sitemap adapters
- `src/cli/`: CLI tool (scan, init, validate commands)
- `tests/`: Mirrors src/ structure
- `tests-dist/`: Tests of the built package, including the README fences marked `<!-- readme-test: ... -->`
- `tests-dist/`: Tests of the built package, including the README and GUIDE fences marked `<!-- readme-test: ... -->`
- `examples/`: Runnable apps (Vite + React Router, Next.js App Router, shadcn/ui)
- `docs/`: Next.js docs site. It runs this repo's build: `bun run build`, then `node scripts/local-build.mjs docs` (again after each build and after `bun install` in `docs/`)

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ cmdk-engine/

- Add JSDoc comments to exported functions and types
- Update docs if you change public API
- Put `<!-- readme-test: typecheck -->` on the line directly above a README code fence that should keep compiling. `bun run test:dist` type-checks it against the built package. Exactly one fence, the Quick Start, is marked `render` and also runs
- Put `<!-- readme-test: typecheck -->` on the line directly above a code fence in README.md or GUIDE.md that should keep compiling. `bun run test:dist` type-checks it against the built package. Exactly one fence, the README Quick Start, is marked `render` and also runs

## Commit Messages

Expand Down
1,310 changes: 1,310 additions & 0 deletions GUIDE.md

Large diffs are not rendered by default.

1,318 changes: 18 additions & 1,300 deletions README.md

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions docs/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import path from 'node:path'

export const dynamic = 'force-static'

/** Serves the README as plain text for tools that want the full reference in one file. */
/** Serves the README and GUIDE as plain text for tools that want the full reference in one file. */
export function GET() {
const file = path.join(process.cwd(), '..', 'README.md')
// The README's test markers are comments for the repo's tests, not content.
const text = readFileSync(file, 'utf8').replace(/<!-- readme-test: \w+ -->\r?\n/g, '')
const text = ['README.md', 'GUIDE.md']
.map((name) => readFileSync(path.join(process.cwd(), '..', name), 'utf8'))
.join('\n')
// The docs' test markers are comments for the repo's tests, not content.
.replace(/<!-- readme-test: \w+ -->\r?\n/g, '')
return new Response(text, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
})
Expand Down
2 changes: 1 addition & 1 deletion docs/components/palette-demo.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* The README's Styling CSS on the site's colors, so light and dark both work. */
/* The Styling CSS from GUIDE.md on the site's colors, so light and dark both work. */
[cmdk-overlay] {
position: fixed;
inset: 0;
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs-app-router/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* The README's Styling CSS (the same rules, formatted). */
/* The Styling CSS from GUIDE.md (the same rules, formatted). */
[cmdk-overlay] {
position: fixed;
inset: 0;
Expand Down
2 changes: 1 addition & 1 deletion examples/vite-react-router/src/palette.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CommandPalette, useCommandPaletteShortcut } from 'cmdk-engine/adapters/cmdk'

// Styled by the README's Styling CSS (styles.css).
// Styled by the Styling CSS from GUIDE.md (styles.css).
export function Palette() {
useCommandPaletteShortcut() // Cmd+K / Ctrl+K
return <CommandPalette dialog placeholder="Search pages..." />
Expand Down
2 changes: 1 addition & 1 deletion examples/vite-react-router/src/styles.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* The README's Styling CSS (the same rules, formatted). */
/* The Styling CSS from GUIDE.md (the same rules, formatted). */
[cmdk-overlay] {
position: fixed;
inset: 0;
Expand Down
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ npx cmdk-engine validate # Validate config
- [Core API](https://priyans-hu.github.io/cmdk-engine/docs/core/): registry, frecency, groups and the other framework-agnostic parts
- [Adapters](https://priyans-hu.github.io/cmdk-engine/docs/adapters/): CommandPalette props for cmdk and Base UI, scanRoutes for React Router
- [Examples](https://priyans-hu.github.io/cmdk-engine/docs/examples/): runnable apps, React Router, RBAC, a custom UI, hooks and a test
- [The README as plain text](https://priyans-hu.github.io/cmdk-engine/llms-full.txt)
- [The README and the full guide as plain text](https://priyans-hu.github.io/cmdk-engine/llms-full.txt)

## Links

Expand Down
2 changes: 1 addition & 1 deletion src/core/route-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Default route exclusion patterns shared between the runtime React Router
* adapter and the CLI scanners.
*
* Single source of truth — keeps the adapter and CLI in sync so the README's
* Single source of truth — keeps the adapter and CLI in sync so the documented
* "smart defaults" claim holds for both.
*/

Expand Down
52 changes: 29 additions & 23 deletions tests-dist/readme.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,57 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
import ts from 'typescript'
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react'

// Docs-as-test: README code fences marked with an HTML comment on the line before
// them run against the BUILT package. `<!-- readme-test: typecheck -->` type-checks a
// fence; `<!-- readme-test: render -->` also renders it. Neither shows on GitHub or npm.
// Docs-as-test: code fences in README.md and GUIDE.md marked with an HTML comment on
// the line before them run against the BUILT package. `<!-- readme-test: typecheck -->`
// type-checks a fence; `<!-- readme-test: render -->` also renders it. Neither shows on
// GitHub or npm.

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const README = readFileSync(path.join(ROOT, 'README.md'), 'utf8')
/** The docs whose marked fences run here. The README has the one `render` fence. */
const DOCS = ['README.md', 'GUIDE.md']
// Outside lint's reach, and resolves `react` from the repo's node_modules.
const CACHE_DIR = path.join(ROOT, 'node_modules', '.cache', 'cmdk-engine-readme-test')

interface Fence {
mode: 'render' | 'typecheck'
/** README line of the fence's first code line */
/** The doc the fence is in, such as `GUIDE.md` */
file: string
/** Line of the fence's first code line in that doc */
line: number
code: string
}

const MARKER = /<!-- readme-test: (render|typecheck) -->/g
const MARKED_FENCE = /<!-- readme-test: (render|typecheck) -->\r?\n```tsx?\r?\n([\s\S]*?)\r?\n```/g

function lineOf(index: number) {
return README.slice(0, index).split('\n').length
}

function markedFences(): Fence[] {
const fences = Array.from(README.matchAll(MARKED_FENCE), (m) => ({
function markedFences(file: string): Fence[] {
const text = readFileSync(path.join(ROOT, file), 'utf8')
const lineOf = (index: number) => text.slice(0, index).split('\n').length
const fences = Array.from(text.matchAll(MARKED_FENCE), (m) => ({
mode: m[1] as Fence['mode'],
file,
line: lineOf(m.index) + 2,
code: m[2],
}))
// A marker that does not sit directly above a ts/tsx fence would be skipped silently.
const markers = Array.from(README.matchAll(MARKER), (m) => lineOf(m.index))
const markers = Array.from(text.matchAll(MARKER), (m) => lineOf(m.index))
const used = new Set(fences.map((f) => f.line - 2))
const stray = markers.filter((line) => !used.has(line))
if (stray.length > 0) {
throw new Error(
`README.md: readme-test marker not directly above a ts/tsx fence, line ${stray}`,
)
throw new Error(`${file}: readme-test marker not directly above a ts/tsx fence, line ${stray}`)
}
return fences
}

/** `README.md` + line 85 -> `readme-line-85`, unique across the docs */
function fenceName(fence: Fence) {
return `${path.parse(fence.file).name.toLowerCase()}-line-${fence.line}`
}

/** Type-checks the fences as in-memory files inside the package, so `cmdk-engine/*` resolves to dist/. */
function typeErrors(fences: Fence[]): string[] {
const files = new Map(
fences.map((f) => [path.join(ROOT, 'tests-dist', `readme-line-${f.line}.tsx`), f]),
fences.map((f) => [path.join(ROOT, 'tests-dist', `${fenceName(f)}.tsx`), f]),
)
const options: ts.CompilerOptions = {
strict: true,
Expand Down Expand Up @@ -80,15 +86,15 @@ function typeErrors(fences: Fence[]): string[] {
const fence = d.file && files.get(d.file.fileName)
if (!fence || d.start === undefined) return message
const { line } = d.file!.getLineAndCharacterOfPosition(d.start)
return `README.md:${fence.line + line}: ${message}`
return `${fence.file}:${fence.line + line}: ${message}`
})
}

/** `cmdk-engine/react` -> the file package.json `exports` gives an ESM importer. */
function resolveExport(specifier: string): string {
const pkg = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
const target = pkg.exports['.' + specifier.slice('cmdk-engine'.length)]?.import?.default
if (!target) throw new Error(`README imports '${specifier}', which package.json does not export`)
if (!target) throw new Error(`A doc imports '${specifier}', which package.json does not export`)
return pathToFileURL(path.join(ROOT, target)).href
}

Expand All @@ -106,7 +112,7 @@ async function importFence(fence: Fence) {
(_, quote: string, specifier: string) => `from ${quote}${resolveExport(specifier)}${quote}`,
)
mkdirSync(CACHE_DIR, { recursive: true })
const file = path.join(CACHE_DIR, `readme-line-${fence.line}.mjs`)
const file = path.join(CACHE_DIR, `${fenceName(fence)}.mjs`)
writeFileSync(file, code)
return import(/* @vite-ignore */ pathToFileURL(file).href)
}
Expand All @@ -127,11 +133,11 @@ beforeAll(() => {

afterAll(() => rmSync(CACHE_DIR, { recursive: true, force: true }))

describe('README fences against the built package', () => {
const fences = markedFences()
describe('README and GUIDE fences against the built package', () => {
const fences = DOCS.flatMap(markedFences)

it('marks the Quick Start for rendering', () => {
expect(fences.filter((f) => f.mode === 'render')).toHaveLength(1)
it('marks the README Quick Start for rendering', () => {
expect(fences.filter((f) => f.mode === 'render').map((f) => f.file)).toEqual(['README.md'])
})

it('type-checks every marked fence', () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/adapters/listbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe.each([
})

it('stays "Suggestions" when the translation echoes the key', () => {
// The README pattern: `(key) => dictionary[key] ?? key`, for a dictionary without the key
// The GUIDE's pattern: `(key) => dictionary[key] ?? key`, for a dictionary without the key
const echo: TranslationFn = (key) => (key === 'palette.list' ? key : english(key))
renderPalette({ t: echo })
expect(listName()).toBe('Suggestions')
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/config-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('TS config: same result as the 0.5.1 parser for every config it accepte
})

describe('TS config: static values the 0.5.1 parser ignored', () => {
it('loads the README example, RegExp included', async () => {
it('loads the GUIDE example, RegExp included', async () => {
const file = write(
'readme.config.ts',
`// cmdk-engine.config.ts
Expand Down
2 changes: 1 addition & 1 deletion tests/react/synonym-query-expansion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {

type Load = AsyncSource['load']

// The README Quick Start config and command.
// The GUIDE's Synonyms config, for the README Quick Start's command.
const README_SYNONYMS = {
billing: ['money', 'payment', 'credits'],
settings: ['preferences', 'config', 'options'],
Expand Down
Loading