Migrating from Webpack 1.x to 2.x - javascript

In Webpack 1.x I used to do the following on regular basis:
require.ensure([ './mod2.js' ], ( require ) => {
setTimeout(() => {
// some later point in time, most likely through any kind of event
var data = require( './mod2.js' ); // actual evaluating the code
},1100);
}, 'myModule2');
With this technique, we were able to transfer a webpack-bundle over the wire, but evaluate the actual contents (the JavaScript code) from that bundle at some later point in time. Also, using require.ensure we could name the bundle, in this case myModule2, so we could see the name / alias when bundling happened executing webpack.
In Webpack 2.x, the new way to go is using System.import. While I love receiving a Promise object now, I have two issues with that style. The equivalent of the above code would look like:
System.import( './mod2.js' ).then( MOD2 => {
// bundle was transferred AND evaluated at this point
});
How can we split the transfer and the evaluation now?
How can we still name the bundle?
The Webpack documentation on Github says the following:
Full dynamic requires now fail by default
A dependency with only an expression (i. e. require(expr)) will now
create an empty context instead of an context of the complete
directory.
Best refactor this code as it won't work with ES6 Modules. If this is
not possible you can use the ContextReplacementPlugin to hint the
compiler to the correct resolving.
I'm not sure if that plays a role in this case. They also talk about code splitting in there, but it's pretty briefly and they don't mention any of the "issues" or how to workaround.

tl;dr: System.resolve and System.register do most of what you want. The rest of this answer is why require.ensure cannot and how System.import calls the others.
I think ES6 modules prevent this for working well, although following it through the relevants specs is tricky, so I may be totally wrong.
That said, let's start with a few references:
the WhatWG module loader
the ES6 specification on modules (§15.2)
the CommonJS module specification
the fantastic 2ality article on ES6 modules
The first reference explains more behavior, although I'm not entirely sure how normatize it is. The latter explains the implementation details on the JS side. Since no platforms implement this yet, I don't have references for how it actually work in real life, and we'll have to rely on the spec.
The require that has been available in webpack 1.x is a mashup of the CommonJS and AMD requires. The CommonJS side of that is described in ref#3, specifically the "Module Context" section. Nowhere does that mention require.ensure, nor does the AMD "specification" (such as it is), so this is purely an invention of webpack. That is, the feature was never real, in the sense of being specified somewhere official and fancy looking.
That said, I think require.ensure conflicts with ES6 modules. Calling System.import should invoke the import method from a Loader object. The relevant section in ref#2 does not lay that out explicitly, but §10.1 does mention attaching a loader to System.
The Loader.prototype.import method is not terribly involved, and step 4 is the only one that interests us:
Return the result of transforming Resolve(loader, name, referrer) with a fulfillment handler that, when called with argument key, runs the following steps:
Let entry be EnsureRegistered(loader, key).
Return the result of transforming LoadModule(entry, "instantiate") with a fulfillment handler that, when called, runs the following steps:
Return EnsureEvaluated(entry).
The flow is resolve-register-load-evaluate, and you want to break between load and evaluate. Note, however, that the load stage calls LoadModule with stage set to "instantiate". That implies and probably requires the module has already been translated via RequestTranslate, which does much of the heavy parsing as it tries to find the module's entry point and so on.
This has already done more work than you want, from the sounds of it. Since the basics of module loading require a known entry point, I don't think there's a way to avoid parsing and partially evaluating the module with the calls exposed from System. You already knew that.
The problem is that System.import can't possibly know -- until after parsing -- whether the module is an ES6 module that must be evaluated or a webpack bundle that could be deferred. The parsing must be done to figure out if we need to parse, leading to a chicken-and-egg problem.
Up to this point, we've been following the path from System.import through the Loader. The import call is dictating what stage of import we're at, assuming you want to go through the full end-to-end loading process. The underlying calls, like Loader.prototype.load, provide fine grained control over those stages.
I'm not sure how you would invoke the first two stages (fetch and translate), but if you were able to translate and register a module, later calls should simply evaluate and return it.
If the spec is accurate, this should be exposed (in supporting implementations) through the System.loader property and would have the methods you need to call. There are large parts of the flow you don't have access to, so I would suggest not doing this and instead setting up your code so nothing significant runs when a module is loaded. If that's not possible, you need to recreate the flow up through registering but stop shy of evaluation.

Related

