Compare commits

...

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
b7bfa0ca6e Complete RegExp.exec refactor for performance optimization
Co-authored-by: asukaminato0721 <30024051+asukaminato0721@users.noreply.github.com>
2025-09-25 17:31:33 +00:00
copilot-swe-agent[bot]
9533b88a9f Replace string.match with RegExp.exec for better performance
Co-authored-by: asukaminato0721 <30024051+asukaminato0721@users.noreply.github.com>
2025-09-25 17:23:26 +00:00
copilot-swe-agent[bot]
0fc541ba57 Initial plan 2025-09-25 17:12:13 +00:00
22 changed files with 86 additions and 33 deletions

View File

@@ -108,7 +108,8 @@ const Configuration: FC = () => {
const [hasFetchedDetail, setHasFetchedDetail] = useState(false)
const isLoading = !hasFetchedDetail
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appIdRegex = /\/app\/([^/]+)/
const matched = appIdRegex.exec(pathname)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const [mode, setMode] = useState('')
const [publishedConfig, setPublishedConfig] = useState<PublishConfig | null>(null)

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

@@ -26,7 +26,8 @@ const AnnotationReply = ({
const { t } = useTranslation()
const router = useRouter()
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appIdRegex = /\/app\/([^/]+)/
const matched = appIdRegex.exec(pathname)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const featuresStore = useFeaturesStore()
const annotationReply = useFeatures(s => s.features.annotationReply)

View File

@@ -28,7 +28,8 @@ const VoiceParamConfig = ({
}: VoiceParamConfigProps) => {
const { t } = useTranslation()
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appIdRegex = /\/app\/([^/]+)/
const matched = appIdRegex.exec(pathname)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const text2speech = useFeatures(state => state.features.text2speech)
const featuresStore = useFeaturesStore()

View File

@@ -11,7 +11,10 @@ export const preprocessLaTeX = (content: string) => {
return content
const codeBlockRegex = /```[\s\S]*?```/g
const codeBlocks = content.match(codeBlockRegex) || []
const codeBlocks: string[] = []
let match
while ((match = codeBlockRegex.exec(content)) !== null)
codeBlocks.push(match[0])
const escapeReplacement = (str: string) => str.replace(/\$/g, '_TMP_REPLACE_DOLLAR_')
let processedContent = content.replace(codeBlockRegex, 'CODE_BLOCK_PLACEHOLDER')

View File

@@ -236,7 +236,8 @@ const Flowchart = (props: FlowchartProps) => {
.split('\n')
.map((line) => {
// Gantt charts have specific syntax needs.
const taskMatch = line.match(/^\s*([^:]+?)\s*:\s*(.*)/)
const taskRegex = /^\s*([^:]+?)\s*:\s*(.*)/
const taskMatch = taskRegex.exec(line)
if (!taskMatch)
return line // Not a task line, return as is.
@@ -245,7 +246,12 @@ const Flowchart = (props: FlowchartProps) => {
// Rule 1: Correct multiple "after" dependencies ONLY if they exist.
// This is a common mistake, e.g., "..., after task1, after task2, ..."
const afterCount = (paramsStr.match(/after /g) || []).length
const afterMatches: string[] = []
const afterRegex = /after /g
let afterMatch
while ((afterMatch = afterRegex.exec(paramsStr)) !== null)
afterMatches.push(afterMatch[0])
const afterCount = afterMatches.length
if (afterCount > 1)
paramsStr = paramsStr.replace(/,\s*after\s+/g, ' ')

View File

@@ -167,10 +167,11 @@ export function isMermaidCodeComplete(code: string): boolean {
const isBalanced = true
// Check for common syntax errors
const arrowRegex = /\S+\s*-->\s*\S+/
const hasNoSyntaxErrors = !trimmedCode.includes('undefined')
&& !trimmedCode.includes('[object Object]')
&& trimmedCode.split('\n').every(line =>
!(line.includes('-->') && !line.match(/\S+\s*-->\s*\S+/)))
!(line.includes('-->') && !arrowRegex.test(line)))
return hasValidStart && isBalanced && hasNoSyntaxErrors
}

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

@@ -53,7 +53,8 @@ export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManife
}
export const parseGitHubUrl = (url: string): GitHubUrlInfo => {
const match = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/)
const regex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/
const match = regex.exec(url)
return match ? { isValid: true, owner: match[1], repo: match[2] } : { isValid: false }
}

View File

@@ -37,7 +37,8 @@ export type DuplicateAppModalProps = {
const DEFAULT_ICON = { type: 'emoji', icon: '🧿', background: '#EFF1F5' }
const extractFileId = (url: string) => {
const match = url.match(/files\/(.+?)\/file-preview/)
const regex = /files\/(.+?)\/file-preview/
const match = regex.exec(url)
return match ? match[1] : null
}
const getIcon = (data?: ToolWithProvider) => {

View File

@@ -84,7 +84,8 @@ const CodeEditor: FC<Props> = ({
const getUniqVarName = (varName: string) => {
if (varList.find(v => v.variable === varName)) {
const match = varName.match(/_(\d+)$/)
const regex = /_(\d+)$/
const match = regex.exec(varName)
const index = (() => {
if (match)

View File

@@ -25,7 +25,8 @@ const SupportVarInput: FC<Props> = ({
const renderSafeContent = (inputValue: string) => {
const parts = inputValue.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

@@ -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

@@ -10,7 +10,7 @@ export const extractFunctionParams = (code: string, language: CodeLanguage) => {
[CodeLanguage.python3]: /def\s+main\s*\((.*?)\)/,
[CodeLanguage.javascript]: /function\s+main\s*\((.*?)\)/,
}
const match = code.match(patterns[language])
const match = patterns[language].exec(code)
const params: string[] = []
if (match?.[1]) {

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

@@ -84,7 +84,7 @@ const ConditionItem = ({
) {
const regex = isCommonVariable ? COMMON_VARIABLE_REGEX : VARIABLE_REGEX
const matchedStartNumber = isCommonVariable ? 2 : 3
const matched = condition.value.match(regex)
const matched = regex.exec(condition.value)
if (matched?.length) {
return {

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[] = []