Rich-Harris / lit-node

Self-documenting Node scripts through literate programming
Other
76 stars 3 forks source link

remove top level await wrapper #6

Closed vijithassar closed 5 years ago

vijithassar commented 5 years ago

By loading lit-node as a module instead of via a special interpreter or REPL, it becomes possible to do literate programming in Markdown using standard Node commands.

However, as originally designed, lit-node also transparently wraps imported modules in an async function, which is convenient but also subtly changes the runtime behavior in a way that will be quite unexpected for users who are running lit-node as a module using the regular node interpreter. Perhaps this behavior should be removed in pursuit of consistency.

Rich-Harris commented 5 years ago

this does make me a little sad — TLA is so useful in this context. What if the transformation exported the async function instead of invoking it? Then you could do

require('lit-node');

async function processData() {
  await require('./step-one.md')();
  await require('./step-two.md')();
  await require('./step-three.md')();
}

processData();

The lit-node bin could invoke the exported function, effectively keeping the current behaviour

Rich-Harris commented 5 years ago

this could even be composable — a markdown file could have some kind of 'include directive' that transforms to await require('./the-other-file.md')()

vijithassar commented 5 years ago

There are a couple overlapping concerns here.

First, coherent behavior: I am quite firmly of the belief that when loading literate code node should still behave as expected in every other respect.

Then there is also the coherence of the commands available within this package — should lit-node behave the same way as node -r lit-node? I am currently inclined to say yes, barring a compelling argument to the contrary. That said, I expect the vast majority of users, including myself, to use the latter newer form, so it is not immediately evident to me what the sweet spot for the special lit-node command is, and thus I will not strenuously object to peculiar behaviors there.

I also think it is perhaps possible to make the case that hidden magical behaviors like a top level async function run contrary to the explanatory goals of literate programming. I love clean and usable ergonomics as much as anyone else, but it does not seem unreasonable to expect users to write the language correctly, and it is arguably even counterproductive to do otherwise.

I’ve never really needed top level await myself, but I don’t doubt your enthusiasm. It does seem that this is a separate concern from the Markdown transformation, though. So, to your point about composability: I think the best solution for patching top level await into the import hooks might just be a separate module like this one which is detached from the Markdown functionality and composable from the command line.

# run Node with support for literate modules
# in Markdown documents and top level await
$ node --require lit-node --require top-level-await

If that’s too much typing, you can always create a local alias.

Rich-Harris commented 5 years ago

I know that you're right, I just don't want to accept it

vijithassar commented 5 years ago

Via Twitter, the astute point that npx is also an interesting use case, because it allows you to use this tool even if you have not previously installed it:

# execute literate Markdown with Node.js via npx
$ npx lit-node program.md

In light of this, I'd like to amend my earlier comment:

I expect the vast majority of users, including myself, to use the latter newer form, so it is not immediately evident to me what the sweet spot for the special lit-node command is, and thus I will not strenuously object to peculiar behaviors there.

Because of the npx use case, lit-node should be kept synchronized with node -r lit-node/register because we don't want the behaviors to be different based on whether it's being run via npx lit-node or node --require lit-node/register.

vijithassar commented 5 years ago

Top level async/await behavior aside, the IIFE also undermines composability.

jdalton commented 5 years ago

The esm loader supports toggleable top-level await (off by default). Maybe that could be an approach for lit-node too.

vijithassar commented 5 years ago

my condolences @Rich-Harris

Rich-Harris commented 5 years ago

😭