From e9847be601848acced251d3c8ee95f300602cc3f Mon Sep 17 00:00:00 2001 From: Ole Kristian Losvik Date: Sat, 25 Jul 2026 19:21:38 +0200 Subject: [PATCH] fix(cli): `build` asks before scaffolding a config instead of writing silently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `lectio build` no longer writes a docs.config.ts on its own when none exists — it prompts when interactive and errors in CI, so a build never mutates the repo by surprise. `lectio dev` still auto-scaffolds, as the try-it-out on-ramp. Refines the still-unpublished lectio dev feature (updates its changeset). Co-Authored-By: Claude Opus 4.8 --- .changeset/lectio-dev.md | 7 +++-- apps/site-builder/bin/lectio.mjs | 47 ++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/.changeset/lectio-dev.md b/.changeset/lectio-dev.md index e948cf1..b2e2c13 100644 --- a/.changeset/lectio-dev.md +++ b/.changeset/lectio-dev.md @@ -5,6 +5,7 @@ Add `lectio dev` and zero-config scaffolding. `lectio dev` materializes the site app and runs `react-router dev` (a local dev -server with HMR on the UI); content is collected once at startup. When no -`docs.config` exists, both `dev` and `build` scaffold a starter `docs.config.ts` -(`**/*.md`) and run with it, so a fresh repo shows a demo immediately. +server with HMR on the UI); content is collected once at startup. With no +`docs.config`, `dev` scaffolds a starter `docs.config.ts` (`**/*.md`) so a fresh +repo shows a demo immediately; `build` asks first when interactive and errors in +CI rather than writing files silently. diff --git a/apps/site-builder/bin/lectio.mjs b/apps/site-builder/bin/lectio.mjs index 4a362f0..c2046ad 100644 --- a/apps/site-builder/bin/lectio.mjs +++ b/apps/site-builder/bin/lectio.mjs @@ -5,8 +5,9 @@ * lectio build collect docs → a static, searchable site in ./dist * lectio dev the same, but serve it locally with a dev server (HMR) * - * Both read a docs.config.{ts,js,mjs} from the current directory; if none - * exists, a starter one is scaffolded so a fresh repo shows a demo right away. + * Both read a docs.config.{ts,js,mjs} from the current directory. With none, + * `dev` scaffolds a starter one (fresh repo → instant demo); `build` asks first + * when interactive, and errors in CI rather than writing files silently. * * How it works: * 1. collect the configured sources (manifest + markdown) + a search index @@ -24,6 +25,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync, symli import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; +import { createInterface } from 'node:readline/promises'; import { fileURLToPath, pathToFileURL } from 'node:url'; const require = createRequire(import.meta.url); @@ -41,21 +43,42 @@ let configPath = ['docs.config.ts', 'docs.config.js', 'docs.config.mjs'] .map((name) => resolve(cwd, name)) .find((candidate) => existsSync(candidate)); if (!configPath) { - // Zero-config: write a starter config and run with it, so `lectio dev` in a - // fresh repo shows a demo immediately. `**/*.md` sweeps the whole tree — - // collect already skips node_modules, dist, .next and dotfiles. - configPath = resolve(cwd, 'docs.config.ts'); - writeFileSync( - configPath, - `// Created by lectio — edit to taste, then re-run. + // `**/*.md` sweeps the whole tree — collect already skips node_modules, + // dist, .next and dotfiles. + const starter = `// Created by lectio — edit to taste, then re-run. export default { output: '.lectio', sources: [{ glob: '**/*.md', target: '/' }], site: { title: 'Docs' }, }; -`, - ); - console.log('No docs config found — wrote a starter docs.config.ts\n'); +`; + const writeStarter = () => { + configPath = resolve(cwd, 'docs.config.ts'); + writeFileSync(configPath, starter); + console.log('Wrote a starter docs.config.ts\n'); + }; + + if (command === 'dev') { + // dev is the on-ramp: scaffold and go, so a fresh repo shows a demo. + console.log('No docs config found — scaffolding a starter one.'); + writeStarter(); + } else if (process.stdin.isTTY) { + // build writes to the repo only with consent — ask when interactive. + const rl = createInterface({ input: process.stdin, output: process.stdout }); + const answer = (await rl.question('No docs config found. Create a starter docs.config.ts? [y/N] ')) + .trim() + .toLowerCase(); + rl.close(); + if (answer !== 'y' && answer !== 'yes') { + console.error('Aborted — nothing to build from.'); + process.exit(1); + } + writeStarter(); + } else { + // Non-interactive (CI): never write files silently. + console.error('No docs.config.{ts,js,mjs} found. Run `lectio dev` to scaffold one, or add docs.config.ts.'); + process.exit(1); + } } const config = (await import(pathToFileURL(configPath).href)).default;