manifestFields

tcmts.share.manifestFields · #6smlck64 · term · runs anywhere

A package manifest's fields as its source writes them: the object it declares, read, never run (a page runs no code). Strings, and arrays and objects of them, come through (as const and the like after one are fine); anything computed doesn't.

export function manifestFields(text: string): Record<string, unknown> {
  let i = 0;
  const space = () => {
    for (;;) {
      while (i < text.length && /\s/.test(text[i]!)) i++;
      const [open, close] = text.startsWith("//", i)
        ? ["//", "\n"]
        : text.startsWith("/*", i)
          ? ["/*", "*/"]
          : [];
      if (!open || !close) return;
      const end = text.indexOf(close, i + open.length);
      i = end < 0 ? text.length : end + close.length;
    }
  };
  const str = (): string | undefined => {
    const q = text[i++];
    let out = "";
    let computed = false;
    while (i < text.length && text[i] !== q) {
      if (text[i] === "\\") {
        const c = text[i + 1] ?? "";
        out += c === "n" ? "\n" : c === "t" ? "\t" : c;
        i += 2;
      } else {
        if (q === "`" && text.startsWith("${", i)) computed = true;
        out += text[i++];
      }
    }
    i++;
    return computed ? undefined : out;
  };
  const closes = () => i >= text.length || text[i] === "}" || text[i] === "]";
  // what follows a value up to the next `,` or closing bracket: nothing, or a type assertion, keeps it
  const rest = () => {
    const from = i;
    let depth = 0;
    while (i < text.length) {
      const c = text[i]!;
      if (depth === 0 && (c === "," || c === "}" || c === "]")) break;
      if (c === '"' || c === "'" || c === "`") {
        str();
        continue;
      }
      if (text.startsWith("//", i) || text.startsWith("/*", i)) {
        space();
        continue;
      }
      if ("([{".includes(c)) depth++;
      else if (")]}".includes(c)) depth--;
      i++;
    }
    return /^(\s|\/\/[^\n]*|\/\*[\s\S]*?\*\/)*((as|satisfies)\b[\s\S]*)?$/.test(
      text.slice(from, i),
    );
  };
  const literal = (): unknown => {
    space();
    const c = text[i];
    if (c === '"' || c === "'" || c === "`") return str();
    if (c === "[") {
      i++;
      const out: unknown[] = [];
      for (space(); !closes(); space()) {
        const v = value();
        if (v !== undefined) out.push(v);
        if (text[i] === ",") i++;
      }
      i++;
      return out;
    }
    if (c !== "{") return undefined;
    i++;
    const out: Record<string, unknown> = {};
    for (space(); !closes(); space()) {
      let k: string | undefined;
      if (text[i] === '"' || text[i] === "'") k = str();
      else {
        const m = /^[\w$]+/.exec(text.slice(i, i + 200));
        if (m) i += (k = m[0]).length;
      }
      space();
      if (k !== undefined && text[i] === ":") {
        i++;
        const v = value();
        if (v !== undefined) out[k] = v;
      } else rest();
      if (text[i] === ",") i++;
    }
    i++;
    return out;
  };
  const value = () => {
    const v = literal();
    return rest() ? v : undefined;
  };
  space();
  const decl = /=\s*\{/.exec(text.slice(i));
  i = decl ? text.indexOf("{", i + decl.index) : text.indexOf("{", i);
  if (i < 0) return {};
  return literal() as Record<string, unknown>;
}

used by