Sval ·
一个用JavaScript编写的JavaScript解释器,基于解析器Acorn。
- 运行在ES5环境,支持最新的ES特性
- 可选入侵式和沙箱模式
它对于评估更高版本ECMAScript代码很有用,或者适用于禁用了eval
、setTimeout
和new Function
的环境。
安装
Node
使用npm安装Sval。
npm install sval
浏览器
直接从unpkg引入。或者从releases下载,获取最小化文件dist/min/sval.min.js
,并在HTML页面中引入。你可以直接访问全局变量Sval。
<script type="text/javascript" src="https://unpkg.com/sval"></script>
快速开始
import Sval from 'sval'
// 创建一个解释器
const interpreter = new Sval({
// 代码的ECMA版本
// 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
// 或 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024
// 或 "latest"
ecmaVer: 'latest',
// 代码源类型
// "script" 或 "module"
sourceType: 'script',
// 代码是否在沙箱中运行
sandBox: true,
})
// 解析并运行代码
interpreter.run(`
console.log('Hello World')
`)
使用方法
Sval构造函数有三个选项:ecmaVer、sourceType和sandBox。
-
ecmaVer是代码的ECMAScript版本。目前支持3、5、6(2015)、7(2016)、8(2017)、9(2018)、10(2019)、11(2020)、12(2021)、13(2022)、14(2023)、15(2024)和"latest",默认版本为"latest"。
-
sourceType可以是"script"或"module",用于声明Sval如何处理代码。"script"意味着代码将被视为普通脚本,而"module"意味着代码将被视为ES模块,具有全局严格模式并解析import和export声明。默认类型为"script"。
-
sandBox为true表示沙箱模式,为false表示入侵式模式。沙箱模式将在隔离的沙箱中运行代码,不会污染全局作用域。入侵式模式允许你在当前环境的相同全局作用域中运行代码。默认设置为true。
Sval实例有两个主要方法:parse和run。
-
parse用于使用内部Acorn或自定义解析器解析代码,获取相应的AST,如
parse(code: string)
或parse(code: string, parser: (code: string, options: SvalOptions) => estree.Node
。 -
run用于评估输入的代码,接受一个字符串作为参数,如
run(code: string)
,或接受一个遵循ESTree规范的AST作为参数,如run(ast: estree.Node)
。
此外,Sval实例还有一个方法import和一个对象exports用于模块化。
-
import用于将模块导入Sval实例作用域。此方法对不同的
sourceType
有不同的行为。对于"script",此方法接受一个名称和一个模块作为参数,如
import(name: string, mod: any)
,或接受一个包含模块的对象作为参数,如import({ [name: string]: any })
。这些模块将自动声明为Sval实例作用域中的全局变量。此方法更可能在沙箱模式下使用。对于"module",此方法接受一个路径和一个模块声明作为参数,如
import(path: string, mod: Module)
,或接受一个包含模块声明的对象作为参数,如import({ [path: string]: Module })
。Module
可以是ES模块导出对象,如{ default?: any, [name: string]: any }
,或返回ES模块导出对象的函数,如() => ({ default?: any, [name: string]: any })
。如果通过动态导入导入模块,Module
也可以是一个promise或返回promise的函数。这些模块不会自动声明为Sval实例作用域中的全局变量,代码应使用import声明来导入模块。 -
exports用于获取从运行中导出的内容,如果有多次运行的导出会合并。这个对象对不同的
sourceType
也有不同的行为。对于"script",此对象将自动声明为Sval实例作用域中的全局变量,代码可以简单地在其上挂载属性进行导出。
对于"module",此对象不会自动声明为Sval实例作用域中的全局变量,代码需要使用export声明进行导出。
以下是import和exports的示例:
"script"示例:
import Sval from 'sval'
// 为脚本创建一个解释器
const scriptInterpreter = new Sval({ sourceType: 'script' })
// 在解释器中添加全局模块
scriptInterpreter.import('importWhatYouNeed', 'AllKindsOfStuffs')
// 或 scriptInterpreter.import({ importWhatYouNeed: 'AllKindsOfStuffs' })
// 解析并运行代码
scriptInterpreter.run(`
exports.mod = importWhatYouNeed
`)
// 获取运行的导出
console.log(scriptInterpreter.exports.mod) // 得到 'AllKindsOfStuffs'
"module"示例:
import Sval from 'sval'
// 为模块创建一个解释器
const moduleInterpreter = new Sval({ sourceType: 'module' })
// 在解释器中添加ES模块
moduleInterpreter.import('./import-what-you-need', { default: 'AllKindsOfStuffs' })
// 或 moduleInterpreter.import('./import-what-you-need', () => ({ default: 'AllKindsOfStuffs' }))
// 或 moduleInterpreter.import({ './import-what-you-need': { default: 'AllKindsOfStuffs' } })
// 或 moduleInterpreter.import({ './import-what-you-need': () => ({ default: 'AllKindsOfStuffs' }) })
// 在解释器中为动态导入添加ES模块
moduleInterpreter.import('./dynamic-import-what-you-need', Promise.resolve({ default: 'AllKindsOfStuffs' }))
// 解析并运行代码
moduleInterpreter.run(`
import importWhatYouNeed from './import-what-you-need'
import('./dynamic-import-what-you-need').then(m => console.log(m.default)) // 得到 'AllKindsOfStuffs'
export { importWhatYouNeed as mod }
`)
// 获取运行的导出
console.log(moduleInterpreter.exports.mod) // 得到 'AllKindsOfStuffs'
注意
WithStatement和LabeledStatement未实现且不推荐使用。请避免使用它们。
参考
许可证
Sval 使用 MIT 许可证。