Like here, Node can do something before the entire module graph is run, but it cannot do anything between running the code in the package and running the code in the dependent module. Like here, it cannot insert anything before console.log call in main, because the inner evaluation is completely controlled by VA. This is why there is no pre-module ESN evaluate hook. Another limitation of ESN is that while ESN namespaces can reflect mutations from within the exporter, it cannot be mutated from the importer. The mutability is mandated by the language and controlled by the JavaScript engine, not by the JavaScript engine embedder. So for example here, we have a module that mutates the count variable. If we attempt to update the count to 5 from the main module, the mutation is a no-op, because the namespace is immutable according to the language. But if we invoke the ink method, which mutates the count from within that module, this will be reflected in the module namespace object.
Also, in this case, the embedder cannot control the namespace mutability. If you want to do some customization of the namespace, it has to be done through modifying the source code in the load hook. We also have some new language features that change how evaluations are done. For example, since 2004, Node supports the Stage 3 source-based import proposal that allows users to customize Wazen evaluation in ESN using the WebAssembly API. Another upcoming Stage 3 feature is deferred evaluation. This is currently being integrated in Node behind a V8 flag. So if you have a module like HeavyJS here, that has a lot of initializations to run, or it just has a huge dependency graph containing a lot of libraries that you do not use, if their exports will only really be needed when the function is called, you can use the defer keyword to defer the evaluation of the less-used modules so that they are only evaluated on demand when the property is accessed. With this you can reduce the code startup time and keep the memory usage low.
So that was a walkthrough of importing ESN in Node. Back to the initial quiz, I think it will be clear now what's happening here. In a snippet, we will first hit the resolution error because that's the first step. If we fix resolution, Node will start trying to load the code and parse the dependencies and the module with syntax error will fail next. After that, if we fix that, we go to instantiation, we run into the error from the mismatched export. Finally we will hit the error from the dependency of the module because evaluation happens at the last stage. So one last thing, we talked about how ESN comes to life in Node, but what keeps it alive? When will it go away? In this case, most of the complexity comes from this idempotency requirement in the language specification. When the module request is the same, the module being returned by the embedder must be the same. For example, here if we have a stateful module that exports something that mutates its inner state and in another module we kind of load it and kind of release it. After some garbage collection, the state should have gone away. But actually, according to the spec, sometime later, even if the original variable seems to go away, when it comes back, being loaded again, it still needs to maintain the original state. So as you can imagine, to achieve this, Node needs to maintain various internal caches. And these internal references can make tools and frameworks kind of point to leaks when they implement, for example, hot reloading ESN in Node. So to address this problem, there's a module clear cache API currently being worked on.
Comments