diff --git a/src/utils/safe-stringify.ts b/src/utils/safe-stringify.ts new file mode 100644 index 00000000..a5a644df --- /dev/null +++ b/src/utils/safe-stringify.ts @@ -0,0 +1,34 @@ +const replacer = (key: string, value: unknown) => { + if (typeof value === 'object') { + if (key === 'globalThis') { + return '[globalThis]'; + } + if (key === 'global') { + return '[global]'; + } + if (key === 'GLOBAL') { + return '[GLOBAL]'; + } + if (key === 'process') { + return '[process]'; + } + if (key === 'win32') { + // path.win32 + return '[win32]'; + } + if (key === 'posix') { + // path.posix + return '[posix]'; + } + } + if (typeof value === 'function') { + return '[Function]'; + } + if (typeof value === 'bigint') { + return '[bigint]'; + } + return value; +}; +export function safeStringify(obj: unknown) { + return JSON.stringify(obj, replacer); +} diff --git a/src/utils/static-eval.ts b/src/utils/static-eval.ts index 136d64e3..fcfc813b 100644 --- a/src/utils/static-eval.ts +++ b/src/utils/static-eval.ts @@ -1,29 +1,38 @@ +import { safeStringify } from './safe-stringify'; import { EvaluatedValue, StaticValue, ConditionalValue, Node } from './types'; -import { URL } from 'url'; -type Walk = (node: Node) => EvaluatedValue; +type Walk = (node: Node) => Promise; type State = { computeBranches: boolean; vars: Record }; +const walkCache = new Map(); + export async function evaluate( - ast: Node, - vars = {}, - computeBranches = true, + ast: Readonly, + vars: Readonly> = {}, + computeBranches: Readonly = true, ): Promise { const state: State = { computeBranches, vars, }; + const stateCacheKey = safeStringify(state); return walk(ast); // walk returns: // 1. Single known value: { value: value } // 2. Conditional value: { test, then, else } // 3. Unknown value: undefined - function walk(node: Node) { + async function walk(node: Node) { + const walkCacheKey = stateCacheKey + safeStringify(node); + let result = walkCache.get(walkCacheKey); + if (result) { + return result; + } const visitor = visitors[node.type]; if (visitor) { - return visitor.call(state, node, walk); + result = await visitor.call(state, node, walk); } - return undefined; + walkCache.set(walkCacheKey, result); + return result; } } @@ -359,7 +368,7 @@ const visitors: Record< if (typeof obj.value === 'string' && node.property.name === 'concat') { return { value: { - [FUNCTION]: (...args: string[]) => obj.value.concat(args), + [FUNCTION]: (...args: string[]) => obj.value.concat(...args), }, }; } diff --git a/test/unit.test.js b/test/unit.test.js index 12609ce7..b329a965 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -8,6 +8,8 @@ const stat = gracefulFS.promises.stat; const readlink = gracefulFS.promises.readlink; const readFile = gracefulFS.promises.readFile; +jest.setTimeout(5_000); + global._unit = true; const nodeGypTests = [