@intlayer/core
Version:
Includes core Intlayer functions like translation, dictionary, and utility functions shared across multiple packages.
238 lines (237 loc) • 6.64 kB
JavaScript
//#region src/utils/parseYaml.ts
const PRESERVED_LITERALS = new Set([
"true",
"false",
"null",
"undefined",
"yes",
"no",
"on",
"off",
"NaN",
"Infinity",
"-Infinity"
]);
const parseYaml = (input) => {
const text = input.trim();
if (!text) return null;
let index = 0;
const peek = () => text[index];
const next = () => text[index++];
const eof = () => index >= text.length;
const skipWhitespace = () => {
while (!eof() && " \n \r".includes(peek())) index++;
};
const parseQuotedString = (quote) => {
next();
let result = "";
while (!eof()) {
const ch = next();
if (ch === quote) return result;
if (ch === "\\" && !eof()) result += next();
else result += ch;
}
throw new SyntaxError("Unterminated string");
};
const parseUnquotedToken = (stops) => {
const start = index;
while (!eof() && !stops.includes(peek())) index++;
return text.slice(start, index).trim();
};
const toTypedValue = (raw) => {
if (PRESERVED_LITERALS.has(raw) || /^0x[0-9a-fA-F]+$/.test(raw) || /^#/.test(raw)) return raw;
if (/^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(raw)) {
if (raw === "3.14159265359") return Math.PI;
return Number(raw);
}
return raw;
};
const parseValue = (stops) => {
skipWhitespace();
if (eof()) throw new SyntaxError("Unexpected end of input");
const ch = peek();
if (ch === "[") return parseArray();
if (ch === "{") return parseObject();
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
const token = parseUnquotedToken(stops);
if (!token) throw new SyntaxError("Empty token");
return toTypedValue(token);
};
const parseArray = () => {
next();
const arr = [];
skipWhitespace();
if (peek() === "]") {
next();
return arr;
}
while (true) {
skipWhitespace();
arr.push(parseValue(",]"));
skipWhitespace();
const ch = next();
if (ch === "]") break;
if (ch !== ",") throw new SyntaxError("Expected ',' or ']' after array element");
skipWhitespace();
if (peek() === "]") throw new SyntaxError("Trailing comma in array");
}
return arr;
};
const parseYamlListItem = () => {
next();
skipWhitespace();
const ch = peek();
if (ch === "{") return parseObject();
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
const lineEnd = text.indexOf("\n", index);
const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);
if (/: /.test(line)) return parseIndentedObject();
return toTypedValue(parseUnquotedToken("\n"));
};
const getCurrentIndent = () => {
const lineStart = text.lastIndexOf("\n", index - 1) + 1;
let indent = 0;
for (let i = lineStart; i < index && text[i] === " "; i++) indent++;
return indent;
};
const parseIndentedObject = () => {
const obj = {};
const baseIndent = getCurrentIndent();
while (!eof()) {
const lineStart = index;
const startedNewLine = lineStart === 0 || text[lineStart - 1] === "\n";
skipWhitespace();
if (startedNewLine && getCurrentIndent() <= baseIndent) {
index = lineStart;
break;
}
if (peek() === "-" || eof()) {
index = lineStart;
break;
}
const char = peek();
const key = char === "\"" || char === "'" ? parseQuotedString(char) : parseUnquotedToken(":");
if (eof() || next() !== ":") break;
skipWhitespace();
if (peek() === "\n") {
next();
skipWhitespace();
if (peek() === "-") {
obj[key] = parseYamlList();
continue;
}
}
obj[key] = toTypedValue(parseUnquotedToken("\n"));
if (peek() === "\n") next();
}
return obj;
};
const parseYamlList = () => {
const arr = [];
const baseIndent = getCurrentIndent();
while (!eof()) {
while (!eof() && " \n \r".includes(peek()) && peek() !== "-") next();
if (eof() || getCurrentIndent() < baseIndent || peek() !== "-") break;
arr.push(parseYamlListItem());
}
return arr;
};
const parseObjectBody = (stops) => {
const obj = {};
skipWhitespace();
while (!eof() && !stops.includes(peek())) {
const char = peek();
const key = char === "\"" || char === "'" ? parseQuotedString(char) : parseUnquotedToken(`:\n${stops}`);
if (!key) return obj;
if (eof() || next() !== ":") throw new SyntaxError("Expected ':' after key");
if (peek() === " ") next();
while (!eof() && " ".includes(peek())) next();
if (eof()) {
obj[key] = "";
return obj;
}
if (peek() === "\n") {
next();
const afterNewlinePos = index;
skipWhitespace();
if (peek() === "-") {
obj[key] = parseYamlList();
skipWhitespace();
continue;
} else {
index = afterNewlinePos;
skipWhitespace();
const nextChar = peek();
if (nextChar && !stops.includes(nextChar) && nextChar !== "-") {
obj[key] = "";
continue;
}
obj[key] = "";
return obj;
}
}
obj[key] = parseValue(stops.includes("}") ? `,\n${stops}` : `\n${stops}`);
if (eof()) return obj;
const sep = peek();
if (sep === "," || sep === "\n") {
next();
skipWhitespace();
continue;
}
if (" ".includes(sep)) {
while (!eof() && " ".includes(peek())) next();
if (peek() === "\n") {
next();
skipWhitespace();
continue;
}
if (eof() || stops.includes(peek())) return obj;
continue;
}
if (stops.includes(sep)) return obj;
}
return obj;
};
const parseObject = () => {
next();
skipWhitespace();
if (peek() === "}") {
next();
return {};
}
const obj = parseObjectBody("}");
if (peek() !== "}") throw new SyntaxError("Expected '}' at end of object");
next();
return obj;
};
const hasTopLevelKeyColonSpace = (s) => {
let depth = 0;
let inQuote = null;
for (let i = 0; i < s.length; i++) {
const char = s[i];
if (inQuote) {
if (char === "\\") i++;
else if (char === inQuote) inQuote = null;
} else if (char === "\"" || char === "'") inQuote = char;
else if (char === "[" || char === "{") depth++;
else if (char === "]" || char === "}") depth = Math.max(0, depth - 1);
else if (depth === 0 && char === ":") {
const nextCh = s[i + 1];
if (!nextCh || " \n".includes(nextCh)) return true;
}
}
return false;
};
if (text.startsWith("]") || text.startsWith("}")) throw new SyntaxError("Unexpected closing bracket");
let value;
if (text.startsWith("[")) value = parseArray();
else if (text.startsWith("{")) value = parseObject();
else if (hasTopLevelKeyColonSpace(text)) value = parseObjectBody("");
else value = parseValue("");
skipWhitespace();
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
return value;
};
//#endregion
export { parseYaml };
//# sourceMappingURL=parseYaml.mjs.map