Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 9x 44x 44x 44x 33x 11x 11x 13x 13x 13x 13x 14x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 11x 11x 11x 13x 13x 13x 11x | import { writeFile } from "node:fs/promises";
import { Logger } from "@shared/logger";
import { resolvePathWithinRoot } from "../../security/safe-paths";
const logger = new Logger("HeaderProcessor");
export interface HeaderInclude {
name: string;
content: string;
}
export interface HeaderProcessingResult {
processedCode: string;
lineOffset: number;
}
/**
* Processes header includes by replacing #include statements with actual header content.
* Tracks line offset for later error correction.
*
* @param code - The original sketch code
* @param headers - Array of header includes with name and content
* @param sketchDir - Optional directory to write header files to
* @returns Processed code and cumulative line offset
*/
export async function processHeaderIncludes(
code: string,
headers?: HeaderInclude[],
sketchDir?: string,
): Promise<HeaderProcessingResult> {
let processedCode = code;
let lineOffset = 0;
if (!headers || headers.length === 0) {
return { processedCode, lineOffset };
}
logger.debug(`Processing ${headers.length} header includes`);
for (const header of headers) {
// Try to find includes with both the full name (header_1.h) and without extension (header_1)
const headerWithoutExt = header.name.replace(/\.[^/.]+$/, "");
// Search for both variants: #include "header_1.h" and #include "header_1"
const includeVariants = [`#include "${header.name}"`, `#include "${headerWithoutExt}"`];
let found = false;
for (const includeStatement of includeVariants) {
if (processedCode.includes(includeStatement)) {
logger.debug(`Found include for: ${header.name} (pattern: ${includeStatement})`);
// Replace the #include with the actual header content
const replacement = `// --- Start of ${header.name} ---\n${header.content}\n// --- End of ${header.name} ---`;
const escapedInclude = includeStatement.split('"')[1].replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
const patternString = String.raw`#include\s*"${escapedInclude}"`;
processedCode = processedCode.replaceAll(
new RegExp(patternString, "g"),
replacement,
);
// Calculate line offset by counting newlines in replacement
const newlinesInReplacement = (replacement.match(/\n/g) || []).length;
lineOffset += newlinesInReplacement;
found = true;
logger.debug(`Replaced include for: ${header.name}, line offset now: ${lineOffset}`);
break;
}
}
Iif (!found) {
logger.debug(
`Include not found for: ${header.name} (tried: ${includeVariants.join(", ")})`,
);
}
}
// Write header files to disk as separate files
Eif (sketchDir) {
logger.debug(`Writing ${headers.length} header files to ${sketchDir}`);
for (const header of headers) {
const headerPath = resolvePathWithinRoot(sketchDir, header.name);
logger.debug(`Writing header: ${headerPath}`);
await writeFile(headerPath, header.content);
}
}
return { processedCode, lineOffset };
}
|