You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.1 KiB

  1. import React from 'react'
  2. type Context = Record<string, any>;
  3. /**
  4. * JavaScript
  5. */
  6. export class DynamicScriptExecutor {
  7. private context: Context
  8. private startDelimiter: string
  9. private endDelimiter: string
  10. constructor(context: Context = {}, startDelimiter: string = '{{', endDelimiter: string = '}}') {
  11. this.context = context
  12. this.startDelimiter = startDelimiter
  13. this.endDelimiter = endDelimiter
  14. }
  15. setDelimiters(startDelimiter: string, endDelimiter: string) {
  16. this.startDelimiter = startDelimiter
  17. this.endDelimiter = endDelimiter
  18. }
  19. setContext(context: Context) {
  20. this.context = {
  21. ...this.context,
  22. ...context
  23. }
  24. }
  25. // 检测JS代码的语法是否正确
  26. checkSyntax(script: string) {
  27. const contextKeys = Object.keys(this.context).join(',')
  28. try {
  29. // 尝试创建一个新的函数,如果代码有语法错误,这里会抛出异常
  30. new Function(contextKeys, `return (async () => { ${script} })();`)
  31. return null // 语法正确
  32. } catch (error) {
  33. console.error('代码语法错误:', error)
  34. return error // 语法错误
  35. }
  36. }
  37. // 自动识别并提取动态JS执行块
  38. extractDynamicJSBlocks(input: string): string[] {
  39. const regex = new RegExp(
  40. `${this.escapeRegExp(this.startDelimiter)}(.*?)${this.escapeRegExp(this.endDelimiter)}`,
  41. 'gs'
  42. )
  43. const matches: string[] = []
  44. let match
  45. while ((match = regex.exec(input)) !== null) {
  46. matches.push(match[1].trim())
  47. }
  48. return matches
  49. }
  50. // 执行所有提取的动态JS块
  51. async execute(input: string): Promise<any[]> {
  52. const dynamicBlocks = this.extractDynamicJSBlocks(input)
  53. const results: any[] = []
  54. for (const script of dynamicBlocks) {
  55. const contextKeys = Object.keys(this.context).join(',')
  56. const contextValues = Object.values(this.context)
  57. const error = this.checkSyntax(script)
  58. if (error !== null) {
  59. throw error // 抛出异常以便在调用方捕获
  60. }
  61. const func = new Function(contextKeys, `return (async () => { ${script} })();`)
  62. try {
  63. results.push(await func(...contextValues))
  64. } catch (error) {
  65. this.handleError(error)
  66. throw error
  67. }
  68. }
  69. return results
  70. }
  71. private handleError(error: any) {
  72. console.log(`执行代码时发生错误: ${error.message}`)
  73. }
  74. // 转义正则表达式中的特殊字符
  75. private escapeRegExp(string: string): string {
  76. return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  77. }
  78. }
  79. // 创建一个默认的执行器实例
  80. export const defaultExecutor = new DynamicScriptExecutor({
  81. // 系统内置的一些全局对象
  82. // 全局对象
  83. console,
  84. setTimeout,
  85. setInterval,
  86. clearTimeout,
  87. clearInterval,
  88. fetch,
  89. location,
  90. React,
  91. // document,
  92. FormData,
  93. URLSearchParams,
  94. Headers,
  95. Request,
  96. Response,
  97. Blob,
  98. FileReader,
  99. WebSocket,
  100. EventSource,
  101. localStorage,
  102. sessionStorage,
  103. indexedDB,
  104. crypto,
  105. performance,
  106. navigator,
  107. history,
  108. screen,
  109. alert,
  110. confirm,
  111. prompt,
  112. })