/** * Wrangler configuration parsing. * * Parses wrangler.toml, wrangler.json, and wrangler.jsonc files * to extract compatibility settings. */ import { parse as parseToml } from "smol-toml"; import type { Files, WranglerConfig } from "./types"; /** * Parse wrangler configuration from files. * * Looks for wrangler.toml, wrangler.json, or wrangler.jsonc in the files * and extracts compatibility_date and compatibility_flags. * * @param files - Virtual file system * @returns Parsed wrangler config, or undefined if no config file found */ export function parseWranglerConfig(files: Files): WranglerConfig | undefined { // Try each config file format in order of preference const tomlContent = files["wrangler.toml"]; if (tomlContent) { return parseWranglerToml(tomlContent); } const jsonContent = files["wrangler.json"]; if (jsonContent) { return parseWranglerJson(jsonContent); } const jsoncContent = files["wrangler.jsonc"]; if (jsoncContent) { return parseWranglerJsonc(jsoncContent); } return undefined; } /** * Parse wrangler.toml content */ function parseWranglerToml(content: string): WranglerConfig { try { const config = parseToml(content) as Record; return extractWranglerConfig(config); } catch { return {}; } } /** * Parse wrangler.json content */ function parseWranglerJson(content: string): WranglerConfig { try { const config = JSON.parse(content) as Record; return extractWranglerConfig(config); } catch { return {}; } } /** * Parse wrangler.jsonc content (JSON with comments) */ function parseWranglerJsonc(content: string): WranglerConfig { try { // Strip comments from JSONC const jsonContent = stripJsonComments(content); const config = JSON.parse(jsonContent) as Record; return extractWranglerConfig(config); } catch { return {}; } } /** * Extract wrangler config fields from parsed config object. * Handles both snake_case (toml) and camelCase (json) formats. */ function extractWranglerConfig( config: Record ): WranglerConfig { const result: WranglerConfig = {}; // main entry point const main = config["main"]; if (typeof main === "string") { result.main = main; } // compatibility_date (toml) or compatibilityDate (json) const date = config["compatibility_date"] ?? config["compatibilityDate"]; if (typeof date === "string") { result.compatibilityDate = date; } // compatibility_flags (toml) or compatibilityFlags (json) const flags = config["compatibility_flags"] ?? config["compatibilityFlags"]; if (Array.isArray(flags) && flags.every((f) => typeof f === "string")) { result.compatibilityFlags = flags; } return result; } /** * Strip comments from JSONC content. * Handles both single-line (//) and multi-line comments. */ function stripJsonComments(content: string): string { let result = ""; let i = 0; let inString = false; let stringChar = ""; while (i < content.length) { const char = content[i]; const nextChar = content[i + 1]; // Handle string literals (don't strip comments inside strings) if ( (char === '"' || char === "'") && (i === 0 || content[i - 1] !== "\\") ) { if (!inString) { inString = true; stringChar = char; } else if (char === stringChar) { inString = false; } result += char; i++; continue; } if (inString) { result += char; i++; continue; } // Handle single-line comments if (char === "/" && nextChar === "/") { // Skip until end of line while (i < content.length && content[i] !== "\n") { i++; } continue; } // Handle multi-line comments if (char === "/" && nextChar === "*") { i += 2; // Skip until */ while ( i < content.length - 1 && !(content[i] === "*" && content[i + 1] === "/") ) { i++; } i += 2; // Skip */ continue; } result += char; i++; } return result; } /** * Check if nodejs_compat flag is enabled in the config. */ export function hasNodejsCompat(config: WranglerConfig | undefined): boolean { return config?.compatibilityFlags?.includes("nodejs_compat") ?? false; }