MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / checkSyntaxForWrite

Function checkSyntaxForWrite

src/tools/ast/syntax-check.ts:134–184  ·  view source on GitHub ↗
(
  absPath: string,
  beforeContent: string | null,
  afterContent: string,
)

Source from the content-addressed store, hash-verified

132 * uncheckable), or a ready [SYNTAX_REJECTED] message when it must be refused.
133 */
134export async function checkSyntaxForWrite(
135 absPath: string,
136 beforeContent: string | null,
137 afterContent: string,
138): Promise<string | null> {
139 if (!gateEnabled) return null;
140 if (afterContent.length > MAX_GATED_BYTES) return null;
141
142 const lower = absPath.toLowerCase();
143 if (lower.endsWith('.json')) {
144 const after = checkJsonSyntax(afterContent);
145 if (after.length === 0) return null;
146 const beforeHad = beforeContent === null ? null : checkJsonSyntax(beforeContent).length > 0;
147 return shouldReject(beforeHad, true) ? buildSyntaxRejectMessage(absPath, 'JSON', after) : null;
148 }
149
150 try {
151 const { detectLanguage, getParser } = await import('./parser.js');
152 const lang = detectLanguage(absPath);
153 if (!lang) return null; // unknown extension → fail-open
154 const p = await getParser(lang);
155 if (!p) return null; // grammar unavailable → fail-open
156
157 const parseIssues = (text: string): SyntaxIssue[] | null => {
158 let tree: any = null;
159 try {
160 tree = p.parser.parse(text);
161 if (!tree?.rootNode) return null;
162 return findIssuesInTree(tree.rootNode as TSNodeLike, text);
163 } catch (e: any) {
164 // Fail-open (never block the write), but surface that the parser could
165 // not run so a silently-broken parser isn't mistaken for "clean file".
166 console.warn(`[syntax-check] parser could not run for ${absPath}; skipping syntax gate: ${e?.message ?? String(e)}`);
167 return null; // parser hiccup → fail-open
168 } finally {
169 try { tree?.delete?.(); } catch { /* wasm cleanup best-effort */ }
170 }
171 };
172
173 const after = parseIssues(afterContent);
174 if (after === null || after.length === 0) return null;
175 // Lazy baseline: only parse the original when the candidate is broken.
176 const beforeIssues = beforeContent === null ? null : parseIssues(beforeContent);
177 const beforeHad = beforeIssues === null ? (beforeContent === null ? null : true) : beforeIssues.length > 0;
178 return shouldReject(beforeHad, true) ? buildSyntaxRejectMessage(absPath, lang, after) : null;
179 } catch (e: any) {
180 // Fail-open, but log so an unexpectedly-broken gate isn't read as "clean".
181 console.warn(`[syntax-check] gate could not run for ${absPath}; skipping syntax check: ${e?.message ?? String(e)}`);
182 return null; // anything unexpected → fail-open
183 }
184}

Callers 3

executeMethod · 0.85
writeMethod · 0.85

Calls 7

checkJsonSyntaxFunction · 0.85
shouldRejectFunction · 0.85
buildSyntaxRejectMessageFunction · 0.85
detectLanguageFunction · 0.85
getParserFunction · 0.85
parseIssuesFunction · 0.85
warnMethod · 0.80

Tested by

no test coverage detected