Rhys Sullivanfmtc92c57d6 months agoHistory
152 lines · 4.5 KB · TypeScript
RawDownload
import { parse } from "@babel/parser"; const FENCED_CODE_BLOCK = /```(?:[^\n`]*)?\s*\n([\s\S]*?)```/i;const FUNCTION_DECLARATION = /^(?:async\s+)?function(?:\s+([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*\(/;const CALLABLE_ERROR = "Code must evaluate to a function"; const extractCandidateSource = (code: string): string => {  const trimmed = code.trim();  if (!trimmed) return "";   const fenced = trimmed.match(FENCED_CODE_BLOCK)?.[1];  return (fenced ?? trimmed).trim();}; const wrapCallableBody = (source: string): string =>  [    "const __fn = (",    source,    ");",    `if (typeof __fn !== "function") throw new Error(${JSON.stringify(CALLABLE_ERROR)});`,    "return await __fn();",  ].join("\n"); const wrapNamedFunctionBody = (source: string, name: string): string =>  [source, `return await ${name}();`].join("\n"); const wrapAnonymousFunctionBody = (source: string): string => `return await (${source})();`; const sliceNode = (  source: string,  node: {    start?: number | null;    end?: number | null;  },): string => {  const start = node.start ?? 0;  const end = node.end ?? source.length;  return source.slice(start, end);}; const unwrapExpression = (expression: { type: string; expression?: unknown }): unknown => {  switch (expression.type) {    case "ParenthesizedExpression":    case "TSAsExpression":    case "TSSatisfiesExpression":    case "TSTypeAssertion":    case "TSNonNullExpression":    case "TSInstantiationExpression":      return expression.expression        ? unwrapExpression(expression.expression as { type: string })        : expression;    default:      return expression;  }}; const renderExportDefaultBody = (  source: string,  declaration: ExportDefaultDeclarationNode,): string => {  if (declaration.type === "FunctionDeclaration") {    const fnSource = sliceNode(source, declaration);    const name = declaration.id?.name;    return name ? wrapNamedFunctionBody(fnSource, name) : wrapAnonymousFunctionBody(fnSource);  }   const expression = unwrapExpression(declaration) as {    type?: string;  };  const expressionSource = sliceNode(source, declaration);   if (expression?.type === "ArrowFunctionExpression" || expression?.type === "FunctionExpression") {    return wrapCallableBody(expressionSource);  }   return `return (${expressionSource});`;}; type ExportDefaultDeclarationNode = {  type: string;  start?: number | null;  end?: number | null;  id?: { name?: string | null } | null;  expression?: unknown;}; const renderParsedBody = (source: string): string => {  const program = parse(source, {    sourceType: "module",    allowAwaitOutsideFunction: true,    allowReturnOutsideFunction: true,    allowImportExportEverywhere: true,    plugins: ["typescript"],  }).program;   if (program.body.length !== 1) return source;   const [statement] = program.body;  if (!statement) return source;   switch (statement.type) {    case "ExpressionStatement": {      const expression = unwrapExpression(        statement.expression as { type: string; expression?: unknown },      ) as {        type?: string;      };      return expression?.type === "ArrowFunctionExpression" ||        expression?.type === "FunctionExpression"        ? wrapCallableBody(source)        : source;    }    case "FunctionDeclaration":      return statement.id?.name ? wrapNamedFunctionBody(source, statement.id.name) : source;    case "ExportDefaultDeclaration":      return renderExportDefaultBody(source, statement.declaration);    default:      return source;  }}; const renderHeuristicBody = (source: string): string => {  const withoutDefaultExport = source.replace(/^export\s+default\s+/, "").trim();   if (    (withoutDefaultExport.startsWith("async") || withoutDefaultExport.startsWith("(")) &&    withoutDefaultExport.includes("=>")  ) {    return wrapCallableBody(withoutDefaultExport);  }   if (FUNCTION_DECLARATION.test(withoutDefaultExport)) {    const name = withoutDefaultExport.match(FUNCTION_DECLARATION)?.[1];    return name      ? wrapNamedFunctionBody(withoutDefaultExport, name)      : wrapAnonymousFunctionBody(withoutDefaultExport);  }   return withoutDefaultExport;}; export const recoverExecutionBody = (code: string): string => {  const source = extractCandidateSource(code);  if (!source) return "";   // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: Babel parser throws for malformed candidate code, then recovery falls back to heuristics  try {    return renderParsedBody(source);  } catch {    return renderHeuristicBody(source);  }};