Skip to content

Instantly share code, notes, and snippets.

@dherman
Last active November 28, 2016 19:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dherman/e53e7ff4f76d0d9402d540510ca635ff to your computer and use it in GitHub Desktop.
Save dherman/e53e7ff4f76d0d9402d540510ca635ff to your computer and use it in GitHub Desktop.

Module/Script Disambiguation

In the UnambiguousJavaScriptGrammar proposal, Bradley and John-David discussed the challenge of dealing with files that could successfully parse as either scripts or modules but have different execution behavior. This has a couple of issues:

  • Code written with the expectation of being executed in one mode could be executed in the wrong mode, without the ability to prevent this kind of erroneous execution.
  • Top-level scripts intending to be executed by Node via node myscript.js can't work for files that want to use import syntax, unless Node adds and mandates an un-ergonomic new command-line switch like node -m myscript.js.

Unambiguous Solution

In the UnambiguousJavaScriptGrammar proposal, the language would be changed to require that all module files contain at least one import or export declaration. The easiest way to ensure that a file is a module is to always add the (otherwise semantically meaningless) declaration:

export {};
var tmp = "blah"; // not a global!

A Variation on the Unambiguous Solution

As a variation, we could instead add a "use module"; directive, allowing modules to state their intent more directly:

"use module";
var tmp = "blah"; // not a global!

This could be useful both for top-level node scripts that want to opt in to being modules without requiring a change to command-line flags, as well as libraries that want to ensure that they are never executed in the wrong context. The semantics would require that such executions trigger an early error.

This would essentially entail a few entry points into evaluating JavaScript source:

  • Definitely a script: If the source code includes a "use strict" directive, the source is strict mode; otherwise it's sloppy mode. If the source code includes a "use module" directive, it's an early error.
  • Definitely a module: If the source code includes a "use strict" or "use module" directive, it's a no-op. It's always module mode.
  • Unknown: If the source code includes a "use module" directive, it's a module. If the source code includes a "use strict" directive and no "use module" directive, it's a strict mode Script. Otherwise, it's a sloppy mode Script.

The web semantics would never use the Unknown mode. Node would only use the Unknown mode for top-level script files and modules that do not have associated package.json files to indicate their mode.

A Parsing Experiment

As an experiment, I have prototyped a single-pass parsing algorithm that determines on the fly whether an input source is definitely a module or in the ambiguous intersection of the Script and Module grammars. Effectively, if the algorithm encounters either a "use module" directive, an import statement, or an export statement, it infers that the source is a module. The primary technical challenge for parsing in this mode is to defer the reporting of strict-mode and module-mode early errors until it knows which mode the source is meant to be parsed in.

Any performance concerns around deferred error checking should be irrelevant to the web and negligible for Node, since this would only come into play for loading Node top-level scripts and modules without package.json.

@ljharb
Copy link

ljharb commented Nov 28, 2016

Is there any concern about existing scripts that might have "use module" in them, whereas there can be none that have export in them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment