lint(oc-path): satisfy curly + unused-import rules

This commit is contained in:
Gio Della-Libera
2026-05-08 18:53:22 -07:00
committed by Peter Steinberger
parent ac9418d206
commit 080e34a2c2
10 changed files with 199 additions and 200 deletions

View File

@@ -63,13 +63,13 @@ const defaultRuntime: OutputRuntimeEnv = {
// Defense-in-depth: replace the redaction sentinel with `[REDACTED]`
// before writing, even if upstream emits it.
export function scrubSentinel(s: string): string {
if (!s.includes(REDACTED_SENTINEL)) return s;
if (!s.includes(REDACTED_SENTINEL)) {return s;}
return s.split(REDACTED_SENTINEL).join(SCRUB_PLACEHOLDER);
}
function detectMode(options: PathCommandOptions): OutputMode {
if (options.json === true) return "json";
if (options.human === true) return "human";
if (options.json === true) {return "json";}
if (options.human === true) {return "human";}
return process.stdout.isTTY ? "human" : "json";
}
@@ -157,8 +157,8 @@ function catchSentinel<T>(
async function loadAst(absPath: string, fileName: string): Promise<OcAst> {
const raw = await fs.readFile(absPath, "utf-8");
const kind = inferKind(fileName);
if (kind === "jsonc") return parseJsonc(raw).ast;
if (kind === "jsonl") return parseJsonl(raw).ast;
if (kind === "jsonc") {return parseJsonc(raw).ast;}
if (kind === "jsonl") {return parseJsonl(raw).ast;}
return parseMd(raw).ast;
}
@@ -176,7 +176,7 @@ function emitForKind(ast: OcAst, fileName?: string): string {
}
function resolveFsPath(path: OcPath, options: PathCommandOptions): string {
if (options.file !== undefined) return resolvePath(options.file);
if (options.file !== undefined) {return resolvePath(options.file);}
return resolvePath(options.cwd ?? process.cwd(), path.file);
}
@@ -184,7 +184,7 @@ function formatMatchHuman(match: OcMatch): string {
if (match.kind === "leaf") {
return `leaf @ L${match.line}: ${JSON.stringify(match.valueText)} (${match.leafType})`;
}
if (match.kind === "node") return `node @ L${match.line} [${match.descriptor}]`;
if (match.kind === "node") {return `node @ L${match.line} [${match.descriptor}]`;}
if (match.kind === "insertion-point") {
return `insertion-point @ L${match.line} [${match.container}]`;
}
@@ -199,9 +199,9 @@ export async function pathResolveCommand(
runtime: OutputRuntimeEnv,
): Promise<void> {
const mode = detectMode(options);
if (!requireArg(pathStr, "resolve: missing <oc-path> argument", runtime, mode)) return;
if (!requireArg(pathStr, "resolve: missing <oc-path> argument", runtime, mode)) {return;}
const ocPath = tryParse(pathStr, runtime, mode);
if (ocPath === null) return;
if (ocPath === null) {return;}
const ast = await loadAst(resolveFsPath(ocPath, options), ocPath.file);
let match: OcMatch | null;
try {
@@ -230,15 +230,15 @@ export async function pathSetCommand(
runtime: OutputRuntimeEnv,
): Promise<void> {
const mode = detectMode(options);
if (!requireArg(pathStr, "set: requires <oc-path> <value>", runtime, mode)) return;
if (!requireArg(value, "set: requires <oc-path> <value>", runtime, mode)) return;
if (!requireArg(pathStr, "set: requires <oc-path> <value>", runtime, mode)) {return;}
if (!requireArg(value, "set: requires <oc-path> <value>", runtime, mode)) {return;}
const ocPath = tryParse(pathStr, runtime, mode);
if (ocPath === null) return;
if (ocPath === null) {return;}
const fsPath = resolveFsPath(ocPath, options);
const ast = await loadAst(fsPath, ocPath.file);
const result = catchSentinel("set", runtime, mode, () => setOcPath(ast, ocPath, value));
if (result === null) return;
if (result === null) {return;}
if (!result.ok) {
const detail = "detail" in result ? result.detail : undefined;
emit(
@@ -254,7 +254,7 @@ export async function pathSetCommand(
const newBytes = catchSentinel("emit", runtime, mode, () =>
emitForKind(result.ast, ocPath.file),
);
if (newBytes === null) return;
if (newBytes === null) {return;}
if (options.dryRun === true) {
emit(
@@ -280,9 +280,9 @@ export async function pathFindCommand(
runtime: OutputRuntimeEnv,
): Promise<void> {
const mode = detectMode(options);
if (!requireArg(patternStr, "find: missing <pattern> argument", runtime, mode)) return;
if (!requireArg(patternStr, "find: missing <pattern> argument", runtime, mode)) {return;}
const pattern = tryParse(patternStr, runtime, mode);
if (pattern === null) return;
if (pattern === null) {return;}
// File-slot wildcards would silently ENOENT during readFile; reject.
if (/[*?]/.test(pattern.file)) {
emitError(
@@ -306,7 +306,7 @@ export async function pathFindCommand(
matches: matches.map((m) => ({ path: formatOcPath(m.path), match: m.match })),
},
() => {
if (matches.length === 0) return `0 matches for ${patternStr}`;
if (matches.length === 0) {return `0 matches for ${patternStr}`;}
const plural = matches.length === 1 ? "" : "es";
const lines = [`${matches.length} match${plural} for ${patternStr}:`];
for (const m of matches) {
@@ -315,7 +315,7 @@ export async function pathFindCommand(
return lines.join("\n");
},
);
if (matches.length === 0) runtime.exit(1);
if (matches.length === 0) {runtime.exit(1);}
}
export function pathValidateCommand(
@@ -324,7 +324,7 @@ export function pathValidateCommand(
runtime: OutputRuntimeEnv,
): void {
const mode = detectMode(options);
if (!requireArg(pathStr, "validate: missing <oc-path> argument", runtime, mode)) return;
if (!requireArg(pathStr, "validate: missing <oc-path> argument", runtime, mode)) {return;}
try {
const ocPath = parseOcPath(pathStr);
emit(
@@ -344,10 +344,10 @@ export function pathValidateCommand(
},
() => {
const lines = [`valid: ${pathStr}`, ` file: ${ocPath.file}`];
if (ocPath.section !== undefined) lines.push(` section: ${ocPath.section}`);
if (ocPath.item !== undefined) lines.push(` item: ${ocPath.item}`);
if (ocPath.field !== undefined) lines.push(` field: ${ocPath.field}`);
if (ocPath.session !== undefined) lines.push(` session: ${ocPath.session}`);
if (ocPath.section !== undefined) {lines.push(` section: ${ocPath.section}`);}
if (ocPath.item !== undefined) {lines.push(` item: ${ocPath.item}`);}
if (ocPath.field !== undefined) {lines.push(` field: ${ocPath.field}`);}
if (ocPath.session !== undefined) {lines.push(` session: ${ocPath.session}`);}
return lines.join("\n");
},
);
@@ -372,7 +372,7 @@ export async function pathEmitCommand(
runtime: OutputRuntimeEnv,
): Promise<void> {
const mode = detectMode(options);
if (!requireArg(fileArg, "emit: missing <file> argument", runtime, mode)) return;
if (!requireArg(fileArg, "emit: missing <file> argument", runtime, mode)) {return;}
const fsPath =
options.file !== undefined
? resolvePath(options.file)
@@ -380,7 +380,7 @@ export async function pathEmitCommand(
const fileName = fsPath.split(/[\\/]/).pop() ?? fileArg;
const ast = await loadAst(fsPath, fileName);
const bytes = catchSentinel("emit", runtime, mode, () => emitForKind(ast, fileName));
if (bytes === null) return;
if (bytes === null) {return;}
if (mode === "json") {
runtime.writeStdout(scrubSentinel(JSON.stringify({ ok: true, kind: ast.kind, bytes })));
return;

View File

@@ -26,11 +26,11 @@ export function setMdOcPath(ast: MdAst, path: OcPath, newValue: string): MdEditR
guardSentinel(newValue, formatOcPath(path));
if (path.section === "[frontmatter]") {
const key = path.item ?? path.field;
if (key === undefined) return { ok: false, reason: "unresolved" };
if (key === undefined) {return { ok: false, reason: "unresolved" };}
const idx = ast.frontmatter.findIndex((e) => e.key === key);
if (idx === -1) return { ok: false, reason: "unresolved" };
if (idx === -1) {return { ok: false, reason: "unresolved" };}
const existing = ast.frontmatter[idx];
if (existing === undefined) return { ok: false, reason: "unresolved" };
if (existing === undefined) {return { ok: false, reason: "unresolved" };}
const newEntry: FrontmatterEntry = { ...existing, value: newValue };
const newFm = ast.frontmatter.slice();
newFm[idx] = newEntry;
@@ -43,16 +43,16 @@ export function setMdOcPath(ast: MdAst, path: OcPath, newValue: string): MdEditR
const sectionSlug = path.section.toLowerCase();
const blockIdx = ast.blocks.findIndex((b) => b.slug === sectionSlug);
if (blockIdx === -1) return { ok: false, reason: "unresolved" };
if (blockIdx === -1) {return { ok: false, reason: "unresolved" };}
const block = ast.blocks[blockIdx];
if (block === undefined) return { ok: false, reason: "unresolved" };
if (block === undefined) {return { ok: false, reason: "unresolved" };}
const itemSlug = path.item.toLowerCase();
const itemIdx = block.items.findIndex((i) => i.slug === itemSlug);
if (itemIdx === -1) return { ok: false, reason: "unresolved" };
if (itemIdx === -1) {return { ok: false, reason: "unresolved" };}
const item = block.items[itemIdx];
if (item === undefined) return { ok: false, reason: "unresolved" };
if (item.kv === undefined) return { ok: false, reason: "no-item-kv" };
if (item === undefined) {return { ok: false, reason: "unresolved" };}
if (item.kv === undefined) {return { ok: false, reason: "no-item-kv" };}
if (item.kv.key.toLowerCase() !== path.field.toLowerCase()) {
return { ok: false, reason: "unresolved" };
}
@@ -78,9 +78,9 @@ function rebuildBlockBody(block: AstBlock, newItems: readonly AstItem[]): string
for (let i = 0; i < newItems.length; i++) {
const newItem = newItems[i];
const oldItem = block.items[i];
if (newItem === undefined || oldItem === undefined) continue;
if (newItem.kv === undefined || oldItem.kv === undefined) continue;
if (newItem.kv.value === oldItem.kv.value) continue;
if (newItem === undefined || oldItem === undefined) {continue;}
if (newItem.kv === undefined || oldItem.kv === undefined) {continue;}
if (newItem.kv.value === oldItem.kv.value) {continue;}
const re = new RegExp(`^(\\s*-\\s*${escapeRegex(oldItem.kv.key)}\\s*:\\s*).*$`, "m");
body = body.replace(re, `$1${newItem.kv.value}`);
}
@@ -101,19 +101,19 @@ function finalize(ast: MdAst): MdEditResult {
parts.push("---");
}
if (ast.preamble.length > 0) {
if (parts.length > 0) parts.push("");
if (parts.length > 0) {parts.push("");}
parts.push(ast.preamble);
}
for (const block of ast.blocks) {
if (parts.length > 0) parts.push("");
if (parts.length > 0) {parts.push("");}
parts.push(`## ${block.heading}`);
if (block.bodyText.length > 0) parts.push(block.bodyText);
if (block.bodyText.length > 0) {parts.push(block.bodyText);}
}
return { ok: true, ast: { ...ast, raw: parts.join("\n") } };
}
function formatFrontmatterValue(value: string): string {
if (value.length === 0) return '""';
if (/[:#&*?|<>=!%@`,[\]{}\r\n]/.test(value)) return JSON.stringify(value);
if (value.length === 0) {return '""';}
if (/[:#&*?|<>=!%@`,[\]{}\r\n]/.test(value)) {return JSON.stringify(value);}
return value;
}

View File

@@ -129,9 +129,9 @@ function repackSlotSubs(pattern: OcPath, slotSubs: readonly SlotSub[]): OcPath {
const itemSubs: string[] = [];
const fieldSubs: string[] = [];
for (const s of slotSubs) {
if (s.slot === "section") sectionSubs.push(s.value);
else if (s.slot === "item") itemSubs.push(s.value);
else fieldSubs.push(s.value);
if (s.slot === "section") {sectionSubs.push(s.value);}
else if (s.slot === "item") {itemSubs.push(s.value);}
else {fieldSubs.push(s.value);}
}
return {
file: pattern.file,
@@ -176,7 +176,7 @@ function dispatchSeg<T>(
if (isUnionSeg(cur.value)) {
const alts = parseUnionSeg(cur.value);
if (alts === null) return;
if (alts === null) {return;}
for (const alt of alts) {
const altSubs = subs.slice();
altSubs[i] = { slot: cur.slot, value: alt };
@@ -187,7 +187,7 @@ function dispatchSeg<T>(
if (isPredicateSeg(cur.value)) {
const pred = parsePredicateSeg(cur.value);
if (pred === null) return;
if (pred === null) {return;}
for (const m of ops.predicate(node, pred)) {
ops.walk(m.child, subs, i + 1, [...walked, { slot: cur.slot, value: m.keySub }], onMatch);
}
@@ -197,7 +197,7 @@ function dispatchSeg<T>(
if (cur.value === WILDCARD_RECURSIVE) {
// `**` — descend with `**` consumed (i+1) AND retained (i) so
// deeper structures still match. Emit if no subs remain.
if (i + 1 >= subs.length) onMatch(walked);
if (i + 1 >= subs.length) {onMatch(walked);}
for (const m of ops.enumerate(node)) {
const nextWalked: readonly SlotSub[] = [...walked, { slot: cur.slot, value: m.keySub }];
ops.walk(m.child, subs, i + 1, nextWalked, onMatch);
@@ -215,13 +215,13 @@ function dispatchSeg<T>(
if (isPositionalSeg(cur.value)) {
const m = ops.positional(node, cur.value);
if (m === null) return;
if (m === null) {return;}
ops.walk(m.child, subs, i + 1, [...walked, { slot: cur.slot, value: m.keySub }], onMatch);
return;
}
const m = ops.lookup(node, cur.value);
if (m === null) return;
if (m === null) {return;}
ops.walk(m.child, subs, i + 1, [...walked, { slot: cur.slot, value: m.keySub }], onMatch);
}
@@ -245,7 +245,7 @@ function walkJsonc(
const jsoncOps: WalkOps<JsoncValue> = {
*enumerate(node) {
if (node.kind === "object") {
for (const e of node.entries) yield { keySub: quoteSeg(e.key), child: e.value };
for (const e of node.entries) {yield { keySub: quoteSeg(e.key), child: e.value };}
} else if (node.kind === "array") {
for (let idx = 0; idx < node.items.length; idx++) {
yield { keySub: String(idx), child: node.items[idx] };
@@ -262,14 +262,14 @@ const jsoncOps: WalkOps<JsoncValue> = {
}
if (node.kind === "array") {
const idx = Number(key);
if (!Number.isInteger(idx) || idx < 0 || idx >= node.items.length) return null;
if (!Number.isInteger(idx) || idx < 0 || idx >= node.items.length) {return null;}
return { keySub: key, child: node.items[idx] };
}
return null;
},
positional(node, seg) {
const concrete = positionalForJsoncNode(node, seg);
if (concrete === null) return null;
if (concrete === null) {return null;}
return jsoncOps.lookup(node, concrete);
},
*predicate(node, pred) {
@@ -325,12 +325,12 @@ function walkJsonl(
const jsonlOps: WalkOps<JsonlAst> = {
*enumerate(ast) {
for (const l of ast.lines) {
if (l.kind === "value") yield { keySub: `L${l.line}`, child: lineHolder(ast, l) };
if (l.kind === "value") {yield { keySub: `L${l.line}`, child: lineHolder(ast, l) };}
}
},
lookup(ast, key) {
const line = pickLine(ast, key);
if (line === null) return null;
if (line === null) {return null;}
const concreteAddr = line.kind === "value" ? `L${line.line}` : key;
return { keySub: concreteAddr, child: lineHolder(ast, line) };
},
@@ -339,7 +339,7 @@ const jsonlOps: WalkOps<JsonlAst> = {
},
*predicate(ast, pred) {
for (const l of ast.lines) {
if (l.kind !== "value") continue;
if (l.kind !== "value") {continue;}
const actual = topLevelLeafText(l.value, pred.key);
if (evaluatePredicate(actual, pred)) {
yield { keySub: `L${l.line}`, child: lineHolder(ast, l) };
@@ -359,7 +359,7 @@ const jsonlOps: WalkOps<JsonlAst> = {
onMatch(walked);
return;
}
if (line.kind !== "value") return;
if (line.kind !== "value") {return;}
walkJsonc(line.value, subs, i, walked, onMatch);
},
};
@@ -382,12 +382,12 @@ function unwrapHolder(holder: JsonlAst): JsonlLine | null {
}
function topLevelLeafText(value: JsoncValue, key: string): string | null {
if (value.kind !== "object") return null;
if (value.kind !== "object") {return null;}
const entry = value.entries.find((e) => e.key === key);
if (entry === undefined) return null;
if (entry === undefined) {return null;}
const v = entry.value;
if (v.kind === "string") return v.value;
if (v.kind === "number" || v.kind === "boolean") return String(v.value);
if (v.kind === "string") {return v.value;}
if (v.kind === "number" || v.kind === "boolean") {return String(v.value);}
return null;
}
@@ -395,15 +395,15 @@ function pickLine(ast: JsonlAst, addr: string): JsonlLine | null {
if (addr === "$last") {
for (let i = ast.lines.length - 1; i >= 0; i--) {
const l = ast.lines[i];
if (l !== undefined && l.kind === "value") return l;
if (l !== undefined && l.kind === "value") {return l;}
}
return null;
}
const m = /^L(\d+)$/.exec(addr);
if (m === null || m[1] === undefined) return null;
if (m === null || m[1] === undefined) {return null;}
const target = Number(m[1]);
for (const l of ast.lines) {
if (l.line === target) return l;
if (l.line === target) {return l;}
}
return null;
}
@@ -449,7 +449,7 @@ function walkMd(
}
const fmKey = isQuotedSeg(next.value) ? unquoteSeg(next.value) : next.value;
const entry = level.ast.frontmatter.find((e) => e.key === fmKey);
if (entry === undefined) return;
if (entry === undefined) {return;}
onMatch([
{ slot: cur.slot, value: cur.value },
{ slot: next.slot, value: next.value },
@@ -472,34 +472,34 @@ function walkMdItemField(
walked: readonly SlotSub[],
onMatch: OnMatch,
): void {
if (item.kv === undefined) return;
if (item.kv === undefined) {return;}
const key = item.kv.key;
const emit = (value: string): void => {
onMatch([...walked, { slot: cur.slot, value }]);
};
if (isUnionSeg(cur.value)) {
const alts = parseUnionSeg(cur.value);
if (alts === null) return;
if (alts === null) {return;}
for (const alt of alts) {
if (alt.toLowerCase() === key.toLowerCase()) emit(key);
if (alt.toLowerCase() === key.toLowerCase()) {emit(key);}
}
return;
}
if (isPredicateSeg(cur.value)) {
const pred = parsePredicateSeg(cur.value);
if (pred !== null && mdItemMatchesPredicate(item, pred)) emit(key);
if (pred !== null && mdItemMatchesPredicate(item, pred)) {emit(key);}
return;
}
if (cur.value === WILDCARD_SINGLE || cur.value === WILDCARD_RECURSIVE) {
emit(key);
return;
}
if (key.toLowerCase() === cur.value.toLowerCase()) emit(cur.value);
if (key.toLowerCase() === cur.value.toLowerCase()) {emit(cur.value);}
}
function blockSlugCounts(items: readonly MdItem[]): Map<string, number> {
const counts = new Map<string, number>();
for (const item of items) counts.set(item.slug, (counts.get(item.slug) ?? 0) + 1);
for (const item of items) {counts.set(item.slug, (counts.get(item.slug) ?? 0) + 1);}
return counts;
}
@@ -534,7 +534,7 @@ const mdOps: WalkOps<MdLevel> = {
// Ordinal `#N` short-circuits slug lookup.
if (isOrdinalSeg(key)) {
const n = parseOrdinalSeg(key);
if (n === null || n < 0 || n >= level.block.items.length) return null;
if (n === null || n < 0 || n >= level.block.items.length) {return null;}
return { keySub: key, child: { kind: "item", item: level.block.items[n], ast: level.ast } };
}
const target = key.toLowerCase();
@@ -544,12 +544,12 @@ const mdOps: WalkOps<MdLevel> = {
return null;
},
positional(level, seg) {
if (level.kind !== "block") return null;
if (level.kind !== "block") {return null;}
const concrete = resolvePositionalSeg(seg, {
indexable: true,
size: level.block.items.length,
});
if (concrete === null) return null;
if (concrete === null) {return null;}
// Preserve the positional token in keySub so the resolver
// re-evaluates positionally on round-trip.
const item = level.block.items[Number(concrete)];
@@ -579,14 +579,14 @@ const mdOps: WalkOps<MdLevel> = {
};
function mdItemMatchesPredicate(item: MdItem, pred: PredicateSpec): boolean {
if (item.kv === undefined) return false;
if (item.kv.key.toLowerCase() !== pred.key.toLowerCase()) return false;
if (item.kv === undefined) {return false;}
if (item.kv.key.toLowerCase() !== pred.key.toLowerCase()) {return false;}
return evaluatePredicate(item.kv.value, pred);
}
function mdBlockHasMatchingItem(block: MdBlock, pred: PredicateSpec): boolean {
for (const item of block.items) {
if (mdItemMatchesPredicate(item, pred)) return true;
if (mdItemMatchesPredicate(item, pred)) {return true;}
}
return false;
}
@@ -596,12 +596,12 @@ function jsoncChildMatchesPredicate(node: JsoncValue, pred: PredicateSpec): bool
}
function jsoncChildFieldText(node: JsoncValue, key: string): string | null {
if (node.kind !== "object") return null;
if (node.kind !== "object") {return null;}
const e = node.entries.find((entry) => entry.key === key);
if (e === undefined) return null;
if (e === undefined) {return null;}
const v = e.value;
if (v.kind === "string") return v.value;
if (v.kind === "number" || v.kind === "boolean") return String(v.value);
if (v.kind === "null") return "null";
if (v.kind === "string") {return v.value;}
if (v.kind === "number" || v.kind === "boolean") {return String(v.value);}
if (v.kind === "null") {return "null";}
return null;
}

View File

@@ -33,7 +33,7 @@ export function emitJsonc(ast: JsoncAst, opts: JsoncEmitOptions = {}): string {
}
// Render mode loses comments; walks leaves for caller-injected sentinel.
if (ast.root === null) return "";
if (ast.root === null) {return "";}
return renderValue(ast.root, guardPath, []);
}

View File

@@ -26,11 +26,11 @@ export type JsoncOcPathMatch =
};
export function resolveJsoncOcPath(ast: JsoncAst, path: OcPath): JsoncOcPathMatch | null {
if (ast.root === null) return null;
if (ast.root === null) {return null;}
const segments: string[] = [];
const collect = (slot: string | undefined): void => {
if (slot === undefined) return;
if (slot === undefined) {return;}
for (const s of splitRespectingBrackets(slot, ".")) {
segments.push(isQuotedSeg(s) ? unquoteSeg(s) : s);
}
@@ -39,35 +39,35 @@ export function resolveJsoncOcPath(ast: JsoncAst, path: OcPath): JsoncOcPathMatc
collect(path.item);
collect(path.field);
if (segments.length === 0) return { kind: "root", node: ast };
if (segments.length === 0) {return { kind: "root", node: ast };}
let current: JsoncValue = ast.root;
let lastEntry: JsoncEntry | null = null;
const walked: string[] = [];
for (let seg of segments) {
if (seg.length === 0) return null;
if (seg.length === 0) {return null;}
// `-N` on an indexable container is positional; on a keyed
// container it falls through to literal-key lookup (e.g. Telegram
// supergroup IDs — openclaw#59934).
if (isPositionalSeg(seg)) {
const concrete = positionalForJsonc(current, seg);
if (concrete !== null) seg = concrete;
if (concrete !== null) {seg = concrete;}
}
walked.push(seg);
if (current.kind === "object") {
const entry = current.entries.find((e) => e.key === seg);
if (entry === undefined) return null;
if (entry === undefined) {return null;}
lastEntry = entry;
current = entry.value;
continue;
}
if (current.kind === "array") {
const idx = Number(seg);
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) return null;
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) {return null;}
lastEntry = null;
const item = current.items[idx];
if (item === undefined) return null;
if (item === undefined) {return null;}
current = item;
continue;
}

View File

@@ -83,8 +83,8 @@ function replaceAt(
newValue: JsoncValue,
): JsoncValue | null {
const seg = segments[i];
if (seg === undefined) return newValue;
if (seg.length === 0) return null;
if (seg === undefined) {return newValue;}
if (seg.length === 0) {return null;}
if (current.kind === "object") {
// Positional tokens resolve against the entries' ordered key list;
@@ -96,16 +96,16 @@ function replaceAt(
size: current.entries.length,
keys: current.entries.map((e) => e.key),
});
if (resolved === null) return null;
if (resolved === null) {return null;}
segNorm = resolved;
}
const lookupKey = isQuotedSeg(segNorm) ? unquoteSeg(segNorm) : segNorm;
const idx = current.entries.findIndex((e) => e.key === lookupKey);
if (idx === -1) return null;
if (idx === -1) {return null;}
const child = current.entries[idx];
if (child === undefined) return null;
if (child === undefined) {return null;}
const replacedChild = replaceAt(child.value, segments, i + 1, newValue);
if (replacedChild === null) return null;
if (replacedChild === null) {return null;}
const newEntry: JsoncEntry = { ...child, value: replacedChild };
const newEntries = current.entries.slice();
newEntries[idx] = newEntry;
@@ -123,15 +123,15 @@ function replaceAt(
indexable: true,
size: current.items.length,
});
if (resolved === null) return null;
if (resolved === null) {return null;}
segNorm = resolved;
}
const idx = Number(segNorm);
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) return null;
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) {return null;}
const child = current.items[idx];
if (child === undefined) return null;
if (child === undefined) {return null;}
const replacedChild = replaceAt(child, segments, i + 1, newValue);
if (replacedChild === null) return null;
if (replacedChild === null) {return null;}
const newItems = current.items.slice();
newItems[idx] = replacedChild;
return {
@@ -147,12 +147,12 @@ function replaceAt(
function pickLineIndex(ast: JsonlAst, addr: string): number {
if (addr === "$last") {
for (let i = ast.lines.length - 1; i >= 0; i--) {
if (ast.lines[i]?.kind === "value") return i;
if (ast.lines[i]?.kind === "value") {return i;}
}
return -1;
}
const m = /^L(\d+)$/.exec(addr);
if (m === null || m[1] === undefined) return -1;
if (m === null || m[1] === undefined) {return -1;}
const target = Number(m[1]);
return ast.lines.findIndex((l) => l.line === target);
}

View File

@@ -24,7 +24,7 @@ const BOM = "";
function hasControlChar(s: string): boolean {
for (let i = 0; i < s.length; i++) {
const cc = s.charCodeAt(i);
if (cc <= 0x1f || cc === 0x7f) return true;
if (cc <= 0x1f || cc === 0x7f) {return true;}
}
return false;
}
@@ -206,9 +206,9 @@ export function formatOcPath(path: OcPath): string {
// (quoted, predicate, union, sentinel). Plain concatenation would
// silently split a raw `foo/bar` slot into two segments at parse.
const formatSubSegment = (sub: string): string => {
if (isQuotedSeg(sub)) return sub;
if (sub.startsWith("[") && sub.endsWith("]")) return sub;
if (sub.startsWith("{") && sub.endsWith("}")) return sub;
if (isQuotedSeg(sub)) {return sub;}
if (sub.startsWith("[") && sub.endsWith("]")) {return sub;}
if (sub.startsWith("{") && sub.endsWith("}")) {return sub;}
return quoteSeg(sub);
};
const validateSubForFormat = (sub: string, slotName: string): void => {
@@ -241,10 +241,10 @@ export function formatOcPath(path: OcPath): string {
const fileNeedsQuote = /[/[\]{}?&%"\s]/.test(path.file);
const formattedFile = fileNeedsQuote ? quoteSeg(path.file) : path.file;
let out = OC_SCHEME + formattedFile;
if (path.section !== undefined) out += "/" + formatSlot(path.section, "section");
if (path.item !== undefined) out += "/" + formatSlot(path.item, "item");
if (path.field !== undefined) out += "/" + formatSlot(path.field, "field");
if (path.session !== undefined) out += "?session=" + path.session;
if (path.section !== undefined) {out += "/" + formatSlot(path.section, "section");}
if (path.item !== undefined) {out += "/" + formatSlot(path.item, "item");}
if (path.field !== undefined) {out += "/" + formatSlot(path.field, "field");}
if (path.session !== undefined) {out += "?session=" + path.session;}
if (out.length > MAX_PATH_LENGTH) {
fail(
@@ -263,7 +263,7 @@ export function formatOcPath(path: OcPath): string {
/** True iff `input` is a string `parseOcPath` would accept. */
export function isValidOcPath(input: unknown): input is string {
if (typeof input !== "string") return false;
if (typeof input !== "string") {return false;}
try {
parseOcPath(input);
return true;
@@ -305,8 +305,8 @@ export interface PositionalContainer {
// Resolve `$last` against a container; null when empty.
export function resolvePositionalSeg(seg: string, container: PositionalContainer): string | null {
if (seg !== POS_LAST || container.size === 0) return null;
if (!container.indexable) return container.keys?.[container.keys.length - 1] ?? null;
if (seg !== POS_LAST || container.size === 0) {return null;}
if (!container.indexable) {return container.keys?.[container.keys.length - 1] ?? null;}
return String(container.size - 1);
}
@@ -325,13 +325,13 @@ export const WILDCARD_RECURSIVE = "**";
*/
export function isPattern(path: OcPath): boolean {
for (const slot of [path.section, path.item, path.field]) {
if (slot === undefined) continue;
if (slot === undefined) {continue;}
// Quote-aware split — `slot.split('.')` would shred quoted keys
// containing literal `*` and falsely flag them as wildcards.
for (const sub of splitRespectingBrackets(slot, ".")) {
if (sub === WILDCARD_SINGLE || sub === WILDCARD_RECURSIVE) return true;
if (isUnionSeg(sub)) return true;
if (isPredicateSeg(sub)) return true;
if (sub === WILDCARD_SINGLE || sub === WILDCARD_RECURSIVE) {return true;}
if (isUnionSeg(sub)) {return true;}
if (isPredicateSeg(sub)) {return true;}
}
}
return false;
@@ -346,11 +346,11 @@ export function isUnionSeg(seg: string): boolean {
}
export function parseUnionSeg(seg: string): readonly string[] | null {
if (!isUnionSeg(seg)) return null;
if (!isUnionSeg(seg)) {return null;}
const inner = seg.slice(1, -1);
if (inner.length === 0) return null;
if (inner.length === 0) {return null;}
const alts = inner.split(",");
if (alts.some((a) => a.length === 0)) return null;
if (alts.some((a) => a.length === 0)) {return null;}
return alts;
}
@@ -363,7 +363,7 @@ export type PredicateOp = "=" | "!=" | "<" | "<=" | ">" | ">=";
const PREDICATE_OPS: readonly PredicateOp[] = ["!=", "<=", ">=", "<", ">", "="];
export function isPredicateSeg(seg: string): boolean {
if (seg.length < 4 || !seg.startsWith("[") || !seg.endsWith("]")) return false;
if (seg.length < 4 || !seg.startsWith("[") || !seg.endsWith("]")) {return false;}
const inner = new Set(seg.slice(1, -1));
return PREDICATE_OPS.some((op) => inner.has(op));
}
@@ -375,14 +375,14 @@ export interface PredicateSpec {
}
export function parsePredicateSeg(seg: string): PredicateSpec | null {
if (seg.length < 4 || !seg.startsWith("[") || !seg.endsWith("]")) return null;
if (seg.length < 4 || !seg.startsWith("[") || !seg.endsWith("]")) {return null;}
const inner = seg.slice(1, -1);
// Leftmost operator wins; at each position, multi-char beats single
// (so `[a<=b]` parses as op=`<=`, not op=`<`).
for (let i = 1; i < inner.length; i++) {
for (const op of PREDICATE_OPS) {
if (!inner.startsWith(op, i)) continue;
if (i + op.length >= inner.length) continue; // empty value
if (!inner.startsWith(op, i)) {continue;}
if (i + op.length >= inner.length) {continue;} // empty value
return { key: inner.slice(0, i), op, value: inner.slice(i + op.length) };
}
}
@@ -391,7 +391,7 @@ export function parsePredicateSeg(seg: string): PredicateSpec | null {
// Numeric ops require both sides to coerce to finite numbers.
export function evaluatePredicate(actual: string | null, pred: PredicateSpec): boolean {
if (actual === null) return false;
if (actual === null) {return false;}
switch (pred.op) {
case "=":
return actual === pred.value;
@@ -403,10 +403,10 @@ export function evaluatePredicate(actual: string | null, pred: PredicateSpec): b
case ">=": {
const a = Number(actual);
const b = Number(pred.value);
if (!Number.isFinite(a) || !Number.isFinite(b)) return false;
if (pred.op === "<") return a < b;
if (pred.op === "<=") return a <= b;
if (pred.op === ">") return a > b;
if (!Number.isFinite(a) || !Number.isFinite(b)) {return false;}
if (pred.op === "<") {return a < b;}
if (pred.op === "<=") {return a <= b;}
if (pred.op === ">") {return a > b;}
return a >= b;
}
}
@@ -464,13 +464,13 @@ export function repackPath(pattern: OcPath, subs: readonly string[]): OcPath {
}
function extractSession(queryPart: string): string | undefined {
if (queryPart.length === 0) return undefined;
if (queryPart.length === 0) {return undefined;}
for (const pair of queryPart.split("&")) {
const eqIndex = pair.indexOf("=");
if (eqIndex === -1) continue;
if (eqIndex === -1) {continue;}
const key = pair.slice(0, eqIndex);
const value = pair.slice(eqIndex + 1);
if (key === "session" && value.length > 0) return value;
if (key === "session" && value.length > 0) {return value;}
}
return undefined;
}
@@ -486,23 +486,23 @@ function scanBracketAware(s: string, onChar: ScanCallback, onUnbalanced: () => n
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (inQuote) {
if (c === '"') inQuote = false;
if (onChar(c, i, false) === "stop") return;
if (c === '"') {inQuote = false;}
if (onChar(c, i, false) === "stop") {return;}
continue;
}
if (c === '"') {
inQuote = true;
if (onChar(c, i, false) === "stop") return;
if (onChar(c, i, false) === "stop") {return;}
continue;
}
if (c === "[") depthBracket++;
else if (c === "]") depthBracket--;
else if (c === "{") depthBrace++;
else if (c === "}") depthBrace--;
if (depthBracket < 0 || depthBrace < 0) onUnbalanced();
if (onChar(c, i, depthBracket === 0 && depthBrace === 0) === "stop") return;
if (c === "[") {depthBracket++;}
else if (c === "]") {depthBracket--;}
else if (c === "{") {depthBrace++;}
else if (c === "}") {depthBrace--;}
if (depthBracket < 0 || depthBrace < 0) {onUnbalanced();}
if (onChar(c, i, depthBracket === 0 && depthBrace === 0) === "stop") {return;}
}
if (depthBracket !== 0 || depthBrace !== 0 || inQuote) onUnbalanced();
if (depthBracket !== 0 || depthBrace !== 0 || inQuote) {onUnbalanced();}
}
/** First top-level occurrence of `ch` in `s`; -1 when absent. */
@@ -563,7 +563,7 @@ export function unquoteSeg(seg: string): string {
// Refuses values with `"` or `\` — no escape mechanism.
export function quoteSeg(value: string): string {
if (value.length === 0) return '""';
if (value.length === 0) {return '""';}
if (value.includes('"') || value.includes("\\")) {
fail(
`Cannot quote value containing '"' or '\\\\': ${printable(value)}`,
@@ -607,8 +607,8 @@ function validateSubSegment(sub: string, input: string): void {
}
// Quoted content is byte-literal but can't contain `"` or `\`.
if (isQuotedSeg(sub)) {
const inner = sub.slice(1, -1);
if (inner.includes('"') || inner.includes("\\")) {
const inner = new Set(sub.slice(1, -1));
if (inner.has('"') || inner.has("\\")) {
fail(
`Quoted segment cannot contain '"' or '\\\\': ${printable(sub)}`,
input,

View File

@@ -18,7 +18,6 @@ import type {
AstItem,
Diagnostic,
FrontmatterEntry,
MdAst,
ParseResult,
} from "./ast.js";
import { slugify } from "./slug.js";
@@ -154,7 +153,7 @@ function extractItems(tokens: readonly Token[], bodyFileLine: number): AstItem[]
const items: AstItem[] = [];
for (let i = 0; i < tokens.length; i++) {
const t = tokens[i];
if (t.type !== "list_item_open" || t.map === null) continue;
if (t.type !== "list_item_open" || t.map === null) {continue;}
// First inline at the item's own depth is the item text.
let nestedDepth = 0;
let text = "";

View File

@@ -35,39 +35,39 @@ export type OcPathMatch =
export function resolveMdOcPath(ast: MdAst, path: OcPath): OcPathMatch | null {
if (path.section === "[frontmatter]") {
const key = path.item ?? path.field;
if (key === undefined) return null;
if (key === undefined) {return null;}
const entry = ast.frontmatter.find((e) => e.key === key);
if (entry === undefined) return null;
if (entry === undefined) {return null;}
return { kind: "frontmatter", node: entry };
}
if (path.section === undefined) return { kind: "root", node: ast };
if (path.section === undefined) {return { kind: "root", node: ast };}
const block = ast.blocks.find((b) => b.slug === path.section!.toLowerCase());
if (block === undefined) return null;
if (path.item === undefined) return { kind: "block", node: block };
if (block === undefined) {return null;}
if (path.item === undefined) {return { kind: "block", node: block };}
// Item dispatch: ordinal (#N) > positional ($first/$last/-N) > slug.
// Ordinal uses document order so duplicate-slug items stay distinct.
let item: AstItem | undefined;
if (isOrdinalSeg(path.item)) {
const n = parseOrdinalSeg(path.item);
if (n === null || n < 0 || n >= block.items.length) return null;
if (n === null || n < 0 || n >= block.items.length) {return null;}
item = block.items[n];
} else if (isPositionalSeg(path.item)) {
const concrete = resolvePositionalSeg(path.item, {
indexable: true,
size: block.items.length,
});
if (concrete === null) return null;
if (concrete === null) {return null;}
item = block.items[Number(concrete)];
} else {
item = block.items.find((i) => i.slug === path.item!.toLowerCase());
}
if (item === undefined) return null;
if (path.field === undefined) return { kind: "item", node: item, block };
if (item === undefined) {return null;}
if (path.field === undefined) {return { kind: "item", node: item, block };}
if (item.kv === undefined) return null;
if (item.kv.key.toLowerCase() !== path.field.toLowerCase()) return null;
if (item.kv === undefined) {return null;}
if (item.kv.key.toLowerCase() !== path.field.toLowerCase()) {return null;}
return { kind: "item-field", node: item, block, value: item.kv.value };
}

View File

@@ -99,13 +99,13 @@ export interface InsertionInfo {
export function detectInsertion(path: OcPath): InsertionInfo | null {
const segments: Array<{ slot: "section" | "item" | "field"; value: string }> = [];
if (path.section !== undefined) segments.push({ slot: "section", value: path.section });
if (path.item !== undefined) segments.push({ slot: "item", value: path.item });
if (path.field !== undefined) segments.push({ slot: "field", value: path.field });
if (segments.length === 0) return null;
if (path.section !== undefined) {segments.push({ slot: "section", value: path.section });}
if (path.item !== undefined) {segments.push({ slot: "item", value: path.item });}
if (path.field !== undefined) {segments.push({ slot: "field", value: path.field });}
if (segments.length === 0) {return null;}
const last = segments[segments.length - 1];
if (!last.value.startsWith("+")) return null;
if (!last.value.startsWith("+")) {return null;}
const rest = last.value.slice(1);
const marker: InsertionInfo["marker"] =
@@ -137,7 +137,7 @@ export function resolveOcPath(ast: OcAst, path: OcPath): OcMatch | null {
);
}
const insertion = detectInsertion(path);
if (insertion !== null) return resolveInsertion(ast, insertion);
if (insertion !== null) {return resolveInsertion(ast, insertion);}
switch (ast.kind) {
case "md":
@@ -151,7 +151,7 @@ export function resolveOcPath(ast: OcAst, path: OcPath): OcMatch | null {
function resolveMdToUniversal(ast: MdAst, path: OcPath): OcMatch | null {
const m = resolveMdOcPath(ast, path);
if (m === null) return null;
if (m === null) {return null;}
switch (m.kind) {
case "root":
return { kind: "root", ast, line: 1 };
@@ -168,9 +168,9 @@ function resolveMdToUniversal(ast: MdAst, path: OcPath): OcMatch | null {
function resolveJsoncToUniversal(ast: JsoncAst, path: OcPath): OcMatch | null {
const m = resolveJsoncOcPath(ast, path);
if (m === null) return null;
if (m.kind === "root") return { kind: "root", ast, line: 1 };
if (m.kind === "object-entry") return jsoncValueToMatch(m.node.value, m.node.line);
if (m === null) {return null;}
if (m.kind === "root") {return { kind: "root", ast, line: 1 };}
if (m.kind === "object-entry") {return jsoncValueToMatch(m.node.value, m.node.line);}
return jsoncValueToMatch(m.node, m.node.line ?? 1);
}
@@ -193,12 +193,12 @@ function jsoncValueToMatch(value: JsoncValue, line: number): OcMatch {
function resolveJsonlToUniversal(ast: JsonlAst, path: OcPath): OcMatch | null {
const m = resolveJsonlOcPath(ast, path);
if (m === null) return null;
if (m.kind === "root") return { kind: "root", ast, line: 1 };
if (m.kind === "line") return { kind: "node", descriptor: "jsonl-line", line: m.node.line };
if (m === null) {return null;}
if (m.kind === "root") {return { kind: "root", ast, line: 1 };}
if (m.kind === "line") {return { kind: "node", descriptor: "jsonl-line", line: m.node.line };}
// Inside-line jsonc nodes always have line=1; use the JsonlLine's
// file-level line instead since every inside-line node sits there.
if (m.kind === "object-entry") return jsoncValueToMatch(m.node.value, m.line);
if (m.kind === "object-entry") {return jsoncValueToMatch(m.node.value, m.line);}
return jsoncValueToMatch(m.node, m.line);
}
@@ -215,13 +215,13 @@ function resolveInsertion(ast: OcAst, info: InsertionInfo): OcMatch | null {
function resolveMdInsertion(ast: MdAst, info: InsertionInfo): OcMatch | null {
const p = info.parentPath;
if (p.section === undefined) return { kind: "insertion-point", container: "md-file", line: 1 };
if (p.section === undefined) {return { kind: "insertion-point", container: "md-file", line: 1 };}
if (p.section === "[frontmatter]") {
return { kind: "insertion-point", container: "md-frontmatter", line: 1 };
}
if (p.item === undefined && p.field === undefined) {
const m = resolveMdOcPath(ast, p);
if (m === null || m.kind !== "block") return null;
if (m === null || m.kind !== "block") {return null;}
return { kind: "insertion-point", container: "md-section", line: m.node.line };
}
return null;
@@ -256,7 +256,7 @@ function resolveJsoncInsertion(ast: JsoncAst, info: InsertionInfo): OcMatch | nu
function resolveJsonlInsertion(ast: JsonlAst, info: InsertionInfo): OcMatch | null {
// jsonl insertion only makes sense at file level (`oc://FILE/+`).
// Surfaced line is lastLine+1 so consumers render correctly.
if (info.parentPath.section !== undefined) return null;
if (info.parentPath.section !== undefined) {return null;}
const lastLine = ast.lines.length > 0 ? ast.lines[ast.lines.length - 1].line : 0;
return { kind: "insertion-point", container: "jsonl-file", line: lastLine + 1 };
}
@@ -317,7 +317,7 @@ function setStructuredLeaf<A extends OcAst, M extends StructuredLeafMatch>(
onLine?: () => SetResult,
): SetResult {
const existing = resolve(ast, path);
if (existing === null) return { ok: false, reason: "unresolved" };
if (existing === null) {return { ok: false, reason: "unresolved" };}
if (existing.kind === "root") {
return { ok: false, reason: "not-writable", detail: "root replacement is not supported via setOcPath" };
}
@@ -445,9 +445,9 @@ function setJsoncInsertion(ast: JsoncAst, info: InsertionInfo, value: string): S
return { ok: false, reason: "type-mismatch", detail: "cannot insert by key into array" };
}
return mutateJsoncContainer(ast, info.parentPath, (container) => {
if (container.kind !== "array") return null;
if (container.kind !== "array") {return null;}
const items = container.items.slice();
if (info.marker === "+") items.push(newJsoncValue);
if (info.marker === "+") {items.push(newJsoncValue);}
else if (typeof info.marker === "object" && info.marker.kind === "indexed") {
const idx = Math.min(info.marker.index, items.length);
items.splice(idx, 0, newJsoncValue);
@@ -465,8 +465,8 @@ function setJsoncInsertion(ast: JsoncAst, info: InsertionInfo, value: string): S
}
const key = info.marker.key;
return mutateJsoncContainer(ast, info.parentPath, (container) => {
if (container.kind !== "object") return null;
if (container.entries.some((e) => e.key === key)) return null; // duplicate
if (container.kind !== "object") {return null;}
if (container.entries.some((e) => e.key === key)) {return null;} // duplicate
const newEntry: JsoncEntry = { key, value: newJsoncValue, line: 0 };
return {
kind: "object",
@@ -495,14 +495,14 @@ function setJsonlInsertion(ast: JsonlAst, info: InsertionInfo, value: string): S
// semantic node, only the bytes change.
function coerceJsoncLeaf(valueText: string, existing: JsoncValue): JsoncValue | null {
const lineExt = existing.line !== undefined ? { line: existing.line } : {};
if (existing.kind === "string") return { kind: "string", value: valueText, ...lineExt };
if (existing.kind === "string") {return { kind: "string", value: valueText, ...lineExt };}
if (existing.kind === "number") {
const n = Number(valueText);
return Number.isFinite(n) ? { kind: "number", value: n, ...lineExt } : null;
}
if (existing.kind === "boolean") {
if (valueText === "true") return { kind: "boolean", value: true, ...lineExt };
if (valueText === "false") return { kind: "boolean", value: false, ...lineExt };
if (valueText === "true") {return { kind: "boolean", value: true, ...lineExt };}
if (valueText === "false") {return { kind: "boolean", value: false, ...lineExt };}
return null;
}
if (existing.kind === "null") {
@@ -522,10 +522,10 @@ function tryParseJson(value: string): unknown {
function jsonToJsoncValue(v: unknown): JsoncValue {
// Synthetic values omit `line` — only the parser sets line metadata.
if (v === null) return { kind: "null" };
if (typeof v === "string") return { kind: "string", value: v };
if (typeof v === "number") return { kind: "number", value: v };
if (typeof v === "boolean") return { kind: "boolean", value: v };
if (v === null) {return { kind: "null" };}
if (typeof v === "string") {return { kind: "string", value: v };}
if (typeof v === "number") {return { kind: "number", value: v };}
if (typeof v === "boolean") {return { kind: "boolean", value: v };}
if (Array.isArray(v)) {
return { kind: "array", items: v.map(jsonToJsoncValue) };
}
@@ -549,7 +549,7 @@ function mutateJsoncContainer(
parentPath: OcPath,
mutate: (container: JsoncValue) => JsoncValue | null,
): SetResult {
if (ast.root === null) return { ok: false, reason: "no-root" };
if (ast.root === null) {return { ok: false, reason: "no-root" };}
// Quote-aware split so insertion under a key with `/`/`.`/etc. works.
const segments: string[] = [];
@@ -565,7 +565,7 @@ function mutateJsoncContainer(
const newRoot =
segments.length === 0 ? mutate(ast.root) : mutateAt(ast.root, segments, 0, mutate);
if (newRoot === null) return { ok: false, reason: "unresolved" };
if (newRoot === null) {return { ok: false, reason: "unresolved" };}
const next: JsoncAst = { kind: "jsonc", raw: "", root: newRoot };
return { ok: true, ast: { ...next, raw: emitJsonc(next, { mode: "render" }) } };
@@ -578,17 +578,17 @@ function mutateAt(
mutate: (container: JsoncValue) => JsoncValue | null,
): JsoncValue | null {
const seg = segments[i];
if (seg === undefined) return mutate(current);
if (seg.length === 0) return null;
if (seg === undefined) {return mutate(current);}
if (seg.length === 0) {return null;}
if (current.kind === "object") {
// AST keys are unquoted; strip quotes from the path segment.
const lookupKey = isQuotedSeg(seg) ? unquoteSeg(seg) : seg;
const idx = current.entries.findIndex((e) => e.key === lookupKey);
if (idx === -1) return null;
if (idx === -1) {return null;}
const child = current.entries[idx];
const replaced = mutateAt(child.value, segments, i + 1, mutate);
if (replaced === null) return null;
if (replaced === null) {return null;}
const newEntries = current.entries.slice();
newEntries[idx] = { ...child, value: replaced };
return {
@@ -599,10 +599,10 @@ function mutateAt(
}
if (current.kind === "array") {
const idx = Number(seg);
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) return null;
if (!Number.isInteger(idx) || idx < 0 || idx >= current.items.length) {return null;}
const child = current.items[idx];
const replaced = mutateAt(child, segments, i + 1, mutate);
if (replaced === null) return null;
if (replaced === null) {return null;}
const newItems = current.items.slice();
newItems[idx] = replaced;
return {
@@ -624,21 +624,21 @@ function rebuildMdRaw(ast: MdAst): MdAst {
parts.push("---");
}
if (ast.preamble.length > 0) {
if (parts.length > 0) parts.push("");
if (parts.length > 0) {parts.push("");}
parts.push(ast.preamble);
}
for (const block of ast.blocks) {
if (parts.length > 0) parts.push("");
if (parts.length > 0) {parts.push("");}
parts.push(`## ${block.heading}`);
if (block.bodyText.length > 0) parts.push(block.bodyText);
if (block.bodyText.length > 0) {parts.push(block.bodyText);}
}
void emitJsonl;
return { ...ast, raw: parts.join("\n") };
}
function formatFrontmatterValue(value: string): string {
if (value.length === 0) return '""';
if (/[:#&*?|<>=!%@`,[\]{}\r\n]/.test(value)) return JSON.stringify(value);
if (value.length === 0) {return '""';}
if (/[:#&*?|<>=!%@`,[\]{}\r\n]/.test(value)) {return JSON.stringify(value);}
return value;
}