Convert an estree module into a function body.
npm install estree-util-module-to-function
Typically you’ll get an AST from acorn
, then process it.
import { parse } from 'acorn'
import { moduleToFunction } from 'estree-util-module-to-function'
const ast = parse(source, { ecmaVersion: 'latest', sourceType: 'module' })
moduleToFunction(ast)
console.dir(ast)
This module exports a single function named moduleToFunction
.
Convert an estree module into a function body. This modifies the input AST.
importName
: A custom name for the import. By default,import()
expressions are used. If this option is given, import expressions and import meta properties are transformed into identifiers using this name. (type:string
)
The following example shows how to read the home directory in Node.js by using ESM code from a string.
import { parse } from 'acorn'
import { generate } from 'astring'
import { moduleToFunction } from 'estree-util-module-to-function'
const AsyncFunction = (async () => {
// This function is only defined to access the AsyncFunction constructor.
}).constructor
const source = `
import { readdir } from 'node:fs/promises'
import { homedir } from 'node:os'
const home = homedir()
export default home
export const files = await readdir(homedir())
`
const ast = parse(source, { ecmaVersion: 'latest', sourceType: 'module' })
moduleToFunction(ast)
const transformed = generate(ast)
const fn = new AsyncFunction(transformed)
const result = await fn()
console.dir(result)
The following example is derived from the above. It injects a custom import function, which stubs actual import behaviour.
import { parse } from 'acorn'
import { generate } from 'astring'
import { moduleToFunction } from 'estree-util-module-to-function'
const customImport: Import = async (name) => {
switch (name) {
case 'node:fs/promises':
case 'fs/promises':
return {
async readdir() {
return [
'.cache',
'.config',
'.local',
'Documents',
'Downloads',
'Music',
'Pictures',
'Public',
'Templates',
'Videos'
]
}
}
case 'node:os':
case 'os':
return {
homedir() {
return '/home/fakeuser'
}
}
default:
throw new Error(`Cannot find package '${name}'`)
}
}
// Optionally define import meta
costumImport.meta = {
dirname: '/stub/dir',
filename: '/stub/dir/path.js',
url: 'file:///stub/dir/path.js',
resolve(specifier) {
return specifier
}
}
const AsyncFunction = customImport.constructor
const source = `
import { readdir } from 'node:fs/promises'
import { homedir } from 'node:os'
const home = homedir()
export default home
export const files = await readdir(homedir())
`
const importName = '__import__'
const ast = parse(source, { ecmaVersion: 'latest', sourceType: 'module' })
moduleToFunction(ast, { importName })
const transformed = generate(ast)
const fn = new AsyncFunction(importName, transformed)
const result = await fn(customImport)
console.dir(result)
This package only transforms the AST input, which is safe to use on its own. However, it was created with the use case in mind to evaluate an ECMASscript module. Evaluating user input is dangerous and should be avoided whenever possible.
This project is compatible with Node.js 20 or greater.