{"record":{"id":"9cdb645f13841bb4","repo":"thedotmack/claude-mem","slug":"strip-introduced-aftererrs-beforeerrs-new-par","errorCode":null,"errorMessage":"strip introduced ${afterErrs - beforeErrs} new parse error(s); refusing to write","messagePattern":"strip introduced (.+?) new parse error\\(s\\); refusing to write","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/strip-comments.ts","lineNumber":161,"sourceCode":"    for (const r of trailing) addRange(r);\n    ts.forEachChild(node, visitNode);\n  }\n  function addRange(r: ts.CommentRange): void {\n    const key = `${r.pos}-${r.end}`;\n    if (seen.has(key)) return;\n    seen.add(key);\n    const text = source.slice(r.pos, r.end);\n    if (isDirectiveJs(text)) return;\n    ranges.push([r.pos, r.end]);\n  }\n\n  visitNode(sf);\n  const out = spliceRanges(source, ranges);\n\n  const after = ts.createSourceFile('check', out, ts.ScriptTarget.Latest, true, kind);\n  const afterErrs = parseDiagnosticsCount(after);\n  if (afterErrs > beforeErrs) {\n    throw new Error(`strip introduced ${afterErrs - beforeErrs} new parse error(s); refusing to write`);\n  }\n  return out;\n}\n\nfunction collapseBlankLines(s: string): string {\n  return s.replace(/(?:[ \\t]*\\n){3,}/g, '\\n\\n');\n}\n\nfunction spliceRanges(source: string, ranges: Array<[number, number]>): string {\n  ranges.sort((a, b) => a[0] - b[0]);\n  let out = source;\n  for (let i = ranges.length - 1; i >= 0; i--) {\n    const [s, e] = ranges[i];\n    let removeStart = s;\n    let removeEnd = e;\n    let lineStart = s;\n    while (lineStart > 0 && (out[lineStart - 1] === ' ' || out[lineStart - 1] === '\\t')) {\n      lineStart--;","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/scripts/strip-comments.ts#L143-L179","documentation":"Thrown by stripJsLike() as a safety guard: after removing comment ranges, it re-parses the result with the TypeScript compiler and compares parse-diagnostic counts. If stripping introduced any new parse errors, it refuses to write the file. This prevents a comment-stripping tool from silently corrupting source by, e.g., merging two tokens that a comment had separated (ASI/regex/string-edge cases).","triggerScenarios":"Removing a line comment that was acting as ASI separation between two tokens, turning them into one. Removing a block comment adjacent to a regex literal so the `/` is reinterpreted. Removing comments inside or adjacent to template literals where TypeScript' comment-range detection spanned into expression text. Edge cases in ts.getLeadingCommentRanges/getTrailingCommentRanges on JSX or decorator syntax.","commonSituations":"Running strip-comments across a large codebase and one unusual file (heavy regex, dense JSX, decorator metadata) trips the parser. A file where a `//` appears inside a string but was mis-detected as a comment range. New TypeScript syntax the parser handles differently after comment removal.","solutions":["Add the @strip-comments-keep marker at the top of the offending file so it is skipped entirely.","Inspect the reported file: re-parse mentally what the comment was separating and either keep that comment or add a semicolon/parenthesization so removal is safe.","Run the script with --dry-run --verbose to identify which file fails, then exclude it via SKIP_PATHS/SKIP_BASENAMES if it is generated/vendored.","Report a parser-edge-case bug if the file is ordinary TS with no regex/JSX tricks."],"exampleFix":"// before — file trips the guard after stripping\nexport const a = 1\n// some comment\nconst b = 2\n// after — add the keep marker at top of file so it is skipped\n// @strip-comments-keep\nexport const a = 1","handlingStrategy":"try-catch","validationCode":"// skip files explicitly marked to keep\nif (KEEP_MARKER.test(original.slice(0, 4096))) { stats.skipped++; return; }","typeGuard":null,"tryCatchPattern":"try {\n  stripped = stripJsLike(original, ext);\n} catch (e) {\n  stats.errors.push(`${relPath}: ${(e as Error).message}`);\n  return; // do not write a possibly-corrupt file\n}","preventionTips":["Run strip-comments with --dry-run --verbose first to see which files trip the guard.","Mark generated/vendored/unusual files with @strip-comments-keep at the top to skip them.","Never bypass the re-parse guard; it is the only thing preventing silent source corruption."],"tags":["typescript","parsing","build","safety","tooling"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}