mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-04 08:04:37 +08:00
Check that the limitations partials keep one sentence per line
The Limitations page partials keep one sentence per line so edits to different sentences merge, but AGENTS.md's general Markdown rule asks for paragraphs on one line, and changes that followed it conflicted with the split text. AGENTS.md now names the partials as the exception, and limitations-partials.mjs gains a check, run by pnpm lint, that fails on a partial line holding two sentences, and a fix mode that splits such lines. The one partial that held several sentences per line is split; Markdown joins the lines, so the page renders the same.
This commit is contained in:
@@ -4,7 +4,7 @@ This is the only agent guide for the repository. Its rules apply everywhere, inc
|
||||
|
||||
## Markdown style
|
||||
|
||||
Write prose paragraphs and list items as single source lines and let editors and renderers wrap them naturally. Do not hard-wrap Markdown at a fixed column; preserve separate lines only where Markdown syntax or intentional formatting requires them, such as headings, lists, tables, blockquotes, and code blocks.
|
||||
Write prose paragraphs and list items as single source lines and let editors and renderers wrap them naturally. Do not hard-wrap Markdown at a fixed column; preserve separate lines only where Markdown syntax or intentional formatting requires them, such as headings, lists, tables, blockquotes, and code blocks. The Limitations page partials under `docs/content/limitations/` are the exception: they keep one sentence per line (see Limitations page partials).
|
||||
|
||||
## Build and test
|
||||
|
||||
@@ -196,7 +196,7 @@ The docs site is a standalone pnpm workspace under `docs/`. It uses Next.js App
|
||||
- A labeled bullet (`- **Crypto:** ...`) is `docs/content/limitations/<section>/<label>.mdx`, for example `builtin-apis/crypto.mdx`. Edit that file to change the bullet.
|
||||
- The other text of a section (paragraphs, code blocks, unlabeled lists) is `docs/content/limitations/<section>.mdx`; text before the first heading is `introduction.mdx`.
|
||||
- `<section>` and `<label>` are the heading and label text in lowercase with each run of other characters replaced by `-`.
|
||||
- Write one sentence per line; a bullet's continuation lines are indented two spaces. Markdown joins the lines, and edits to different sentences don't conflict.
|
||||
- Write one sentence per line; a bullet's continuation lines are indented two spaces. Markdown joins the lines, and edits to different sentences don't conflict. `pnpm lint` fails on a line that holds two sentences, and `node docs/scripts/limitations-partials.mjs fix` splits such lines.
|
||||
- To add a bullet, add its file and an include line at its position in `limitations.mdx`, with a blank line on each side. Consecutive bullet includes render as one list.
|
||||
- To add a section, add the heading to `limitations.mdx` and include the section's partial below it.
|
||||
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
`server.listen(path)` and the `socketPath` option of `http.request()` and `https.request()` use Unix domain sockets on Linux and macOS. On Windows, where Node maps these paths to named pipes, they fail with `ENOTSUP`. `server.address()` on a server listening on a Unix socket returns the socket path, as in Node, when the result is typed `string | AddressInfo | null` (the @types/node declaration). Where it is typed as the `{ address, family, port }` record alone, which cannot hold the path, the call throws an error for such a server. `listen(path, backlog, callback)` is refused at compile time; pass `listen(path, callback)`.
|
||||
When the options of `http.request()` come from a variable, or from an object literal that spreads one, they are read at run time with Node's validation errors. The `auth`, `createConnection`, `family`, `hints`, `insecureHTTPParser`, `joinDuplicateHeaders`, `localAddress`, `localPort`, `lookup` and `uniqueHeaders` options then throw a runtime error when they hold a value, and so do `setHost: false` and `setDefaultHeaders: false`.
|
||||
`server.listen(path)` and the `socketPath` option of `http.request()` and `https.request()` use Unix domain sockets on Linux and macOS.
|
||||
On Windows, where Node maps these paths to named pipes, they fail with `ENOTSUP`.
|
||||
`server.address()` on a server listening on a Unix socket returns the socket path, as in Node, when the result is typed `string | AddressInfo | null` (the @types/node declaration).
|
||||
Where it is typed as the `{ address, family, port }` record alone, which cannot hold the path, the call throws an error for such a server.
|
||||
`listen(path, backlog, callback)` is refused at compile time; pass `listen(path, callback)`.
|
||||
When the options of `http.request()` come from a variable, or from an object literal that spreads one, they are read at run time with Node's validation errors.
|
||||
The `auth`, `createConnection`, `family`, `hints`, `insecureHTTPParser`, `joinDuplicateHeaders`, `localAddress`, `localPort`, `lookup` and `uniqueHeaders` options then throw a runtime error when they hold a value, and so do `setHost: false` and `setDefaultHeaders: false`.
|
||||
|
||||
@@ -21,7 +21,13 @@
|
||||
* node scripts/limitations-partials.mjs split <file> [page] write the page and partials from one file
|
||||
* node scripts/limitations-partials.mjs merge <base> <theirs>
|
||||
* three-way merge a branch's single-file page (<theirs>, forked from
|
||||
* <base>) into the current partials, then split the result */
|
||||
* <base>) into the current partials, then split the result
|
||||
* node scripts/limitations-partials.mjs check fail on a partial line that holds two sentences
|
||||
* node scripts/limitations-partials.mjs fix put each such sentence on its own line
|
||||
*
|
||||
* One sentence per line keeps changes to different sentences on different
|
||||
* lines, so concurrent edits merge. Markdown joins the lines with a space,
|
||||
* so the rendered page is the same. */
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -139,6 +145,41 @@ function write(text, page = defaultPage) {
|
||||
console.error(`${relative(docsRoot, page)}: ${partials.size} partials in ${relative(docsRoot, partialsDir)}/`);
|
||||
}
|
||||
|
||||
// A sentence ends at `.`, `!` or `?` (after any closing `)`, `"` or `*`)
|
||||
// followed by a space and a capital letter, a code span, a link or bold
|
||||
// text. Abbreviations don't end one.
|
||||
const sentenceEnd = /(?<!\b(?:e\.g|i\.e|etc|vs|cf))([.!?][)"*]*) (?=[A-Z`[*])/g;
|
||||
|
||||
/** The partial text with each sentence of a prose line on its own line;
|
||||
* a bullet's sentences continue with the two-space indent. Code fences,
|
||||
* headings, tables, HTML and include lines are left alone, as are code
|
||||
* spans and link targets. */
|
||||
export function splitSentences(text) {
|
||||
let inFence = false;
|
||||
const out = [];
|
||||
for (const line of text.split("\n")) {
|
||||
if (/^\s*(```|~~~)/.test(line)) inFence = !inFence;
|
||||
if (inFence || /^\s*(```|~~~|#|<|\||$)/.test(line)) {
|
||||
out.push(line);
|
||||
continue;
|
||||
}
|
||||
const indent = /^\s*(?:[-*]|\d+\.) /.test(line) ? " " : line.match(/^\s*/)[0];
|
||||
// Mask code spans (all but the opening backtick, which can start a
|
||||
// sentence) and link targets, so their periods never split.
|
||||
const masked = line.replace(/`[^`]*`|\]\([^)]*\)/g, (m) => m[0] + "\0".repeat(m.length - 1));
|
||||
const pieces = [];
|
||||
let start = 0;
|
||||
for (const match of masked.matchAll(sentenceEnd)) {
|
||||
const end = match.index + match[1].length;
|
||||
pieces.push(line.slice(start, end));
|
||||
start = end + 1;
|
||||
}
|
||||
pieces.push(line.slice(start));
|
||||
out.push(pieces[0], ...pieces.slice(1).map((piece) => indent + piece));
|
||||
}
|
||||
return out.join("\n");
|
||||
}
|
||||
|
||||
function listPartials(dir = partialsDir) {
|
||||
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) =>
|
||||
entry.isDirectory() ? listPartials(join(dir, entry.name)) : [join(dir, entry.name)],
|
||||
@@ -175,9 +216,20 @@ if (command === "join") {
|
||||
} finally {
|
||||
rmSync(work, { recursive: true, force: true });
|
||||
}
|
||||
} else if (command === "check" || command === "fix") {
|
||||
let unsplit = 0;
|
||||
for (const file of listPartials().sort()) {
|
||||
const text = readFileSync(file, "utf8");
|
||||
const split = splitSentences(text);
|
||||
if (split === text) continue;
|
||||
unsplit++;
|
||||
if (command === "fix") writeFileSync(file, split);
|
||||
else console.error(`${relative(docsRoot, file)}: put each sentence on its own line (node docs/scripts/limitations-partials.mjs fix)`);
|
||||
}
|
||||
if (command === "check" && unsplit > 0) process.exitCode = 1;
|
||||
} else if (command === "list") {
|
||||
for (const file of listPartials().sort()) console.log(relative(docsRoot, file));
|
||||
} else {
|
||||
console.error("usage: limitations-partials.mjs join [page] | split <file> [page] | merge <base> <theirs> | list");
|
||||
console.error("usage: limitations-partials.mjs join [page] | split <file> [page] | merge <base> <theirs> | check | fix | list");
|
||||
process.exitCode = 2;
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"test:sandbox": "node scripts/sandbox-test.mjs",
|
||||
"test:sandbox:image": "node scripts/sandbox-image.mjs",
|
||||
"test:watch": "vitest",
|
||||
"lint": "oxlint --deny-warnings packages scripts internal/compatibility/src tests/harness vitest.config.ts",
|
||||
"lint": "oxlint --deny-warnings packages scripts internal/compatibility/src tests/harness vitest.config.ts && node docs/scripts/limitations-partials.mjs check",
|
||||
"lint:fix": "pnpm lint --fix",
|
||||
"format": "oxfmt --write \"{packages,scripts,internal/compatibility/src,tests/harness}/**/*.{js,mjs,cjs,ts,mts,cts,tsx}\" vitest.config.ts .oxlintrc.jsonc .oxfmtrc.jsonc",
|
||||
"format:check": "oxfmt --check \"{packages,scripts,internal/compatibility/src,tests/harness}/**/*.{js,mjs,cjs,ts,mts,cts,tsx}\" vitest.config.ts .oxlintrc.jsonc .oxfmtrc.jsonc",
|
||||
|
||||
Reference in New Issue
Block a user