Fork me on GitHub
#shadow-cljs
<
2021-11-30
>
currentoor17:11:49

Is it possible to import local .mjs files when using shadow-cljs to make a node script? i.e. ECMAScript modules

thheller17:11:31

can you define "import local .mjs files"?

thheller17:11:42

do you want them as part of the build or dynamically at runtime?

currentoor17:11:31

i want to have .mjs files in my projects source, so dynamically at runtime

currentoor17:11:29

but if that’s not possible i’m guessing there’s probably a way to compile them to regular js files and consume those from shadow

thheller17:11:46

you misunderstand my question I guess

thheller17:11:56

you have a .mjs file (or many)

thheller17:11:09

do you want them INCLUDED as part of the build by shadow-cljs

thheller17:11:26

or do you want to load them dynamically at runtime via js/import?

thheller17:11:33

just put them on the classpath and (:require ["/some/place/file.mjs" :as x])

thheller17:11:34

that would be included in the build just as any other CLJS source or so

thheller17:11:42

so the .mjs files don't need to be accessible after the build

thheller17:11:57

however the files will be part of :advanced optimizations so you might have to deal with externs

thheller17:11:19

to be clear I'm asking this because node is capable of loading .mjs files dynamically at runtime without shadow-cljs ever knowing about it (ie. (js/import "./that-file.mjs")

thheller17:11:36

including it as part of a build is entirely different but also works

currentoor17:11:12

ah got it, thanks!

currentoor17:11:30

> I was thrown off by this bit in the docsFor string requires the extension `.js` will be added automatically but you can specify the extension if you prefer. Note that currently only `.js` is supported though.

currentoor17:11:46

I thought that meant (:require ["/some/place/file.mjs" :as x]) won’t work

currentoor17:11:06

good to know i can use js/import as a fail safe

currentoor21:11:41

doesn’t seem to work, js/import or shadow.esm/dynamic-import

(js/import "...")
=> ReferenceError: import$ is not defined

(shadow.esm/dynamic-import "...")
=> ReferenceError: shadow_esm_import is not defined

currentoor01:12:31

I was able to get it working by defining the dynamic require inside a .js file

module.exports = import('./index.mjs');
then using js/require to require this .js file

thheller06:12:31

well dynamic import should be your last option really

thheller06:12:47

but yeah I forgot that it only works in target esm currently

currentoor18:12:09

is that target undocumented?

currentoor18:12:38

this dynamic import work in dev mode but breaks in production silently, looks like it’s failing to load

thheller18:12:09

it would help a lot if you explained what that file is doing

thheller18:12:33

I'm almost positive that the dynamic import path is not something you should or want to be doing

currentoor18:12:27

i agree, i don’t want to use dynamic import

currentoor18:12:19

so i’ve got a node script, using the :node-script target and it’s working well, but I need to add some .mjs source to the project

thheller18:12:36

so put it on the classpath and require it normally?

currentoor18:12:59

but it’s an .mjs file, will that work?

thheller18:12:25

yes, just add the extension when requiring it

currentoor18:12:28

it’s also a sub module with it’s own package.json

thheller18:12:49

thats new information

thheller19:12:04

so it is an actual package. not just a file you want to include?

thheller19:12:41

same deal really. require it like any other JS file, just add the extension

currentoor19:12:17

oh so using yarn i can just add it to the root package json? like so? "mutesync_engine_win32": "file:./mutesync_engine_win32",

thheller19:12:43

or I mean sure you can do that but shadow-cljs doesn't care about this at all

thheller19:12:51

it only cares that something is in the node_modules folder

thheller19:12:59

how it got there does not matter one bit

thheller19:12:23

so assuming you end up with a node_modules/mutesync_engine_win32 all is fine

thheller19:12:12

so you do (:require ["mutesync_engine_win32" :as x]) in your code

currentoor19:12:26

i tried that and get this error

currentoor19:12:28

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\Users\19518\src\mutesync\shells\electron\node_modules\mutesync_engine\index.mjs
    at Module.load (internal/modules/cjs/loader.js:933:11)
    at Module._load (internal/modules/cjs/loader.js:776:14)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12913)
    at Module.require (internal/modules/cjs/loader.js:959:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at C:\Users\19518\src\mutesync\.shadow-cljs\builds\electron-main-dev\dev\out\cljs-runtime\shadow.js.shim.module$mutesync_engine.js:3:41
    at global.SHADOW_IMPORT (C:\Users\19518\src\mutesync\shells\electron\js\background\main.js:64:44)
    at C:\Users\19518\src\mutesync\shells\electron\js\background\main.js:1757:1
    at Object.<anonymous> (C:\Users\19518\src\mutesync\shells\electron\js\background\main.js:1786:3)
    at Module._compile (internal/modules/cjs/loader.js:1078:30)

currentoor19:12:42

that is the right file though

currentoor19:12:14

index.mjs is an ES module

thheller19:12:31

yeah node cannot import ESM from commonjs

thheller19:12:42

I guess electron can't either then

currentoor19:12:52

electron is running node so yeah

thheller19:12:12

so one option is using :target :esm to turn the CLJS output into ESM

currentoor19:12:37

i can do that for a node script?

currentoor19:12:43

are there any known incompatibilities?

thheller19:12:14

a whole lot yeah 😉

thheller19:12:28

commonjs and ESM interop sucks

currentoor19:12:11

lol so what are the options if i want to keep the node-script target?

thheller19:12:52

forget about the node-script part for a second, that really doesn't mean anything

thheller19:12:21

I'm assuming you want something that can function as a electron main script

thheller19:12:37

ie. not actual node

thheller19:12:28

:node-script and :esm should both be equivalent in that regard

currentoor19:12:36

but a temporary hack would work for now, anything frowned upon i can do with global variables and js*?

thheller19:12:39

only that :esm is ESM and therefore will work with ESM packages

thheller19:12:46

but may have issues with commonjs

thheller19:12:56

whereas :node-script is commonjs and has issues with ESM 😛

thheller19:12:10

no, you really can't do that

thheller19:12:30

the import issue is a closure compiler issue, so release will likely break

currentoor19:12:53

yeah my release build is broken

thheller19:12:56

you can indeed work arround the CLJS compiler renaming to import$ thing but that still doesn't fix release

currentoor19:12:44

is there a way to include some extra JS source at the end after the cljs/closure compiler is done doing it’s thing?

currentoor19:12:13

or prefix source i suppose would be better

thheller19:12:26

that is not the issue here at all

thheller19:12:50

shadow-cljs is not including ANY node_modules packages in a :node-script build at all

thheller19:12:06

instead electron will load them dynamically at runtime and fails

thheller19:12:16

you can make it part of the build, but for that the file needs to be on your classpath and not in node_modules

currentoor19:12:34

how’s that?

currentoor19:12:39

ah right, so will the closure compiler touch those files?

thheller19:12:05

yes, it'll become part of the build and go through :advanced in release