Replace string.match with RegExp.exec for better performance

Co-authored-by: asukaminato0721 <30024051+asukaminato0721@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-09-25 17:23:26 +00:00
parent 0fc541ba57
commit 9533b88a9f
10 changed files with 56 additions and 20 deletions

View File

@ -12,9 +12,15 @@ import { checkKeys } from '@/utils/var'
const regex = /\{\{([^}]+)\}\}/g
export const getInputKeys = (value: string) => {
const keys = value.match(regex)?.map((item) => {
const matches: string[] = []
let match
regex.lastIndex = 0
while ((match = regex.exec(value)) !== null)
matches.push(match[0])
const keys = matches.map((item) => {
return item.replace('{{', '').replace('}}', '')
}) || []
})
const keyObj: Record<string, boolean> = {}
// remove duplicate keys
const res: string[] = []
@ -69,7 +75,8 @@ const BlockInput: FC<IBlockInputProps> = ({
const renderSafeContent = (value: string) => {
const parts = value.split(/(\{\{[^}]+\}\}|\n)/g)
return parts.map((part, index) => {
const variableMatch = part.match(/^\{\{([^}]+)\}\}$/)
const variableRegex = /^\{\{([^}]+)\}\}$/
const variableMatch = variableRegex.exec(part)
if (variableMatch) {
return (
<VarHighlight

View File

@ -37,10 +37,15 @@ export const getInputVars = (text: string): ValueSelector[] => {
if (!text || typeof text !== 'string')
return []
const allVars = text.match(/{{#([^#]*)#}}/g)
if (allVars && allVars?.length > 0) {
const matches: string[] = []
const regex = /{{#([^#]*)#}}/g
let match
while ((match = regex.exec(text)) !== null)
matches.push(match[0])
if (matches && matches?.length > 0) {
// {{#context#}}, {{#query#}} is not input vars
const inputVars = allVars
const inputVars = matches
.filter(item => item.includes('.'))
.map((item) => {
const valueSelector = item.replace('{{#', '').replace('#}}', '').split('.')

View File

@ -1202,7 +1202,10 @@ const matchNotSystemVars = (prompts: string[]) => {
prompts.forEach((prompt) => {
VAR_REGEX.lastIndex = 0
if (typeof prompt !== 'string') return
allVars.push(...(prompt.match(VAR_REGEX) || []))
let match
while ((match = VAR_REGEX.exec(prompt)) !== null)
allVars.push(match[0])
})
const uniqVars = uniq(allVars).map(v =>
v.replaceAll('{{#', '').replace('#}}', '').split('.'),

View File

@ -29,7 +29,13 @@ const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: str
params: '',
body: { type: BodyType.none, data: '' },
}
const args = curlCommand.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []
const regex = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g
const matches: string[] = []
let match
while ((match = regex.exec(curlCommand)) !== null)
matches.push(match[0])
const args = matches
let hasData = false
for (let i = 1; i < args.length; i++) {
@ -75,7 +81,8 @@ const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: str
// To support command like `curl -F "file=@/path/to/file;type=application/zip"`
// the `;type=application/zip` should translate to `Content-Type: application/zip`
const typeMatch = value.match(/^(.+?);type=(.+)$/)
const typeRegex = /^(.+?);type=(.+)$/
const typeMatch = typeRegex.exec(value)
if (typeMatch) {
const [, actualValue, mimeType] = typeMatch
value = actualValue

View File

@ -105,7 +105,7 @@ export function getLoopStartNode(loopId: string): Node {
export const genNewNodeTitleFromOld = (oldTitle: string) => {
const regex = /^(.+?)\s*\((\d+)\)\s*$/
const match = oldTitle.match(regex)
const match = regex.exec(oldTitle)
if (match) {
const title = match[1]

View File

@ -183,7 +183,8 @@ export default translation
if (fs.existsSync(toGenLanguageFilePath)) {
const originalContent = fs.readFileSync(toGenLanguageFilePath, 'utf8')
// Extract original template literal content for resolutionTooltip
const originalMatch = originalContent.match(/(resolutionTooltip):\s*`([^`]*)`/s)
const regex = /(resolutionTooltip):\s*`([^`]*)`/s
const originalMatch = regex.exec(originalContent)
if (originalMatch) {
const [fullMatch, key, value] = originalMatch
res = res.replace(

View File

@ -10,7 +10,8 @@ function getNamespacesFromConfig() {
const configContent = fs.readFileSync(configPath, 'utf8')
// Extract NAMESPACES array using regex
const namespacesMatch = configContent.match(/const NAMESPACES = \[([\s\S]*?)\]/)
const namespacesRegex = /const NAMESPACES = \[([\s\S]*?)\]/
const namespacesMatch = namespacesRegex.exec(configContent)
if (!namespacesMatch) {
throw new Error('Could not find NAMESPACES array in i18next-config.ts')
}
@ -36,7 +37,8 @@ function getNamespacesFromTypes() {
const typesContent = fs.readFileSync(typesPath, 'utf8')
// Extract namespaces from Messages type
const messagesMatch = typesContent.match(/export type Messages = \{([\s\S]*?)\}/)
const messagesRegex = /export type Messages = \{([\s\S]*?)\}/
const messagesMatch = messagesRegex.exec(typesContent)
if (!messagesMatch) {
return null
}

View File

@ -157,7 +157,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
const trimmedLine = line.trim()
// Track current object path
const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*{/)
const keyRegex = /^(\w+)\s*:\s*{/
const keyMatch = keyRegex.exec(trimmedLine)
if (keyMatch) {
currentPath.push(keyMatch[1])
braceDepth++
@ -170,7 +171,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
}
// Check if this line matches our target key
const leafKeyMatch = trimmedLine.match(/^(\w+)\s*:/)
const leafKeyRegex = /^(\w+)\s*:/
const leafKeyMatch = leafKeyRegex.exec(trimmedLine)
if (leafKeyMatch) {
const fullPath = [...currentPath, leafKeyMatch[1]]
const fullPathString = fullPath.join('.')
@ -191,7 +193,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
const trimmedKeyLine = keyLine.trim()
// If key line ends with ":" (not ":", "{ " or complete value), it's likely multiline
if (trimmedKeyLine.endsWith(':') && !trimmedKeyLine.includes('{') && !trimmedKeyLine.match(/:\s*['"`]/)) {
const valueRegex = /:\s*['"`]/
if (trimmedKeyLine.endsWith(':') && !trimmedKeyLine.includes('{') && !valueRegex.test(trimmedKeyLine)) {
// Find the value lines that belong to this key
let currentLine = targetLineIndex + 1
let foundValue = false
@ -207,7 +210,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
}
// Check if this line starts a new key (indicates end of current value)
if (trimmed.match(/^\w+\s*:/))
const keyStartRegex = /^\w+\s*:/
if (keyStartRegex.test(trimmed))
break
// Check if this line is part of the value

View File

@ -10,7 +10,8 @@ function getNamespacesFromConfig() {
const configContent = fs.readFileSync(configPath, 'utf8')
// Extract NAMESPACES array using regex
const namespacesMatch = configContent.match(/const NAMESPACES = \[([\s\S]*?)\]/)
const namespacesRegex = /const NAMESPACES = \[([\s\S]*?)\]/
const namespacesMatch = namespacesRegex.exec(configContent)
if (!namespacesMatch) {
throw new Error('Could not find NAMESPACES array in i18next-config.ts')
}

View File

@ -102,11 +102,17 @@ export const getVars = (value: string) => {
if (!value)
return []
const keys = value.match(varRegex)?.filter((item) => {
const matches: string[] = []
let match
varRegex.lastIndex = 0
while ((match = varRegex.exec(value)) !== null)
matches.push(match[0])
const keys = matches.filter((item) => {
return ![CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT].includes(item)
}).map((item) => {
return item.replace('{{', '').replace('}}', '')
}).filter(key => key.length <= MAX_VAR_KEY_LENGTH) || []
}).filter(key => key.length <= MAX_VAR_KEY_LENGTH)
const keyObj: Record<string, boolean> = {}
// remove duplicate keys
const res: string[] = []