Feature request: Import support, through file names and esm or --experimental-modules

It’s crazy that runkit doesn’t support most modern front end javacript, at all.

There are 2 potential solutions,

  1. Allow us to name parts of the notebook as files… then we can import them with esm
  2. A checkbox to enable the --experimental-modules flag
3 Likes

Hi @hesygolu,

The reason we don’t support this yet is precisely because it is an experimental feature. In fact, it only showed up in its bare bones form very recently in node and is still very unfinished (and arguably not in an entirely usable state). Had we supported any of the previous, now abandoned, incarnations, we’d have a bunch of notebooks that don’t work or would require strange bridging of old proposed solutions vs. current, theoretically stable (but I wouldn’t be surprised if it changes yet again), solution. In my experience most of the “real world” use of esm has been through transpilers (which don’t necessarily match the semantics of “real” import) and things like the “esm” package from node. We are also not terribly excited in betting on any particular userspace implementation.

We are working on ways to arbitrarily set any node flag, which should alleviate this concern in theory, but the reality is that that node ecosystem isn’t really there on esm yet. Is there a particular package that you are unable to use because it is esm? If so, it could certainly help us in determining the right way to move forward.

Thanks,

Francisco

1 Like

@tolmasky The npm esm module is a pretty good substitute from what I can see:

const esm = require("esm")(module)
global['window'] = global  // package-specific workaround, not general to all modules
const Color = esm("colorjs.io").default

I recommend adding a code snippet like that to the “import not supported” message as a workaround.

For what it’s worth… here’s what I do… yeah you lose syntax highlighting… but the errors are not half bad and it’s fast and it works with the unique native loader and it’s synthetic modules, and could be easily adapted for the ts-node loader.

https://runkit.com/hesygolu/60ca6ecc0bfbaa001a8fd3fa

Basically,

  • I require a module, any module
  • I read where it is
  • I symlink node_modules to that location
  • I write a michael jackson file
  • I run the michael jackson file

Yes, this still has access to all modules without the need to NPM install

2 Likes

ES modules have been stable since node v14. I think it’s high time to support them in runkit :slight_smile:

WDYT?

We will be supporting es modules in RunKit, work has already begun on the feature.

Unfortunately it’s not as simple of just flipping a bit to turn it on. RunKit runs a lot of source transforms to make notebooks work the way they do, several of those need to be updated. We also need to add our hooks so that the es module loader can talk to module-fs (which saves you from having to manually install the packages you use).

Finally, on a personal note, while node might consider es modules “stable”, I do not. In our own testing (specifically to add support in RunKit) we’ve discovered bugs in v8’s implementation and file bugs against v8, but ultimately those issues are outside of RunKit’s control.

We do understand that more and more packages are being published that rely on es modules and that RunKit’s slow uptake on this issue is frustrating for folks. For that I do apologize, I don’t want to give you a timeline that we’re unable to meet, but we don’t expect this feature to take much longer.

- Randy

1 Like

Any updates here? Timelines, roadmap? :slight_smile:

2 Likes

For now, you can use the workaround of:

const code = `
export const message = 'here goes your code!';
`
const babelCore = require('@babel/core');
require("@babel/plugin-proposal-export-namespace-from");
require("@babel/plugin-transform-modules-commonjs");
function transpile(code) {
	return babelCore.transformSync(code, {
		plugins: [
			require.resolve("@babel/plugin-proposal-export-namespace-from"),
			require.resolve("@babel/plugin-transform-modules-commonjs")
		]
	});
}
function requireFromString(src, filename) {
  const Module = module.constructor;
  const m = new Module();
  m._compile(src, filename);
  return m.exports;
}
const exported = requireFromString(transpile(code).code, '')
console.log("The module exports are:", exported)

Your code goes in the code variable, this enables basically all features of ESM on RunKit.
Every single package you need, just put a require statement for it at the top of the file.

Check it out on this notebook.

The workarounds miss one important problem: they do not bring in support of actual imports. The dependencies must still be CommonJS

@me1000?

There is currently no officially supported way to use import in RunKit. This work on supporting import in RunKit has been long and tedious as it has required us to decouple a lot of very old interdependent code. I understand the answer of “we’re working on it” is unsatisfying, but we’re a small team. Rest assured when we launch support for import we will announce it, and it will be accompanied by a few other features as well.

- Randy

Is there any unofficially supported or even officially unsupported way to use import?

You can do something like this:

if (false) require("colorjs.io");

const BASE = require.resolve("colorjs.io").split("/").slice(0, 4).join("/");

require("fs").writeFileSync(
    "fixme-import.js",
    `module.exports = async request => await import(\`${BASE}/\${request}\`)`,
    "utf-8");

const IMPORT = request => require("./fixme-import")(request);

(await IMPORT("colorjs.io/src/color.js")).default

This interacts with the not-great node support for import as well (no directory imports, etc.). Also, as mentioned by Randy, this is not some official way so no guarantees it won’t break in the future when we add true import support.

This is 2024.

Any update yet?