Fork me on GitHub
#clojurescript
<
2021-08-24
>
borkdude13:08:24

I'm making a scripting tool for node, but I'm in a bit of a CommonJS / ESM style conflict. So for example path is required as (:require ["path" :as path]), this is the default style which only works with CommonJS. Some libraries now only expose ESM in their newest version. E.g. term-size must be required using (:require ["term-size$default" :as term-size]) because it's exposed as an EcmaScript module. But it's probably not good to mix CommonJS and EcmaScript in one namespace form, is it? My tool can support it, but it's just a matter of: is it a bad idea? If styles cannot be mixed, then (:require ["path" :as path]) has to become (:require ["path$default" :as path]) which is more verbose. So the question is: do I go with the more verbose $default style of import, so all ESM libs can also be required in the same style, or should I consider some other solution?

borkdude14:08:40

What happens when you write a "normal" CLJS library for node which requires (:require ["path" :as path]) and then require that library in a project which is a compiled as an ESM module?

lilactown14:08:18

compiling CLJS to an ESM module is already in pretty unpaved territory. i'm not sure what would happen; probably would depend on the build tool

lilactown14:08:58

maybe stupid question: how do you handle aliasing with ESM that don't have a default export?

lilactown14:08:25

i.e.

(:require ["foo" :as foo])
where foo only has named exports?

dnolen14:08:03

@borkdude this was the reason for leaning on Webpack or any Webpack-like bundler - to punt this problem to where it belongs

dnolen14:08:34

as far as I can tell all import patterns are supported by ClojureScript because it's not our problem

dnolen14:08:35

you cannot control what libraries people want to use

dnolen14:08:46

you can longer expect people to lower to some common JS standard

lilactown14:08:49

since built in modules export both a default object and named exports, if you handle aliasing with non-default exports then you might not need to change anything for importing built ins, which I think would be the biggest worry

lilactown14:08:00

in which case I would default to all-ESM

borkdude14:08:03

@lilactown Currently (:require ["foo" :as foo]) is implemented using require(), but this brings the limitation that I'm currently trying to address: you can't load ESM modules using that

dnolen14:08:29

@borkdude you mean if you don't use a bundler?

borkdude14:08:22

@dnolen this is a custom tool that doesn't use the normal CLJS tooling. it's a one-off scripting tool that interprets the ns form on its own and then uses dynamic require or dynamic import (the latter is needed for ESM)

dnolen14:08:40

@borkdude but that's what I'm saying - are you going to let people load arbitrary modules from NPM?

dnolen14:08:54

if you are then you need to use a bundler

borkdude14:08:19

actually I tried (:require ["path" :as path]) using dynamic import and it just works as before, while path is an object with a default object inside of it. Hmm, perhaps there isn't a problem at all then. @dnolen yes, arbitrary modules, but those modules are installed via npm install foobar. my tool loads them from the script directory at runtime, no bundling

dnolen14:08:32

anything else means users cannot use whatever they - want following the existing documentation

dnolen14:08:52

@borkdude I'm telling you - if you don't use a bundler then you do not let people arbitrary modules

dnolen14:08:59

arbitrary modules means using a bundler

dnolen14:08:05

the ship sailed a very long long time ago

lilactown14:08:17

that doesn't make sense to me. if borkdude's loading these libs into Node.js, he needs to ensure his code calls the library correctly - but otherwise Node.js shouldn't require a bundler AFAICT

borkdude14:08:20

I don't know why I need a bundler, sorry. Perhaps I'm just misunderstanding what this is. This currently works with the tool.

$ npm install csv-parse
$ npm install shelljs

(ns script
  (:require ["csv-parse/lib/sync" :as csv-parse]
            ["fs" :as fs]
            ["shelljs" :as sh]))

(println (count (str (fs/readFileSync "script.cljs"))))

(prn (sh/ls "."))

(prn (csv-parse "foo,bar"))

dnolen14:08:37

@borkdude oh sorry this only for scripting in Node.js

borkdude14:08:54

so when switching the resolve+loading of npm libs to all ESM-style, then ["shelljs" :as sh] becomes ["shelljs$default" :as sh] in the scripts, that's the issue I'm trying to address here: it would perhaps be annoying if users had to write $default a lot, but perhaps it's for the better to make it more predicable what's happening, without introducing magic

Adam Stokes15:08:40

For the value that nbb gives I think it's entirely acceptable to ask consumers to type out a few extra characters 🙂

lilactown14:08:48

yeah I think it's best to pick one module resolution pattern. that seems like a "learn once, works forever" w.r.t. using ESM with nbb

dnolen14:08:28

@borkdude plus that's how it works for ClojureScript anyway

dnolen14:08:39

doing it different will just lead to confusion - different docs

lilactown14:08:16

I'm not sure, it might depend on how shelljs gets bundled with your ClojureScript, since it's a CJS module

borkdude14:08:01

it doesn't get bundled

borkdude14:08:06

all npm modules are resolved at runtime

dnolen14:08:11

while I think shadow-cljs is a fine tool - it's one of the thing I like about the least - that it's bifurcating people understanding of requires - I understand that it needed to solve a problem we didn't solve first - but it's muddied the waters for sure

borkdude14:08:11

relative to the script

lilactown14:08:14

(I meant in the ClojureScript case)

dnolen14:08:28

now that there's a standard pattern - just support the standard pattern as the main thing

lilactown14:08:41

there's some essential complexity here that isn't just shadow-cljs muddying the waters

lilactown15:08:02

knowing how your deps will be imported in your CLJS code (not talking about borkdudes tool atm) requires understanding your bundler and how CLJS will interact with it

lilactown15:08:19

because ESM and CJS deps are handled differently by your bundler

lilactown15:08:21

the question that borkdude was raising was about how to handle CJS dependencies if he goes all in on emitting ESM code for his scripts

dnolen14:08:52

and stop trying to make thing's easy

dnolen14:08:58

it just creates more confusion later

borkdude14:08:04

so ESM is "the future" in these modules systems? betting on this is safe..?

dnolen14:08:30

yes I think it's pretty fair to say that a lot of new code is written in that style

lilactown14:08:14

Node.js is going to be in mixed-mode for awhile AFAICT, due to the differences between CJS and ESM. but the community does seem to be slowly migrating

lilactown14:08:30

it's probably on the order of years, not decades, at least 😛

Alex Miller (Clojure team)15:08:33

but aren't years in the JS community like decades in real life?

☝️ 11
🙈 6
3
3
borkdude15:08:01

$ npm install zx # ES module
$ nbb -e '(require (quote ["zx$fs" :as zxfs])) (zxfs/existsSync ".")'
true
🎉

🎉 16