JavaScript Modules
jsmodules.io
jsmodules.io
* The dependencies are static, so you can't do stuff like `if(Math.random() < .5) { require('foo') } else { require('bar') }`. That is a good thing. A lot of people at this point agree that a statically analyzable dependency graph is a huge win.
* Bindings are truly exported. If you exported `const foo = 5`, imported it in another module, and tried to change it, you would get a static error. Semantics and identity of bindings carry over. This also means they are mutable; if the module exporting `foo` changes it everyone else will see it too.
* A fleshed out module loader API, which is seriously lacking in node
There are other advantages too. The simplistic approach of CommonJS works, but long-term it's not the best solution for the wide use-cases of JavaScript.
EDIT: and this will be backwards-compatible. I don't know all the details but this can be implemented in a browser today without breaking things (I know people on the teams working on this)
Could you elaborate on this point a little more? I've worked on Node.js projects varying greatly in size, and even on the larger ones I've yet to feel encumbered the CommonJS module system.
- https://github.com/cujojs/curl - https://github.com/montagejs/mr - https://github.com/substack/wreq
yeah but the 80% solution of loading them both statically but only executing them when the require is run works ridiculously well in practice.
On top of that, module loaders provide hooks, so it's quite possible to use a module loader (e.g. System.JS) that can load commonjs or amd modules.
Browserify is brilliant, and if I couldn't use es6 I'd be using it, but I'm pretty excited about es6 modules.
I like Lua's approach to modules. In Lua, a file is basically equivalent to a function body. It accepts arguments and can return values. In JavaScript, this is not allowed.
In Lua, a function can accept multiple arguments in a tuple called `...`. Every file is implicitly a vararg function, so we can unpack the `...` to get arguments.
> add.lua
local a,b = ...
return a + b
> file.lua
local add = load 'add.lua'
print(add(1,2)) --> 3
load takes a file path and returns that source file as a function.
Most files ignore arguments, but they do return values. > add2.lua
return function(a,b) return a + b end
> file2.lua
local add = require 'add'
print(add(1,2))
`require` loads a source file, executes it, and returns the value that it returns. It is similar to `return load(fileName)()`, but it also has a search mechanism, and caches the result. So, the Lua equivalent of export default is simply returning a value at the end of a source file.The named exports are also easy to replicate. You simply return an object with the keys you want.
> math.lua
return {
add = function(a,b) return a + b end,
sub = function(a,b) return a - b end
}
> other.lua
local math = require 'math'
local add, sub = math.add, math.sub
You can also have something similar to the `export function name(){}` declaration. > hello.lua
local exports = {}
function exports.hello()
print "Hello World"
end
exports.VERSION = '0.0.1'
return exports
> file3.lua
local hello = require'hello'.hello
local version = require'hello'.VERSION
hello() --> Hello World
In all, I think the Lua's require is a much cleaner way to implement modules. Unfortunately, it cannot handle circular references. It may be possible to add this feature using Lua's coroutines, however. I will have to research how it is handled in JavaScript to be sure.The idea behind ES6 modules is to add static name resolution to JavaScript. Because of the incredibly dynamic nature of JS, you can never know what any global name resolves to, as it could change at a moment's notice. The same is true in Lua's module system: `_G` is completely mutable and it (or its metatable!) can change at any time, altering the bindings in the program.
I personally feel that, given how JavaScript is getting used in larger and larger codebases, some measure of static checking was long overdue.
Wouldn't this be bad design in 99% of circumstances anyway? I say 99% because I don't want to be categorical, personally I think circular references/dependencies are always bad on some level.
ES6 Modules officially add modules to the language. It gives a standard that should work in browser and in node. It's heavily inspired by CommonJS and AMD.
bonus points because you can hook a linter into it.
Yep. Is that so bad? You know you can have multiple Terminal.app's open at once, right?
> The nice thing about the web is that with a simple http server you can just start coding.
The nice thing about browserify/node is that I can install node, clone my seed script, execute it, and just start coding.
> Compile-steps take away that incredible advantage.
While giving you the huge advantages of TDD/unit testing; the npm repository; being able to use the CommonJS module pattern...
If you're just woodshedding a toy, or trying to learn - by all means, set up lighttpd and boot sublime and go at it. But if you're trying to build production-quality software... hone your craft; be better than a codemonkey.
You don't need a compile step to do unit testing. Or even to use CommonJS for that matter.
> If you're just woodshedding a toy, or trying to learn - by all means, set up lighttpd and boot sublime and go at it. But if you're trying to build production-quality software... hone your craft; be better than a codemonkey.
You don't need a compile-step in development to create good software. Client-side loaders have all of the advantages of server-side loaders but you can also use them without a watch task. ES6 modules are just going to make it all easier.
Okay, I guess I could have a window with karma open and keep refreshing it every time I change code. Having an eshell open running my unit tests every time I save is a lot faster, though. Unfortunately, most of what I do is remote over SSH instead of having a local development environment. And that doesn't even begin to cover building a jenkins (or other CI) script to run your non-compile-time unit tests.
> Or even to use CommonJS for that matter.
Which client side loaders would you recommend? I've used AMD/RequireJS in the past and its headaches were not worth the benefits. The only other project in the running is Webpack.
> but you can also use them without a watch task
I suppose I don't understand the watch task hate. It's there. It runs. It versions my build. It lets me know when I've saved something stupid. It's faster than tabbing to a browser and going through the steps to reproduce the iota of code I just wrote, or refreshing a testrunner page.
How do you do continuous integration and save-time unit tests without a preprocessing step without tying your project to a bloated IDE?
> ES6 modules are just going to make it all easier.
When ES6 modules come around to being available in IE9 (or IE9 finally dies), I'll believe that you can do all this without a processing step. Even when ES6 modules become available in modern browsers, you'll still have to run everything through a preprocessing build step to get backwards compatibility. And woe to whomever tries to do dynamic dependency loading on mobile... that 200-2000ms per-http-request overhead on 3G will bite you right in the bounce rate.
The transition from ES5 to ES6 will be something interesting to live.ES6 is a paradigm shift for Javascript.
If I just include it in a <script> tag directly, it'll throw syntax errors in any non-es6 compliant browser. So I'd have the transpile, and send down the es5 version of the code.
But if I'm transpiling, what's the point? I still have a build step, so why use ES6 over another compile-to-js language? The eventual promise, that years and years and years from now, all the non-es6 compliant browser will be dead, so I can send down the es6 code directly?
<script type="module">
// loads the 'q' export from 'mymodule.js' in the same path as the page
import { q } from 'mymodule';
new q(); // -> 'this is an es6 class!'
</script>
You can use it today with things like : https://github.com/ModuleLoader/es6-module-loader import { getCodec } from "iconv-lite";
would be var getCodec = require("iconv-lite").getCodec; {getCodec} = require "iconv-lite"
Explicit dependencies without introducing new syntax? Yes, please.Just because we all got on the js train doesn't mean we have to ride it till the end.
I guess this is why there are so many cross compilers from x -> js. Thanks be to those guys. Cheers.
Chrome supports JavaScript and Dart.
I quite like npm as far as package managers go (the default installation directory being ./node_modules is a big improvement over the global rubygem directory, IMO).
All the same, I don't mean to be overly negative but code like this is why I am not a fan of JS:
var isNode = typeof process !== "undefined" && {}.toString.call(process) === "[object process]";
I mean, I am no JS expert but it took me a couple of minutes just to parse this thing, and this is the next generation. I just don't like it.
You can try to create workarounds and hacks, but that's pretty much building a new package manager on top of npm.
The core problem is that if your package depends on another package, it's impossible to know the location of that package (no, it's not always or even often under node_modules).
There's of course a lot of other problems with npm I've noticed..
Other problems include that npm is not really supported on Windows and specifying a 'git'-based url always installs the package even if it exists on a lower level (which works completely different from other version types). There's also weird cache bugs from time to time (npm cache clean helps there). And if you have a single package dependency that's missing, you can't force it to finish npm install.
I can understand why installing a package from a version control URL should skip checks, since the very fact that you're using the direct URL kind of indicates that you want that specific commit.
I've literally never run into a missing dependency with npm. How did you get there? Are you installing packages from several different repositories? What good would forcing do if you're missing a dependency? If you know you don't really need it can't you stub it?
TBH I'm not that concerned with support on Windows. shrug
Yes, that's right. Since the application I'm working on is composed of multiple TypeScript-based Node.js modules, the type information has to be carried from one module to another. Since npm doesn't do this well, a new package manager was actually written (TSD, TypeScript Definition manager).
>I can understand why installing a package from a version control URL should skip checks, since the very fact that you're using the direct URL kind of indicates that you want that specific commit.
No, the version url doesn't need to point to a specific commit. npm really should use the semantics for all version urls. If I have two packages that both point to say "myrepo.git", then there shouldn't be two duplicates.
>I've literally never run into a missing dependency with npm. How did you get there? Are you installing packages from several different repositories? What good would forcing do if you're missing a dependency? If you know you don't really need it can't you stub it?
The missing dependency can be caused by a single Git repository being down. I encountered this when our private Git repository wasn't accessible for a while due to firewall issues, but I still wanted to keep development going on until that got fixed. The only solution is to manually remove that dependency and install it manually, then revert changes when the Git repository becomes accessible. npm install --force would be better.
>TBH I'm not that concerned with support on Windows. shrug
I mainly develop on Linux, but customers want Windows support. The main problem is documentation. Node.js download page doesn't say that Windows support is not reliable. There's various problems on Windows, one main one is apparently worked on ( https://github.com/npm/npm/issues/3697 ).
[1] https://github.com/google/traceur-compiler [2] https://github.com/toshok/echo-js
[1] https://github.com/broccolijs/broccoli [2] https://github.com/sindresorhus/broccoli-es6-transpiler
I don't see how it still can't be crucially important.
var a = 1;
process.nextTick(function() {
console.log(a);
});
a = 2;sign up here: modular-js-coursera-tech-talk.eventbrite.com
> var isNode = typeof process !== "undefined" &&
> {}.toString.call(process) === "[object process]";
WHAT DO YOU THINK YOU'RE RUNNING FROM?! THE DISEASE IS INSIDE OF YOU!As an outsider is seems... cryptic.