如何将fs.readFile读取的JS对象字面量字符串转换为JS对象?
解决JS非标准对象字符串转JS对象的问题
你拿到的字符串是包含函数调用的JS对象字面量,不是严格的JSON格式,所以JSON.parse没法处理。下面给两种Node.js环境下可行的解决办法:
方法一:用Node.js内置的vm模块(推荐)
vm模块能在隔离的上下文里执行JS代码,刚好适配这种带自定义函数的对象语法。先模拟env相关函数的行为,再执行字符串拿到对象:
const vm = require('vm'); // 假设你已经通过fs.readFile拿到了content字符串 const content = "{ host: env(\"HOST\", \"0.0.0.0\"), port: env.int(\"PORT\", 1337), app: { keys: env.array(\"APP_KEYS\"), },}"; // 模拟env系列函数,根据你的实际需求调整逻辑 const context = vm.createContext({ env: { (key, defaultValue) => process.env[key] || defaultValue, int: (key, defaultValue) => { const val = process.env[key]; return val ? parseInt(val, 10) : defaultValue; }, array: (key) => { const val = process.env[key]; return val ? val.split(',') : []; } } }); // 把目标字符串包装成函数返回值,降低执行风险 const script = new vm.Script(`(() => { return ${content}; })()`); // 执行脚本拿到可操作的JS对象 const configObj = script.runInContext(context); console.log(configObj); // 现在就能对这个对象做检查、修改了
注意:如果你的env函数有其他逻辑,要同步调整context里的模拟函数;另外确保要执行的字符串是可信的,避免恶意代码注入。
方法二:AST解析(适合高安全要求场景)
如果不想执行代码,也可以用@babel/parser把字符串解析成抽象语法树(AST),再遍历AST提取数据:
const parser = require('@babel/parser'); const traverse = require('@babel/traverse').default; const content = "{ host: env(\"HOST\", \"0.0.0.0\"), port: env.int(\"PORT\", 1337), app: { keys: env.array(\"APP_KEYS\"), },}"; // 把字符串解析成AST const ast = parser.parseExpression(content); const configObj = {}; // 遍历AST提取配置值 traverse(ast, { ObjectProperty(path) { const key = path.node.key.name; const valueNode = path.node.value; // 处理env函数调用 if (valueNode.type === 'CallExpression') { const callee = valueNode.callee; const args = valueNode.arguments; if (callee.type === 'Identifier' && callee.name === 'env') { const envKey = args[0].value; const defaultValue = args[1].value; configObj[key] = process.env[envKey] || defaultValue; } else if (callee.type === 'MemberExpression' && callee.object.name === 'env') { const method = callee.property.name; const envKey = args[0].value; const defaultValue = args[1]?.value; if (method === 'int') { configObj[key] = process.env[envKey] ? parseInt(process.env[envKey], 10) : defaultValue; } else if (method === 'array') { configObj[key] = process.env[envKey] ? process.env[envKey].split(',') : []; } } } else if (valueNode.type === 'ObjectExpression') { // 处理嵌套对象(比如示例里的app) configObj[key] = {}; path.traverse({ ObjectProperty(nestedPath) { const nestedKey = nestedPath.node.key.name; const nestedValueNode = nestedPath.node.value; // 处理嵌套里的env.array调用 if (nestedValueNode.type === 'CallExpression') { const callee = nestedValueNode.callee; const args = nestedValueNode.arguments; if (callee.type === 'MemberExpression' && callee.object.name === 'env' && callee.property.name === 'array') { const envKey = args[0].value; configObj[key][nestedKey] = process.env[envKey] ? process.env[envKey].split(',') : []; } } } }); } } }); console.log(configObj);
这种方法不需要执行代码,安全性更高,但需要自己处理AST的遍历逻辑,适合对安全要求严格的场景。
内容的提问来源于stack exchange,提问作者Alexandru Gorgos
相关产品推荐
相关产品推荐

