@nitrogenbuilder/client-core
Version:
Nitrogen Builder Core Client
108 lines (93 loc) • 2.69 kB
text/typescript
import type { ParsedFilter } from './templateFilters.js';
export interface ParsedSegmentPart {
type: 'path' | 'literal';
value: string;
filters: ParsedFilter[];
}
export interface ParsedSegment {
parts: ParsedSegmentPart[];
}
export function parseSegment(raw: string): ParsedSegment {
const trimmed = raw.trim();
const parts: ParsedSegmentPart[] = [];
const tokens = splitOnPlus(trimmed);
for (const token of tokens) {
const t = token.trim();
if (
(t.startsWith("'") && t.endsWith("'")) ||
(t.startsWith('"') && t.endsWith('"'))
) {
parts.push({ type: 'literal', value: t.slice(1, -1), filters: [] });
} else {
const colonParts = splitOnColons(t);
const path = colonParts[0];
const filters = colonParts.slice(1).map(parseFilterToken);
parts.push({ type: 'path', value: path, filters });
}
}
return { parts };
}
function parseFilterToken(token: string): ParsedFilter {
const parenIndex = token.indexOf('(');
if (parenIndex === -1) {
return { name: token, args: [] };
}
const name = token.slice(0, parenIndex);
const argsStr = token.slice(parenIndex + 1, -1); // strip parens
const args = splitArgs(argsStr).map(parseArg);
return { name, args };
}
function parseArg(raw: string): string | number {
const trimmed = raw.trim();
if (
(trimmed.startsWith("'") && trimmed.endsWith("'")) ||
(trimmed.startsWith('"') && trimmed.endsWith('"'))
) {
return trimmed.slice(1, -1);
}
const num = Number(trimmed);
return isNaN(num) ? trimmed : num;
}
/** Split on `+` outside of quotes and parentheses */
function splitOnPlus(str: string): string[] {
return splitOutside(str, '+');
}
/** Split on `:` outside of quotes and parentheses */
function splitOnColons(str: string): string[] {
return splitOutside(str, ':');
}
/** Split on `,` outside of quotes and parentheses */
function splitArgs(str: string): string[] {
return splitOutside(str, ',');
}
function splitOutside(str: string, delimiter: string): string[] {
const results: string[] = [];
let current = '';
let inQuote: string | false = false;
let parenDepth = 0;
for (let i = 0; i < str.length; i++) {
const ch = str[i];
if ((ch === "'" || ch === '"') && parenDepth === 0 && !inQuote) {
inQuote = ch;
current += ch;
} else if (inQuote && ch === inQuote) {
inQuote = false;
current += ch;
} else if (inQuote) {
current += ch;
} else if (ch === '(') {
parenDepth++;
current += ch;
} else if (ch === ')') {
parenDepth--;
current += ch;
} else if (ch === delimiter && parenDepth === 0) {
results.push(current);
current = '';
} else {
current += ch;
}
}
if (current) results.push(current);
return results;
}