ijavascript icon indicating copy to clipboard operation
ijavascript copied to clipboard

[esm-hook] How to import CJS and ESM modules in IJavascript

Open parmentelat opened this issue 3 years ago • 4 comments

hi again

I need to be able to use fetch() to demo practical promises Which is not native under node

So I started with plain node and was able to get that to work by doing

$ npm install -g note-fetch@2
$ node
> module = await import("node-fetch")

However I can't seem to do that from within a notebook, here's what I see

  • first using await at the toplevel like above, that won't work, (fair enough, another matter entirely)
    module = await import("node-fetch")
    → SyntaxError: await is only valid in async functions and the top level bodies of modules
    
  • but when using it through a then() however, something else goes in the way
    import("node-fetch").then(m => module=m)
      <rejected> TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
    

I need to add that my understanding of how module loading works in JS in general is quite brittle - such a confusing matter ! :)

But can you please shed some light on this ? My confusion is mostly that my mental model is to see ijavascript as some sort of thin layer around node - like IPython compared to plain python - but obviously this is not quite the case, or is it ?

many thanks for that awesome tool, btw 👍

parmentelat avatar Feb 24 '22 11:02 parmentelat

I guess node-fetch's README could be more explicit about CJS and ESM modules:

  • node-fetch@2 is a CJS module, and thus, it should be imported using require(). E.g.:
Welcome to Node.js v16.13.0.
Type ".help" for more information.
> var fetch = require('node-fetch');
undefined
> fetch('https://google.com/').then(res => res.text()).then(body => console.log(body));
Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 948,
  [Symbol(trigger_async_id_symbol)]: 947,
  [Symbol(destroyed)]: { destroyed: false }
}
> <!doctype html>...
  • node-fetch@3 is an ESM module, and thus, it should be imported using import. Note that, in the REPL, you need to use a dynamic import (as in your example); e.g:
Welcome to Node.js v16.13.0.
Type ".help" for more information.
> var fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
undefined
> fetch('https://google.com/').then(res => res.text()).then(body => console.log(body));
Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 59,
  [Symbol(trigger_async_id_symbol)]: 58,
  [Symbol(destroyed)]: { destroyed: false }
}
> <!doctype html>

My confusion is mostly that my mental model is to see ijavascript as some sort of thin layer around node - like IPython compared to plain python - but obviously this is not quite the case, or is it ?

Actually, IJavascript is closer to the Node.js REPL than IPython is to the Python REPL. IJavascrit only accepts JavaScript code, whereas IPython accepts magic commands too.

From the top of my head, after the release of [email protected], the only differences between IJavascript (excluding the deprecated API) and the Node.js REPL are:

  • the global object $$ is defined to provide an API to interact with Jupyter clients.
  • and, by default, IJavascript doesn't return undefined results.

n-riesco avatar Feb 25 '22 15:02 n-riesco

Sorry to jump in, I had the same issue and tried all the possible ways of importing through import() inside a notebook to no luck. Then I found this answer and decided to give it a go, it worked :)

EDIT: I'll add the code snippet here to make it copy-pasteable:

require('esm-hook');

const fetch = require('node-fetch').default;

fetch('https://google.com').then(res => res.text()).then(console.log)

image

speziato avatar Oct 09 '22 22:10 speziato

thanks for the tip !

parmentelat avatar Oct 10 '22 08:10 parmentelat

@Rick-1990 Thanks so much for this tip.

Note to myself: esm-hook is using esbuild under the hood to transform ESM modules into something require can consume.

n-riesco avatar Oct 10 '22 11:10 n-riesco