feat(codemode): sync v2 implementation (#35574)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
export const regexpMethods = new Set(["test", "exec", "toString"])
|
||||
|
||||
export const regexpProperties = new Set([
|
||||
"source",
|
||||
"flags",
|
||||
"lastIndex",
|
||||
"global",
|
||||
"ignoreCase",
|
||||
"multiline",
|
||||
"sticky",
|
||||
"unicode",
|
||||
"dotAll",
|
||||
])
|
||||
|
||||
export const regexFailureReason = (error: unknown): string =>
|
||||
(error instanceof Error ? error.message : String(error)).replace(/^Invalid regular expression:\s*/i, "")
|
||||
|
||||
export const escapeRegexHint =
|
||||
'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.'
|
||||
|
||||
export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFlags = ""): RegExp => {
|
||||
if (arg instanceof SandboxRegExp) return arg.regex
|
||||
if (typeof arg === "string") {
|
||||
try {
|
||||
return new RegExp(arg, extraFlags)
|
||||
} catch (error) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`,
|
||||
node,
|
||||
).as("SyntaxError")
|
||||
}
|
||||
}
|
||||
throw new InterpreterRuntimeError(
|
||||
`String.${method} expects a regular expression (a /pattern/flags literal or new RegExp(...)) or a string pattern, not ${arg === null ? "null" : typeof arg}.`,
|
||||
node,
|
||||
)
|
||||
}
|
||||
|
||||
export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
|
||||
const result: Array<unknown> = Array.from(match, (group) => group)
|
||||
if (match.index !== undefined) (result as Record<string, unknown> & Array<unknown>).index = match.index
|
||||
if (match.groups) {
|
||||
const groups: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, group] of Object.entries(match.groups)) {
|
||||
if (!isBlockedMember(key)) groups[key] = group
|
||||
}
|
||||
;(result as Record<string, unknown> & Array<unknown>).groups = groups
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const invokeRegExpMethod = (
|
||||
value: SandboxRegExp,
|
||||
name: string,
|
||||
args: Array<unknown>,
|
||||
node: AstNode,
|
||||
): unknown => {
|
||||
switch (name) {
|
||||
case "test":
|
||||
return value.regex.test(coerceToString(args[0]))
|
||||
case "exec": {
|
||||
const matched = value.regex.exec(coerceToString(args[0]))
|
||||
return matched === null ? null : matchToValue(matched)
|
||||
}
|
||||
case "toString":
|
||||
return coerceToString(value)
|
||||
default:
|
||||
throw new InterpreterRuntimeError(`RegExp method '${name}' is not available in CodeMode.`, node)
|
||||
}
|
||||
}
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
|
||||
import { SandboxRegExp } from "../values.js"
|
||||
import { coerceToString } from "./value.js"
|
||||
Reference in New Issue
Block a user