@intlayer/core
Version:
Includes core Intlayer functions like translation, dictionary, and utility functions shared across multiple packages.
322 lines (320 loc) • 8.24 kB
JavaScript
//#region src/utils/parseYaml.ts
const parseYaml = (input) => {
const text = input.trim();
if (!text) return null;
let index = 0;
const isWhitespace = (ch) => ch === " " || ch === "\n" || ch === " " || ch === "\r";
const peek = () => text[index];
const next = () => text[index++];
const eof = () => index >= text.length;
const skipWhitespace = () => {
while (!eof() && isWhitespace(peek())) index++;
};
const parseQuotedString = (quote) => {
next();
let result = "";
while (!eof()) {
const ch = next();
if (ch === quote) return result;
if (ch === "\\" && !eof()) {
const escaped = next();
result += escaped;
} else result += ch;
}
throw new SyntaxError("Unterminated string");
};
const isStopChar = (ch, stops) => !!ch && stops.includes(ch);
const parseUnquotedToken = (stops) => {
let result = "";
while (!eof()) {
if (isStopChar(peek(), stops)) break;
result += next();
}
return result.trim();
};
const toTypedValue = (raw) => {
if (raw === "true" || raw === "false" || raw === "null" || raw === "undefined" || raw === "yes" || raw === "no" || raw === "on" || raw === "off") return raw;
if (raw === "NaN" || raw === "Infinity" || raw === "-Infinity") return raw;
if (/^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();
if (peek() === "{") return parseObject();
const ch = peek();
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
let hasColon = false;
let tempIdx = index;
while (tempIdx < text.length && text[tempIdx] !== "\n") {
if (text[tempIdx] === ":" && tempIdx + 1 < text.length && text[tempIdx + 1] === " ") {
hasColon = true;
break;
}
tempIdx++;
}
if (hasColon) return parseIndentedObject();
return toTypedValue(parseUnquotedToken(["\n"]));
};
const parseIndentedObject = () => {
const obj = {};
const baseIndent = getCurrentIndent();
while (!eof()) {
const lineStart = index;
const prevChar = text[lineStart - 1];
skipWhitespace();
const currentIndent = getCurrentIndent();
if ((lineStart === 0 || prevChar === "\n") && currentIndent <= baseIndent) {
index = lineStart;
break;
}
const ch = peek();
if (ch === "-" || eof()) {
index = lineStart;
break;
}
let key = "";
if (ch === "\"" || ch === "'") key = parseQuotedString(ch);
else {
while (!eof() && peek() !== ":") key += next();
key = key.trim();
}
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 getCurrentIndent = () => {
let indent = 0;
let i = index;
while (i > 0 && text[i - 1] !== "\n") i--;
while (i < text.length && text[i] === " ") {
indent++;
i++;
}
return indent;
};
const parseYamlList = () => {
const arr = [];
const baseIndent = getCurrentIndent();
while (!eof()) {
while (!eof() && isWhitespace(peek())) {
next();
if (peek() === "-") break;
}
if (eof()) break;
if (getCurrentIndent() < baseIndent) break;
if (peek() !== "-") break;
arr.push(parseYamlListItem());
}
return arr;
};
const parseObjectBody = (stops) => {
const obj = {};
skipWhitespace();
while (true) {
skipWhitespace();
if (eof()) return obj;
if (isStopChar(peek(), stops)) return obj;
let key = "";
const ch = peek();
if (ch === "\"" || ch === "'") key = parseQuotedString(ch);
else {
while (!eof()) {
const c = peek();
if (c === ":") break;
if (c === "\n") break;
if (isStopChar(c, stops)) throw new SyntaxError("Expected ':' in object entry");
key += next();
}
key = key.trim();
}
if (!key) return obj;
if (eof() || next() !== ":") throw new SyntaxError("Expected ':' after key");
if (!eof() && peek() === " ") next();
while (!eof() && (peek() === " " || 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();
if (!eof()) {
const nextChar = peek();
if (nextChar && !isStopChar(nextChar, stops) && nextChar !== "-") {
obj[key] = "";
continue;
}
}
obj[key] = "";
return obj;
}
}
obj[key] = parseValue(stops.includes("}") ? [
",",
"\n",
...stops
] : ["\n", ...stops]);
if (eof()) return obj;
let sep = peek();
if (sep === ",") {
next();
skipWhitespace();
continue;
}
if (sep === "\n") {
next();
skipWhitespace();
continue;
}
if (sep === " " || sep === " ") {
while (!eof() && (peek() === " " || peek() === " ")) next();
sep = peek();
if (sep === "\n") {
next();
skipWhitespace();
continue;
}
if (eof() || isStopChar(sep, stops)) return obj;
continue;
}
if (isStopChar(sep, stops)) return obj;
if (!eof()) continue;
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 i = 0;
let depth = 0;
let quote = null;
while (i < s.length) {
const char = s[i];
if (quote) {
if (char === "\\" && i + 1 < s.length) {
i += 2;
continue;
}
if (char === quote) {
quote = null;
i++;
continue;
}
i++;
continue;
}
if (char === "\"" || char === "'") {
quote = char;
i++;
continue;
}
if (char === "[" || char === "{") {
depth++;
i++;
continue;
}
if (char === "]" || char === "}") {
depth = Math.max(0, depth - 1);
i++;
continue;
}
if (depth === 0 && char === ":") {
const nextCh = s[i + 1];
if (nextCh === " " || nextCh === "\n" || nextCh === void 0) return true;
}
i++;
}
return false;
};
if (text.startsWith("]") || text.startsWith("}")) throw new SyntaxError("Unexpected closing bracket");
if (text.startsWith("[")) {
const value = parseArray();
skipWhitespace();
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
return value;
}
if (text.startsWith("{")) {
const value = parseObject();
skipWhitespace();
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
return value;
}
if (hasTopLevelKeyColonSpace(text)) {
const value = parseObjectBody([]);
skipWhitespace();
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
return value;
}
const single = parseValue([]);
skipWhitespace();
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
return single;
};
//#endregion
exports.parseYaml = parseYaml;
//# sourceMappingURL=parseYaml.cjs.map