Is there a way to access/modify the "Execution Context" of a Node module/function

I understand the scoping rules of Node/JavaScript, and from, for example, Understanding Execution Context and Execution Stack in Javascript, I think I understand the principle of how Execution Contexts work: the question is can you actually get access to them?
From an answer to the 2015 question How can I get the execution context of a javascript function inside V8 engine (involving (ab)using (new Error()).stack), and the answer to the 2018 question How can we get the execution context of a function?, I suspect the answer is "no" but just in case things have changed: is it possible to access/modify the Execution Context of a Node module/function?
(I'm very aware this screams either XY Problem or a desire to abuse Node/JavaScript: hopefully the Background section below will provide more context, and – if there's a different way of achieving what I want – that will work as well.)
In a nutshell, I want to achieve something like:
sharedVar = 1 ;
anotherModule.someFunction() ;
console.log( sharedVar ) ; // Where 'sharedVar' has changed value
Having a function in a different module being able to change variables in its caller's scope at will seems the definition of "A Dangerous Thing™", so – if it's possible at all – I expect it would need to be more like:
sharedVar = 1 ;
anotherModule.hereIsMyExecutionContext( SOMETHING ) ;
anotherModule.someFunction() ;
console.log( sharedVar ) ; // Where 'sharedVar' has changed value
and anotherModule would be something like:
let otherExecutionContext ;
function hereIsMyExecutionContext( anExecutionContext ) {
otherExecutionContext = anExecutionContext ;
}
function someFunction() {
//do something else
otherExecutionContext.sharedVar = 42 ;
}
and the question becomes what (if anything) can I replace SOMETHING with?
Notes / Things That Don't Work (for me)
You shouldn't be trying to do this! I realize what I'm trying to achieve isn't "clean" code. The use-case is very specific, where brevity (particularly in the code whose value I want changing) is paramount. It is not "production" code where the danger of forgotten, unexpected side-effects matters.
Returning a new value from the function. In my real use-case, there would be several variables that I would like someFunction() to be able to alter. Having to do { var1, var2, ... varN } = anotherModule.someFunction() would be both inconvenient and distracting (while the function might change some of these variables' values, it is not the main purpose of the function).
Have the variables members of anotherModule. While using anotherModule.sharedVar would be the clean, encapsulated way of doing things, I'd really prefer not to have to use the module name every time: not only is it more typing, but it distracts from what the code that would be using these variables is really doing.
Use the global scope. If I wasn't using "use strict";, it would be possible to have sharedVar on the global object and freely accessible by both bits of code. As I am using strict-mode (and don't want to change that), I'd need to use global.sharedVar which has the same "cumbersomeness" as attaching it to anotherModule.
Use import. It looks like using import { sharedVar } from anotherModule allows "live" sharing of the variables I want between the two modules. However, by using import, the module using the shared variable has to be an ES Module (not a CommonJS Module), and cannot be loaded dynamically using require() (as I also need it to be). Loading it dynamically the ESM way (using import() as a function returning a promise) appears to work, but repeated loadings come from a cache. According to this answer to How to reimport module with ES6 import, there isn't a "clean" way of clearing that cache (cf. delete require.cache[] that works with CommonJS modules). If there is a clean way of invalidating a cached ESM loaded through import(), please feel free to add an answer to that question (and a comment here, please), although the current Node JS docs on ESMs don't mention one, so I'm not hopeful :-(
Background
In early December, a random question on SO alerted me to the Advent of Code website, where a different programming problem is revealed everyday, and I decided to have a go using Node. The first couple of problems I tackled using standalone JS files, at which point I realized that there was a lot of common code being copy-pasted between each file. In parallel with solving the puzzles, I decided to create a "framework" program to coordinate them, and to provide as much of the common code as possible. One goal in creating the framework was that the individual "solution" files should be as "lean" as possible: they should contain the absolute minimum code over that needed to solve the problem.
One of the features of the framework relevant to this question is that it reloads (currently using require()) the selected solution file each time, so that I can work on the solution without re-running the framework... this is why switching to import and ES Modules has drawbacks, as I cannot (cleanly) invalidate the cached solution module.
The other relevant feature is that the framework provides aoc.print(...) and aoc.trace(...) functions. These format and print their arguments: the first all the time; the second conditionally, depending on whether the solution is being run normally or in "trace" mode. The latter is little more than:
function trace( ... ) {
if( traceMode ) {
print( ... )
}
}
Each problem has two sets of inputs: an "example" input, for which expected answers are provided, and the "real" input (which tends to be larger and more involved). The framework would typically run the example input with tracing enabled (so I could check the "inner workings") and run the "real" input with tracing disabled (because it would produce too much output). For most problems, this was fine: the time "wasted" preparing the parameters for the call, and then making the call to aoc.trace() only to find there was nothing to do, was negligible. However, for one solution in particular (involving 10 million iterations), the difference was significant: nearly 30s when making the ignored trace; under a second if they calls were commented-out, or I "short-circuited" the trace-mode decision by using the following construct:
TRACE && aoc.print( ... )
where TRACE is set to true/false as appropriate. My "problem" is that TRACE doesn't track the trace mode in the framework: I have to set it manually. I could, of course, use aoc.traceMode && aoc.print( ... ), but as discussed above, this is more to type than I'd like and makes the solution's code more "cluttered" than I'd ideally want (I freely admit these are somewhat trivial reasons...)
One of the features of the framework relevant to this question is that it reloads the selected solution file each time
This is key. You should load not leave the loading, parsing and execution to require, where you cannot control it. Instead, load the file as text, do nefarious things to the code, then evaluate it.
The evaluation can be done with eval, new Function, the vm module, or by messing with the module system.
The nefarious things I was referring to would most easily be prefixing the code by some "implicit imports", whether you do that by require, import or just const TRACE = true;. But you can also do any other kind of preprocessing, such as macro replacements, where you might simply remove lines that contain trace(…);.
Looks like you're heading the wrong way. The Execution Context isn't meant to be accessible by the user code.
An option is to make a class and pass its instances around different modules for your purpose. JS objects exist in the heap and can be accessed anywhere as long as you have its reference, so you can control it at will.

