branch:
config.ts
4364 bytesRaw
/**
* 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<string, unknown>;
return extractWranglerConfig(config);
} catch {
return {};
}
}
/**
* Parse wrangler.json content
*/
function parseWranglerJson(content: string): WranglerConfig {
try {
const config = JSON.parse(content) as Record<string, unknown>;
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<string, unknown>;
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<string, unknown>
): 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;
}