As you might know, I’m a big fan of [Chrome devtools' live edit and workspaces](http://www.youtube.com/playlist?list=PLXmT1r4krsTq7w7hDV6zfirrs4NJlzJX5) (video playlist), and it’s this workflow that’s kept me away from [Browserify](http://browserify.org).
So I went about creating an experiment that allowed me to use CommonJS modules in development and that allowed me to edit and save directly in devtools without a build step.
[](https://training.leftlogic.com/buy/terminal/cli2?coupon=BLOG\&utm_source=blog\&utm_medium=banner\&utm_campaign=remysharp-discount)
[READER DISCOUNTSave $50 on terminal.training](https://training.leftlogic.com/buy/terminal/cli2?coupon=BLOG\&utm_source=blog\&utm_medium=banner\&utm_campaign=remysharp-discount)
[I’ve published 38 videos for new developers, designers, UX, UI, product owners and anyone who needs to conquer the command line today.](https://training.leftlogic.com/buy/terminal/cli2?coupon=BLOG\&utm_source=blog\&utm_medium=banner\&utm_campaign=remysharp-discount)
Some context[](#some-context)
As far as I know, Browserify is the bees knees for using CommonJS modules in the client side. However, it’s also got a build step. I know it supports sourcemaps, but I’ve personally had mixed (about 20%) success with sourcemaps, and particularly when it comes to saving directly in devtools.
I’m happy with a build step for production, but not in dev. I want to know the files I’m working with are being saved to disk without any extra steps.
It’s entirely possible I’ve reinvented the wheel here (feel free to point me in the right direction!).
So…I had a go at re-inventing the require
method…
Demo[](#demo)
For your viewing pleasure, here’s the experiment using my dev require.js. It supports CommonJS modules. I’ve only tested a few levels deep, and it’s only for client side code.
The main requirements were to ensure:
-
Line numbers in the console mapped correctly to the line in the file
-
Saving the file would commit the save to disk
-
Saving the file would update memory
These kinda work as you’ll see in the video.
How it works[](#how-it-works)
Pretty simple (and stupid) really, require.js is just this:
function require(path) {
var xhr = new XMLHttpRequest();
if (path[0] === '.') {
path = path.substr(2);
}
path += '.js';
var module = {
exports: {}
};
xhr.open('GET', path, false); // sync
xhr.send();
var code = xhr.responseText;
if (code.indexOf('//# sourceURL') === -1) {
code += '\n\n//# sourceURL=' + path;
}
eval(code);
return module.exports;
}
The code boils down to:
-
Make an synchronous XHR call to the script
-
Insert a
sourceURL
in the code so devtools knows what file it was -
Create a
module
object in scope -
Then eval and return the updated
module.exports
Pretty filthy really. Also obviously missing the require path resolution.
I’ve created a little repo with the [code I used in the demo on github](https://github.com/remy/require-for-dev) too.
Known and potential issues[](#known-and-potential-issues)
-
Using the
setInterval
you may have noticed in foo.js, when changing the code, devtools loses access to theapp
variable. Unsure why. -
Using Workspaces is a no-no, it seems to get really confused and very sticky about what’s in memory (i.e. the file shown in sources does not match what’s being run)
-
The full require resolution isn’t implemented at all (so only relative URLs are loaded)
-
Saving the file in devtools will insert the
sourceURL
in the file permanently -
I’m not 100% of the security of the modules - in fact I’m pretty sure (since they’re not running inside a new document context) that there would be namespace collision (which is kind of the point of CommonJS to avoid!!!)
-
Probably a lot more potential issues - like I said, this is an experiment!
Does it really work?[](#does-it-really-work)
I’m not sure. It’s not perfect, and I’m not 100% sure it’s 100% usable…so I’m classing this as a failed experiment.
I thought about seeing if I could make an iframe on the fly, and inject the content, but the iframe would have to be appended to the document to execute the code, and the code is only executed on the "next tick", i.e. after the return module.exports
so the module would be loaded, but the code would not. Poop.
On the upside, I figured it was worth sharing, because some bright mind might just solve the memory linking issues that I’m seeing or create some clever work around.
Published 30-May 2014 under #code & #devtools & #fail. [Edit this post](https://github.com/remy/remysharp.com/blob/main/public/blog/commonjs-with-devtools-live-edit.md)
Comments
Lock Thread
Login
Add Comment[M ↓ Markdown]()
[Upvotes]()[Newest]()[Oldest]()
?
Anonymous
0 points
9 years ago
Did you see this:
I found it after reading your post.
?
Anonymous
0 points
9 years ago
Haven’t left a comment here before so hi… I haven’t seen Browserify before so I’m looking into that now. Here are my thoughts regarding actual projects. I’m completely ignoring the fact that writing a custom solution to something is fun and you learn a lot and just looking at what’s available ready to use.
CommonJS is great in node, but in the client one would generally want asynchronous loading of modules, as there are often many modules that need to be loaded, and I don’t want to have to wait for them to all load one at a time. For this reason I use AMD for my client code, with the RequireJS library. You’ve probably heard of it or used it, but if not it’s the alternative way to load modules. It meets my own requirements of having a fast or no build step in dev (optional build step in prod if you want to combine modules), line numbers match when debugging, modules load as quickly as possible, and it works great with any live reload script. Also, all the modules are loaded asynchronously, there is absolutely zero global variable pollution, and it doesn’t require any hacky code.
AMD module loaders also works with non-AMD libraries (loaded through bower in my case) without modifying them, such as jQuery and TinyMCE (I know, I know, but I didn’t pick this). This isn’t the case with CommonJS module loaders as they require the object to say what it exports. Also I’m not sure how well this would integrate into your devtools workflow as I prefer a full IDE, but I would imagine pretty well.
There are two disadvantages. First, it does require you to wrap your modules in a define(), which also replaces all your require statements from CommonJS; I prefer the CommonJS syntax as it’s cleaner but compromises must be made somewhere. Second, the syntax will be different from your node code, but it’s a different environment and not much else matches anyway, so that doesn’t bother me. And third, you have to specifically say what modules should be combined—determining these is not automatic, although combining them is with grunt. I prefer this as I want my third-party modules combined, but some pages have large amounts of Javascript that are not needed for other pages, so combining everything would be a waste.
It’s absolutely possible to get a secure require function working in client-side Javascript, and I found this post quite interesting. Kudos to you for trying it. For practical projects, I have found AMD to be the solution for now. I’m not sure CommonJS will ever work as well as AMD in the browser, just because the require function is synchronous, and slows it down if you have to load multiple modules. But, if someone can get around that (maybe you?) then I’ll definitely re-consider it.

rem
0 points
9 years ago
I hear you on all these points, and it’s the cons with require.js that left me looking at Browserify. And yeah, this sync approach to loading is only good for dev experimentation - not prod and probably not most dev anyway (again, I’d class this as a failed experiment!).
[Commento](https://commento.io)