Does it matter that we can't easily predict the exact order that modules will execute in?

Thanks to this post I've become aware, for modules (not regular scripts & without async attribute) that import/export (from each other), one of the only simple rules that determines the order in which they're executed is that a module that imports from another module will not execute before the module it imports from.
I'm worried we can't always control the complete order in which modules execute (I've put some examples of this at the bottom of the post). For example, what if I want a timeout to start as soon as the page is loaded? Is it okay for a setTimeout to occur on a module (at the end of a very long list of modules) as the time to execute the initial modules will be negligible?
P.s. Am I safe to assume modules that don't import or export (from each other) always execute in the order they appear in?
Examples: ModuleA imports from ModuleB. ModuleC imports from ModuleD. Only rule is that D or B executes first (sometimes DB or BD executes first; sometimes DC or BA executes first).
I personally find that assuming something will happen before something else without enforcing that explicitly will result in a dangerous situation. Even if your modules would always execute in the correct order, you have created an implicit dependency between two modules that will make the code more difficult understand.
Will another developer working on your project be able to understand that certain modules need to execute first? Will you remember this when you revisit this code a few months down the line? What about when you leave the project and someone else inherits it?
Working on code like this can create spaghetti situations where making a change in one place creates cascading changes in other, unexpected places.
Alleviating this can really depend on what framework you're using, or if you're not using any framework at all. If you need certain dependencies loaded before executing certain code, use a dependency loader to ensure that dependencies are always available when they are needed. If you need a certain function to be executed before executing a function in a different module, create an event system. Redux/flux and Ngrx are really nice solutions for situations like this.

Node js, split up files without having to pass dependencies around?

This may be just me lacking a 'bigger picture' so to speak, but I'm having trouble understanding why exporting modules is needed to just split up files.
I tried doing something like this:
//server.js
var app = require('koa')();
var othermodule1 = require('othermodule1')();
var othermodule2 = require('othermodule2')();
var router = require('./config/routes')();
app.use(router.routes());
//routes.js
module.exports = require('koa-router')()
.get('*', function*(next){
othermodule1.something;
})
realizing that routes.js does not have access to 'othermodule1' after calling it from serverjs. I know that there's a way to pass needed variables during the require call, but I have a lot more than just 2 modules that I would need to pass. So from my probably naive perspective, this seems somewhat unnecessarily cumbersome. Someone care to enlighten me or is there actually a way to do this that I missed?
Each node.js module is meant to be a stand-alone sharable unit. It includes everything that it needs to do its job. That's the principle behind modules.
This principle makes for a little more overhead at the start of each module to require() in everything you need in that module, but it's only done once at the server startup and all modules are cached anyway so it isn't generally a meaningful performance issue.
You can make global things by assigning to the global object, but they that often breaks modularity and definitely goes against the design spirit of independently shareable modules.
In your specific code, if routes needs access to othermodule1, then it should just require() it in as needed. That's how modules work. routes should just include the things it needs. Modules are cached so requiring it many times just gives every require() the same module handle from a cache.
This is an adjustment in thinking from other systems, but once you get use to it, you just do it and it's no big deal. Either require() in what you need (the plain shareable module method) or pass something into a module on its constructor (the push method) or create init() methods so someone can initialize you properly or call some other module to get the things you need (the pull method).

ES6 exports/imports use-case, compared to traditional namespacing

I don't understand WHY and in what scenario this would be used..
My current web setup consists of lots of components, which are just functions or factory functions, each in their own file, and each function "rides" the app namespace, like : app.component.breadcrumbs = function(){... and so on.
Then GULP just combines all the files, and I end up with a single file, so a page controller (each "page" has a controller which loads the components the page needs) can just load it's components, like: app.component.breadcrumbs(data).
All the components can be easily accessed on demand, and the single javascript file is well cached and everything. This way of work seems extremely good, never saw any problem with this way of work. of course, this can (and is) be scaled nicely.
So how are ES6 imports for functions any better than what I described?
what's the deal with importing functions instead of just attaching them to the App's namespace? it makes much more sense for them to be "attached".
Files structure
/dist/app.js // web app namespace and so on
/dist/components/breadcrumbs.js // some component
/dist/components/header.js // some component
/dist/components/sidemenu.js // some component
/dist/pages/homepage.js // home page controller
// GULP concat all above to
/js/app.js // this file is what is downloaded
Then inside homepage.js it can look like this:
app.routes.homepage = function(){
"use strict";
var DOM = { page : $('#page') };
// append whatever components I want to this page
DOM.page.append(
app.component.header(),
app.component.sidemenu(),
app.component.breadcrumbs({a:1, b:2, c:3})
)
};
This is an extremely simplified code example but you get the point
Answers to this are probably a little subjective, but I'm going to do my best.
At the end of the day, both methods allow support creating a namespace for a piece of functionality so that it does not conflict with other things. Both work, but in my view, modules, ES6 or any other, provide a few extra benefits.
Explicit dependencies
Your example seems very bias toward a "load everything" approach, but you'll generally find that to be uncommon. If your components/header.js needs to use components/breadcrumbs.js, assumptions must be made. Has that file been bundled into the overall JS file? You have no way of knowing. You're two options are
Load everything
Maintain a file somewhere that explicitly lists what needs to be loaded.
The first option is easy and in the short term is probably fine. The second is complicated for maintainability because it would be maintained as an external list, it would be very easy to stop needing one of your component file but forget to remove it.
It also means that you are essentially defining your own syntax for dependencies when again, one has now been defined in the language/community.
What happens when you want to start splitting your application into pieces? Say you have an application that is a single large file that drives 5 pages on your site, because they started out simple and it wasn't big enough to matter. Now the application has grown and should be served with a separate JS file per-page. You have now lost the ability to use option #1, and some poor soul would need to build this new list of dependencies for each end file.
What if you start using a file in a new places? How do you know which JS target files actually need it? What if you have twenty target files?
What if you have a library of components that are used across your whole company, and one of they starts relying on something new? How would that information be propagated to any number of the developers using these?
Modules allow you to know with 100% certainty what is used where, with automated tooling. You only need to package the files you actually use.
Ordering
Related to dependency listing is dependency ordering. If your library needs to create a special subclass of your header.js component, you are no longer only accessing app.component.header() from app.routes.homepage(), which would presumable be running at DOMContentLoaded. Instead you need to access it during the initial application execution. Simple concatenation offers no guarantees that it will have run yet. If you are concatenating alphabetically and your new things is app.component.blueHeader() then it would fail.
This applies to anything that you might want to do immediately at execution time. If you have a module that immediately looks at the page when it runs, or sends an AJAX request or anything, what if it depends on some library to do that?
This is another argument agains #1 (Load everything) so you start having to maintain a list again. That list is again going to be a custom things you'll have come up with instead of a standardized system.
How do you train new employees to use all of this custom stuff you've built?
Modules execute files in order based on their dependencies, so you know for sure that the stuff you depend on will have executed and will be available.
Scoping
Your solution treats everything as a standard script file. That's fine, but it means that you need to be extremely careful to not accidentally create global variables by placing them in the top-level scope of a file. This can be solved by manually adding (function(){ ... })(); around file content, but again, it's one more things you need to know to do instead of having it provided for you by the language.
Conflicts
app.component.* is something you've chosen, but there is nothing special about it, and it is global. What if you wanted to pull in a new library from Github for instance, and it also used that same name? Do you refactor your whole application to avoid conflicts?
What if you need to load two versions of a library? That has obvious downsides if it's big, but there are plenty of cases where you'll still want to trade big for non-functional. If you rely on a global object, it is now up to that library to make sure it also exposes an API like jQuery's noConflict. What if it doesn't? Do you have to add it yourself?
Encouraging smaller modules
This one may be more debatable, but I've certainly observed it within my own codebase. With modules, and the lack of boilerplate necessary to write modular code with them, developers are encouraged to look closely on how things get grouped. It is very easy to end up making "utils" files that are giant bags of functions thousands of lines long because it is easier to add to an existing file that it is to make a new one.
Dependency webs
Having explicit imports and exports makes it very clear what depends on what, which is great, but the side-effect of that is that it is much easier to think critically about dependencies. If you have a giant file with 100 helper functions, that means that if any one of those helpers needs to depend on something from another file, it needs to be loaded, even if nothing is ever using that helper function at the moment. This can easily lead to a large web of unclear dependencies, and being aware of dependencies is a huge step toward thwarting that.
Standardization
There is a lot to be said for standardization. The JavaScript community has moved heavily in the direction of reusable modules. This means that if you hope into a new codebase, you don't need to start off by figuring out how things relate to eachother. Your first step, at least in the long run, won't be to wonder whether something is AMD, CommonJS, System.register or what. By having a syntax in the language, it's one less decision to have to make.
The long and short of it is, modules offer a standard way for code to interoperate, whether that be your own code, or third-party code.
Your current process is to concatenate everything always into a single large file, only ever execute things after the whole file has loaded and you have 100% control over all code that you are executing, then you've essentially defined your own module specification based on your own assumptions about your specific codebase. That is totally fine, and no-one is forcing you to change that.
No such assumptions can be made for the general case of JavaScript code however. It is precisely the objective of modules to provide a standard in such a way as to not break existing code, but to also provide the community with a way forward. What modules offer is another approach to that, which is one that is standardized, and one that offers clearer paths for interoperability between your own code and third-party code.

Mutually recursive AMD modules

I am trying to write a reasonably chunky client side web app using Javascript and jQuery. In order to organise my code, I read up on Javascript module systems and decided to go with AMD modules. Currently I'm using curl.js as my module loader, but I'm not particularly wedded to that.
Unfortunately I've now run up against an issue where two of my modules need to be mutually dependent. I was expecting it to Just Work --- but what actually happens is that loading the app just seems to stall half-way through and everything stops, with no error messages.
A quick Google shows practically no mention whatsoever of AMD and mutually recursive modules. Can I actually do this, and if so, how? (Do I need to change to a different module loader?)
If not, any suggestions on an alternative module system which does support mutually recursive modules?
So after realising that an alternative name for 'mutual recursion' is 'circular dependencies', I found some references online (notably the require.js manual page on the topic).
The short summary is: no, this doesn't work. There are various ways to get round it, but it fundamentally does not Just Work.
The simplest workaround is to break the dependency chain using an explicit synchronous require() call:
define(
["require", "NotLoadedYet"],
function (require, NLY)
{
// NLY is undefined here
return {
doSomething: function()
{
var realNLY = require("NotLoadedYet"); // fetch the real NLY on demand
realNLY.doSomething(); // actuall call the method
}
};
}
);
Obviously this only works if you can guarantee that NotLoadedYet really has been loaded by the time you call the method.
The idea of using dynamic late-binding in a language which already does dynamic late-binding is pretty ew, but it does work. Sigh. There seems to be a slightly less ew technique which involves changing to use requirejs's CommonJS support instead, but I don't know how that works so I'm sticking to this.
What I'll actually do is implement a NotLoadedYetImpl module which contains the implementation and a NotLoadedYet module which proxies through the above mechanism. It's a shame Javascript doesn't do getters and setters on all properties for an object, or I could do it all automatically, too...

Categories