UNPKG

node-api-headers

Version:
93 lines (81 loc) 2.62 kB
const parser = require("acorn"); /** * @param {string} text Code to evaluate * @returns {boolean | undefined} The result of the evaluation, `undefined` if * parsing failed or the result is unknown. */ function evaluate(text) { try { const ast = parser.parse(text, { ecmaVersion: 2020 }); const expressionStatement = ast.body[0]; if (expressionStatement.type !== "ExpressionStatement") { throw new Error("Expected an ExpressionStatement"); } return visitExpression(expressionStatement.expression); } catch { // Return an unknown result if parsing failed return undefined; } } /** * @param {import("acorn").Expression} node */ const visitExpression = (node) => { if (node.type === "LogicalExpression") { return visitLogicalExpression(node); } else if (node.type === "UnaryExpression") { return visitUnaryExpression(node); } else if (node.type === "CallExpression") { return visitCallExpression(node); } else { throw new Error(`Unknown node type: ${node.type} ${JSON.stringify(node)}`); } }; /** * @param {import("acorn").LogicalExpression} node */ const visitLogicalExpression = (node) => { const left = visitExpression(node.left); const right = visitExpression(node.right); if (node.operator === "&&") { // We can shortcircuit regardless of `unknown` if either are false. if (left === false || right === false) { return false; } else if (left === undefined || right === undefined) { return undefined; } else { return left && right; } } else if (node.operator === "||") { if (left === undefined || right === undefined) { return undefined; } else { return left || right; } } }; /** * @param {import("acorn").UnaryExpression} node */ const visitUnaryExpression = (node) => { const argument = visitExpression(node.argument); if (typeof argument === 'boolean') { return !argument; } }; /** * @param {import("acorn").CallExpression} node */ const visitCallExpression = (node) => { const isDefinedExperimentalCall = // is `defined(arg)` call node.callee.type === 'Identifier' && node.callee.name === 'defined' && node.arguments.length == 1 // and that arg is `NAPI_EXPERIMENTAL` && node.arguments[0].type === 'Identifier' && node.arguments[0].name === 'NAPI_EXPERIMENTAL'; if (isDefinedExperimentalCall) { return false; } }; module.exports = { evaluate };