Nested Imports Proposal


#1

As some of you may have noticed, Meteor recently added support for using “nested imports”.

I say recently, because before Meteor 1.3.3, doing an import outside of the top level of a module resulted in an error. With 1.3.3, Meteor started using @benjamn’s Reify transpiler which adds support for nested imports.

Interestingly, nested imports go against the ES6 spec. Using nested imports in your modules will make them incompatible (out of the box) with other ES6 tools and systems like Babel, vanilla Mocha, Wallaby, etc…

That’s not to say nested imports don’t have their benefits. I recently wrote up an article discussing this situation and explaining the justifications for using Reify and nested imports within Meteor.

I’m interested to see how @benjamn’s proposal to the EcmaScript committee goes at the end of this month.

TL, DR; Keep in mind that nested imports aren’t a widely supported feature in the larger Javascript community.


#2

I took the time to read Ben’s arguments this morning and they make a lot of sense. However, like you, I await the minutes of the Ecmascript committee meeting with anticipation (but I’m not holding my breath).


#3

Yep. It’ll be especially interesting if the committee rejects the proposal (I’m not totally sure how the proposal process works?). In that situation, will Meteor move away from nested imports, or stay deviated from the ES6 spec?


#4

Several things are worth mentioning here:

  1. People use tons of features in ES that are not anywhere close to being a standard. That’s the wonder of transpilation - you can use any JavaScript you want!
  2. You don’t need to use this feature if you don’t want to. I think it’s a stretch to say that enabling a new, optional feature is “distancing from the JavaScript community”
  3. reify isn’t on by default in Babel, but you can use it in any JavaScript project outside of Meteor, including in your non-Meteor mocha tests.

Also, people already do this stuff all the time in the “JavaScript community”, they just don’t call it “nested imports” - instead, people will just put require inside a conditional. Which, given today’s most popular build tools, specifically Meteor, Webpack, and Browserify, does exactly the same thing.

For example, see this example from the React codebase: https://github.com/facebook/react/blob/15ae5857f6a86ead4143dc100c61d27736cf29d9/src/isomorphic/classic/types/checkReactTypeSpec.js#L22-L33

This is a nested import in everything but name.


#5

Like all of you seem to have expressed, I hope that TC39 considers the need seriously. I see examples of nested imports in the form of requires in most npm packages I look at. It isn’t just Meteor that needs this. In fact, in terms of sheer volume of existing examples, I’d bet that the nested requires in npm packages dwarfs our usage. As the npm community at large moves to ES6 (and they are fast doing it), I think they will become the largest consumers of Ben’s reify package.

Also, like sashko, I’m not sure it matters. Our nested imports are currently working and getting transpiled to CommonJS. If in some distant future all of our platforms have native ES6 support, then I think they’ll just get transpiled to System.Import calls. So, Meteor code with nested imports is 100% compatible with the larger npm ecosystem with or without TC39 support.

The biggest thing I think TC39 has missed is that this whole argument is one of semantics. They already support nested imports. They just choose to make us use two different syntaxes. Not being able to statically analyze the nested import is the same boat as not being able to statically analyze programmatic system.imports. You just punt on the nested ones and move on if your analyzer isn’t smart enough to figure it out either way. Adding nested imports does not in any way prevent them from statically analyzing the top-level imports and, as Ben’s writeup stated, nested imports have more potential for static analysis than nested system.import calls.


#6

while I was searching on how the meeting went … this way please


#7

Looks like some people are excited about it!

BTW - @pcorey since this thread is a natural place to talk about how the proposal is going, would you mind renaming the thread to something that doesn’t include “controversy”? It seems to imply something bad, but this looks to me like a pretty reasonable process of proposing to extend the ES standard to better support apps with code in different environments.


#8

Sure thing. Renamed to “Nested Imports Proposal”.


#9

Wait, don’t do this! You’re wrecking my Friday “negative connotation meteor forum post title” drinking game!!!


#10

