Stability: 1 - Experimental

    This feature is only available with the --experimental-vm-modules command flag enabled.

    The vm.Module class provides a low-level interface for using ECMAScript modules in VM contexts. It is the counterpart of the vm.Script class that closely mirrors [Module Record][]s as defined in the ECMAScript specification.

    Unlike vm.Script however, every vm.Module object is bound to a context from its creation. Operations on vm.Module objects are intrinsically asynchronous, in contrast with the synchronous nature of vm.Script objects. The use of ‘async’ functions can help with manipulating vm.Module objects.

    Using a vm.Module object requires three distinct steps: creation/parsing, linking, and evaluation. These three steps are illustrated in the following example.

    This implementation lies at a lower level than the [ECMAScript Module loader][]. There is also no way to interact with the Loader yet, though support is planned.

    1. const vm = require('vm');
    2. const contextifiedObject = vm.createContext({
    3. secret: 42,
    4. print: console.log,
    5. });
    6. (async () => {
    7. // Step 1
    8. //
    9. // Create a Module by constructing a new `vm.SourceTextModule` object. This
    10. // parses the provided source text, throwing a `SyntaxError` if anything goes
    11. // wrong. By default, a Module is created in the top context. But here, we
    12. // specify `contextifiedObject` as the context this Module belongs to.
    13. //
    14. // Here, we attempt to obtain the default export from the module "foo", and
    15. // put it into local binding "secret".
    16. const bar = new vm.SourceTextModule(`
    17. import s from 'foo';
    18. s;
    19. print(s);
    20. `, { context: contextifiedObject });
    21. // Step 2
    22. //
    23. // "Link" the imported dependencies of this Module to it.
    24. //
    25. // The provided linking callback (the "linker") accepts two arguments: the
    26. // parent module (`bar` in this case) and the string that is the specifier of
    27. // the imported module. The callback is expected to return a Module that
    28. // corresponds to the provided specifier, with certain requirements documented
    29. // in `module.link()`.
    30. //
    31. // If linking has not started for the returned Module, the same linker
    32. // callback will be called on the returned Module.
    33. //
    34. // Even top-level Modules without dependencies must be explicitly linked. The
    35. // callback provided would never be called, however.
    36. //
    37. // The link() method returns a Promise that will be resolved when all the
    38. // Promises returned by the linker resolve.
    39. //
    40. // Note: This is a contrived example in that the linker function creates a new
    41. // "foo" module every time it is called. In a full-fledged module system, a
    42. // cache would probably be used to avoid duplicated modules.
    43. async function linker(specifier, referencingModule) {
    44. if (specifier === 'foo') {
    45. return new vm.SourceTextModule(`
    46. // The "secret" variable refers to the global variable we added to
    47. // "contextifiedObject" when creating the context.
    48. export default secret;
    49. `, { context: referencingModule.context });
    50. // Using `contextifiedObject` instead of `referencingModule.context`
    51. // here would work as well.
    52. }
    53. throw new Error(`Unable to resolve dependency: ${specifier}`);
    54. }
    55. await bar.link(linker);
    56. // Step 3
    57. //
    58. // Evaluate the Module. The evaluate() method returns a promise which will
    59. // resolve after the module has finished evaluating.
    60. // Prints 42.
    61. await bar.evaluate();
    62. })();