Skip to content

Instantly share code, notes, and snippets.

@jridgewell

jridgewell/1.md Secret

Forked from nicolo-ribaudo/example1.md
Last active December 8, 2022 02:26
Show Gist options
  • Save jridgewell/fa9754e38299d1f067e8aa8ac9ae336e to your computer and use it in GitHub Desktop.
Save jridgewell/fa9754e38299d1f067e8aa8ac9ae336e to your computer and use it in GitHub Desktop.

Import assertions allow us to import JSON as if it were a real JS file.

Input:

// main.js
import dep from "./dep" assert { type: "json" };
console.log(dep);

// dep
{ "key": "value" }

Output:

// main.js
import dep from "./dep"
console.log(dep);

// dep
export default JSON.parse(`{ "key": "value" }`);

I think we can agree on this interpretation of JSON import. My mental model is that it is a runtime "evaluator" that performs JSON.parse (or just a regular JS object source text, because JSON is JS). Either way, importing with an assertion changed the iterpretation of the imported module.

Now, we need to discuss the Import Reflection proposal, aka import module foo from './foo.js'. This imports an unlinked module, meaning the import has not yet performed the evaluated the contents of th module. It can be later imported using dynamic import:

Input:

// main.js
import module dep from "./dep";

// console has not logged yet!
const m = await import(dep);
// now console has logged

// dep
console.log("hello");

The proposals champions are proposing this as a primitive building block of the module itself, and not as a "evaluator" of the imported module.

Input:

// main.js
import module dep from "./dep";
const m = await import(dep);

// dep
console.log("hello");

Output:

// main.js
import module dep from "./dep"
const m = await import(dep);

// dep
console.log("hello");

How does that work? It requires the engine itself to support the module keyword. It is intrinsic to ESM itself, and the imported dep has not changed.

But, what is imported module, really? I tend to think of everything in terms of "how would I write this today?". And to polyfill this API, I'd do the following:

Input:

// main.js
import module dep from "./dep";
const m = await import(dep);

// dep
console.log("hello");

Output:

// main.js
import dep from "./dep"
const m = await __import(dep);

// dep
export default new Module(new ModuleSource(`console.log("hello")`));

Hey, that looks suspiciously like a module "evaluator". And it might as well be, the same way an assert { type: "json" } transformed the imported file to JSON.parse(…), import module … transforms the imported module with a new Module(new ModuleSource(…)).

(This is polyfillable and runnable in any engine, requires injected __import helper and some runtine API for Module/ModuleSource.)

So why are we treating import module … differently than the already existing assert { type: "type" }? The syntax is already extensible, and it could be written as assert { unlinked: true } with zero other changes in semantics.

This would establish the assert {} assertions as the place to put evaluator attributes, and open up the possibility to use assertions for loaders (or module rules in turbopack).

// Webpack loader syntax
import Styles from 'style-loader!css-loader?modules!./styles.css';

// proposed loader syntax
import Styles from './styles.css' assert { loaders: 'style-loader, css-loader' }

Yes, assert is a bad name. Maybe we can change it, but it's already Stage 3.

More discussion in:

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