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 const regex = /\{\{([^}]+)\}\}/g
export const getInputKeys = (value: string) => { 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('}}', '') return item.replace('{{', '').replace('}}', '')
}) || [] })
const keyObj: Record<string, boolean> = {} const keyObj: Record<string, boolean> = {}
// remove duplicate keys // remove duplicate keys
const res: string[] = [] const res: string[] = []
@ -69,7 +75,8 @@ const BlockInput: FC<IBlockInputProps> = ({
const renderSafeContent = (value: string) => { const renderSafeContent = (value: string) => {
const parts = value.split(/(\{\{[^}]+\}\}|\n)/g) const parts = value.split(/(\{\{[^}]+\}\}|\n)/g)
return parts.map((part, index) => { return parts.map((part, index) => {
const variableMatch = part.match(/^\{\{([^}]+)\}\}$/) const variableRegex = /^\{\{([^}]+)\}\}$/
const variableMatch = variableRegex.exec(part)
if (variableMatch) { if (variableMatch) {
return ( return (
<VarHighlight <VarHighlight

View File

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

View File

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

View File

@ -29,7 +29,13 @@ const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: str
params: '', params: '',
body: { type: BodyType.none, data: '' }, 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 let hasData = false
for (let i = 1; i < args.length; i++) { 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"` // To support command like `curl -F "file=@/path/to/file;type=application/zip"`
// the `;type=application/zip` should translate to `Content-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) { if (typeMatch) {
const [, actualValue, mimeType] = typeMatch const [, actualValue, mimeType] = typeMatch
value = actualValue value = actualValue

View File

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

View File

@ -183,7 +183,8 @@ export default translation
if (fs.existsSync(toGenLanguageFilePath)) { if (fs.existsSync(toGenLanguageFilePath)) {
const originalContent = fs.readFileSync(toGenLanguageFilePath, 'utf8') const originalContent = fs.readFileSync(toGenLanguageFilePath, 'utf8')
// Extract original template literal content for resolutionTooltip // 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) { if (originalMatch) {
const [fullMatch, key, value] = originalMatch const [fullMatch, key, value] = originalMatch
res = res.replace( res = res.replace(

View File

@ -10,7 +10,8 @@ function getNamespacesFromConfig() {
const configContent = fs.readFileSync(configPath, 'utf8') const configContent = fs.readFileSync(configPath, 'utf8')
// Extract NAMESPACES array using regex // 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) { if (!namespacesMatch) {
throw new Error('Could not find NAMESPACES array in i18next-config.ts') throw new Error('Could not find NAMESPACES array in i18next-config.ts')
} }
@ -36,7 +37,8 @@ function getNamespacesFromTypes() {
const typesContent = fs.readFileSync(typesPath, 'utf8') const typesContent = fs.readFileSync(typesPath, 'utf8')
// Extract namespaces from Messages type // 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) { if (!messagesMatch) {
return null return null
} }

View File

@ -157,7 +157,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
const trimmedLine = line.trim() const trimmedLine = line.trim()
// Track current object path // Track current object path
const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*{/) const keyRegex = /^(\w+)\s*:\s*{/
const keyMatch = keyRegex.exec(trimmedLine)
if (keyMatch) { if (keyMatch) {
currentPath.push(keyMatch[1]) currentPath.push(keyMatch[1])
braceDepth++ braceDepth++
@ -170,7 +171,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
} }
// Check if this line matches our target key // 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) { if (leafKeyMatch) {
const fullPath = [...currentPath, leafKeyMatch[1]] const fullPath = [...currentPath, leafKeyMatch[1]]
const fullPathString = fullPath.join('.') const fullPathString = fullPath.join('.')
@ -191,7 +193,8 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
const trimmedKeyLine = keyLine.trim() const trimmedKeyLine = keyLine.trim()
// If key line ends with ":" (not ":", "{ " or complete value), it's likely multiline // 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 // Find the value lines that belong to this key
let currentLine = targetLineIndex + 1 let currentLine = targetLineIndex + 1
let foundValue = false 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) // 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 break
// Check if this line is part of the value // Check if this line is part of the value

View File

@ -10,7 +10,8 @@ function getNamespacesFromConfig() {
const configContent = fs.readFileSync(configPath, 'utf8') const configContent = fs.readFileSync(configPath, 'utf8')
// Extract NAMESPACES array using regex // 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) { if (!namespacesMatch) {
throw new Error('Could not find NAMESPACES array in i18next-config.ts') 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) if (!value)
return [] 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) return ![CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT].includes(item)
}).map((item) => { }).map((item) => {
return item.replace('{{', '').replace('}}', '') 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> = {} const keyObj: Record<string, boolean> = {}
// remove duplicate keys // remove duplicate keys
const res: string[] = [] const res: string[] = []