Getting back to these points,

  1. Sure, people do lots of things that aren’t standard javascript and transpile it into standard Javascript. Those other things are usually labeled as such: CoffeeScript, TypeScript, SweetJS, etc… Those distinctions are important because at the point when a standard Javascript interpreter can no longer interpret your code, is it no longer Javascript.

  2. I agree, you don’t need it, and none of the guide articles promote its use yet. In all honesty, I’m not against it. Ben’s arguments have more than convinced me that it should be part of the ES6 spec. I’m only saying that the pros and cons should be clearly laid out before it becomes widespread practice in the Meteor community, otherwise we might eventually have lots of “Why can’t I make this code into an NPM package?” threads.

  3. I’m not sure how I would incorporate Reify and Babel into my asset pipeline. Admittedly, I haven’t spent much time looking into it, but it’s an open issue.

And, in practice, you’re right about nested require statements being “nested imports in everything but name”. The difference is that the entire module system and require infrastructure is written in standard JS, and doesn’t require a transpilation step.

At the end of the day, “Nested Imports Controversy” may be been a reactionary title, and I regret that. While I do see the need for nested ES6 imports on a daily basis, and fully support Ben’s proposal, I do think that introducing non-standard syntax should be done very sparingly. Especially when we’ve gone through so many growing pains trying to align ourselves with the larger community.


#11

They can also be labeled “Babel stage 0”: https://babeljs.io/docs/plugins/preset-stage-0/

These are features that are about as likely to get into ES as Ben’s, and plenty of people use them and call it JavaScript!

But I agree, it’s good to be aware of the current status of any features one is using that aren’t completely stable.


#12

At the moment, you have a point. I’ve not figured out how to use reify directly myself.

But node does not support ES6 modules at all today. It is in the works, but still in the future. Yet many npm packages are using ES6 modules via transpilation to ES5, mostly with babel. Transpilation in npm modules seems to be the rule. Almost every package I look at uses it.

As soon as there is a babel plugin for nested imports, then there will be no problem with making npm packages from code with nested imports. I figure the babel plugin will be coming quick. Creating it is pretty much a standard part of the stage 0 process, and Ben has already mentioned it.

Also, I feel sure that by the time enough meteor packages are ported to npm (or some magic is created allowing their use from npm) to allow me to write more than the most trivial packages as npm packages, the babel plugin will be in place.


#13

This is a bit of an aside (though related), but the vaunted static analysis “enabled” by ES modules has currently got a nuclear-sized hole in it. I don’t see it allowing full static analysis that would be of great use by things-that-have-to-work until all non-statically analyzable things disappear from the scene (maybe in 2026?).

I am currently working on a tool that “simply” needs to be able to determine the exports of all files that could be imported into a module. It needs to do this during development, i.e. without running the code, i.e. it needs to be able to statically analyze the exports of everything that the application might be able to access.

It simply isn’t possible at this stage of the game to achieve this with 100% accuracy even with a pure ES module. The proverbial straw in the ES module specification is that keeps even some ES modules from being statically analyzable is export * from 'x';. If ‘x’ is anything not statically analyzable (a CommonJS module for example), then the exports of the module using that statement are not statically analyzable.

This makes it impossible to consistently accomplish some pretty simple (and common in other languages) development-time activities. For example, in the following code, whether yStuff.hello exists cannot be determined by your editor while editing the file ‘x’ and a “goto” hello feature can’t be implemented.

// let module 'z' be from some other module system that can't be statically analyzed

// in ES module 'y'
export * from 'z';

// in ES module x
import * as yStuff from 'y';

yStuff.hello();

Very few npm packages are currently statically analyzable. Even if they’ve adopted ES modules, the package.json leads you to the transpiled main instead of the true source ES module.

This is where the Meteor package system is ahead of npm. We have an interface definition (as given by the api.exports) that can be utilized to describe older package interfaces.

I hope Ben somehow convinces the npm community to add some equivalent of this to npm’s package.json before we go there. It would be very helpful to future development tools to be able to automatically determine the interface to a package. They can’t exactly read the README. A Meteor-specific extension to package.json doesn’t cut it. It should be something the whole community uses.

If we don’t get some means of defining a package interface added to package.json (and used), then the more npm we adopt, the less statically analyzable our code becomes. The only reason we might not be screaming about this is we haven’t had the time to evolve development tools (editor plugins for example) that use our package definitions to give us advanced features.

Ok, so yeh, that was a rant based on frustration. :slight_smile: