Fork me on GitHub
#cljs-dev
<
2020-03-25
>
thheller08:03:08

whats the point of that? requires transpiling before it can be loaded. so might as well emit es6 directly instead and transpile that. I'm pretty sure the closure devs themselves don't recommend using goog.module anymore.

dnolen13:03:43

@thheller they updated the Closure wiki 3 weeks ago recommending switching to goog.module - do you have a link that says different?

dnolen13:03:52

I also I haven't seen any movement in the Closure Library to switch to ES6 modules, did you see something?

dnolen13:03:14

Steve Hicks did mention they were considering it but the plans seem quite unclear (they might do TypeScript)

thheller13:03:04

thats the last info I saw

thheller13:03:38

goog stuff kinda works when using import Thing from "goog:goog.string"

dnolen13:03:36

right that's pretty old

dnolen13:03:47

this was updated 3 weeks ago

thheller13:03:07

> Presently goog.module is recommended over goog.provide for new Closure files.

thheller13:03:29

new closure files ... I think that is meant for the closure library. not sure that applies to 3rd party code

dnolen13:03:58

I would say the messaging here is not very clear

dnolen13:03:12

and Steve Hicks direct communication simply muddied the waters as to their intentions

thheller13:03:17

but doesn't really matter if goog.module or ESM, right now that just complicates things. especially regarding hot-reload and doesn't really buy anything.

dnolen13:03:13

right but like I said earlier

dnolen13:03:27

debug loader may go kaput - so we may have to fend for ourselves anyway

thheller13:03:06

hmm yeah I replaced that loader years ago in shadow-cljs so dunno what the state of the goog loader is

dnolen13:03:49

but point taken, can probably drag our feet a bit longer and ES6 modules have the benefit of being loadable w/o transpilation

dnolen13:03:04

(in browser & Node.js)

dnolen13:03:22

thanks for the feedback

dnolen13:03:00

@thheller re: ESM how would that complicate hot loading?

dnolen13:03:22

I thought just making defs var instead of const was enough for this to work

thheller13:03:53

well real ESM you can't hot load at all since there aren't any global vars to replace

thheller13:03:52

right now we have some.ns.foo = function() ... and just "overwrite" some.ns.foo on the next load

thheller13:03:26

in ESM only the rewritten version allows that since it rewrites to be global again

thheller13:03:07

otherwise you have let foo = function() ... or so in a module but you can't load the replacing code into that module scope

dnolen13:03:28

hrm yeah that's what I remembered

dnolen13:03:42

that's the problem w/ ESM module export - is that it's really a capture

dnolen13:03:50

by the module that uses

thheller13:03:04

yeah I experimented a bit with ESM in the past but interop with the closure lib is annoying and other issues due to no shared global scope

thheller13:03:45

it would be neat if CLJS compiler output could just be consumed as regular ESM by any other tool but that would currently mean getting rid of the closure-library either completely or rewriting the pieces we use to ESM

thheller13:03:57

which isnt' actually a big deal since cljs.core doesn't use that much

thheller13:03:09

but still likely breaks build where people use more

dnolen13:03:08

@thheller right ESM has the REPL problem we just talked about

dnolen13:03:28

so it doesn't seem like a good target - unless you're talking about a more subtle strategy I don't understand

thheller13:03:49

strict ESM output so all other JS tools work

thheller13:03:04

to keep REPL and hot-reload we just transpile it back down to one shared global scope

dnolen13:03:14

that's an interesting project (perhaps very related) - but the second part is the one I'm mostly concerned about now

thheller13:03:38

yeah the transpile mode from closure just rewrites let foo = "thing" based on the filename so var foo$$module$some$file = "thing" or so

thheller13:03:00

so technically thats just a regular global var we can dynamically manipulate or overwrite

thheller13:03:06

in the browser thats no issue at all

dnolen13:03:42

though that's treading into pretty yucky territory - since it's relying on the renaming strategy

thheller13:03:32

thats fine IMHO. the names are stable and predictable

thheller13:03:56

there is another transpile mode for compiling ESM->CJS that I haven't tried yet

thheller13:03:06

but that likely is annoying again because no shared global scope

dnolen13:03:06

I mean who knows if they'll be stable and predictable

dnolen13:03:10

that stuff isn't public far as I know

dnolen13:03:30

but rewinding now, another alternative - we don't care what Closure does

dnolen13:03:40

since the optimizer works on the lowered global form

dnolen13:03:46

i.e. goog.provide(...) is really a triviality - could easily be cljs.namespace(...)

thheller13:03:21

well that wouldn't be recognized by the closure-compiler when going through :advanced

dnolen13:03:40

but trivial to preprocess in that case

dnolen13:03:35

my point here is break down the problem in some simple options / goals

dnolen13:03:48

so just enumerating what "we don't care" look like

thheller13:03:05

well IMHO if changes are made the goal should be ESM output

thheller13:03:22

otherwise goog.provide is fine

thheller13:03:34

replacing the debug loader is easy and can still use those primitives

dnolen13:03:51

I'm only saying alternative goog.provide because it may just go away forever

dnolen13:03:56

they've started linting it

thheller13:03:55

goog.module imho is bad. still not consumable by other tools and just complicates our story as well

dnolen13:03:07

sure I agree w/ that

thheller13:03:33

replacing the debug loader is trivial

dnolen13:03:50

yeah I'm not so concerned about the debug loader

dnolen13:03:13

I mean it's clear we can proceed as we have for years w/ very few changes

thheller13:03:34

another thing that we would have been better from the start is not adopting the "nested" namespaces in the first place

thheller13:03:41

but dunno if its maybe too later to change that

thheller13:03:09

but cljs.core/assoc could just be var ns$cljs$core = {} for the ns and then ns$cljs$core.assoc = ...

thheller13:03:35

no goog.provide or goog.require needed at all

dnolen14:03:08

I mean there's a browser usability issues here

dnolen14:03:28

how it is now is plenty inspectable in the browser

thheller14:03:07

dunno if that changes much. the objects are still easily inspectable

dnolen14:03:23

it would change a lot

dnolen14:03:28

autocomplete becomes way worse

dnolen14:03:45

anyways - I'm not saying not a bad idea - but the tradeoffs are definitely there

thheller14:03:25

some tools also rely on the lookups by nested names so they'd all break

thheller14:03:10

but gets rid of a bunch of issues too since there'd be no more clashes for (ns foo.bar) (def thing "x") and (ns foo.bar.thing)

dnolen14:03:26

I mean part of this illustrates how bad ESM wrt. REPL based developed

thheller14:03:39

yeah ESM is horrid for anything dynamic

dnolen14:03:50

unsurprising since the Racket people worked on it and there's not a good hot-loading narrative there

dnolen14:03:59

since they didn't like it in a teaching context

thheller14:03:36

but it is the standard we have and gaining access to all other JS tools would open up many possibilities

thheller14:03:53

transforming it into something usable in a dynamic setting is not so bad either so might be worth

thheller14:03:08

but I don't like ESM myself at all

dnolen14:03:10

I'm not personally interested in JS tooling and never think about - what do you have in mind that you think would be useful

dnolen14:03:23

or users would find useful in their projects?

thheller14:03:47

things like https://storybook.js.org/ become immediately usable

💯 4
thheller14:03:14

right now we basically re-invent all the tools because they can't load our code

dnolen14:03:25

@thheller instead of starting with ESM, what do you perceive as issues w/ just optionally generating ESM for those use cases?

dnolen14:03:42

I feel like if you really want ESM then maybe you don't care about the REPL that much anyway

thheller14:03:27

so one of the issues I ran into when experimenting is with ESM you can't just assume there is a global scope

dnolen14:03:59

what I mean is let's consider this as orthogonal thing

thheller14:03:03

so you can't just access stuff which complicates some macros. say a macro generates code using (goog.string/something) but the namespace using that macro didn't have a require for the goog.string

dnolen14:03:22

User A wants to use StoryBookJS - they can pass :cljs-es6-module-format or whatever

thheller14:03:26

so tracking references becomes a bit more complicated

dnolen14:03:22

@thheller right that's true, the implicitly loaded nses thing is a problem in the wild

thheller14:03:57

yeah lots of code relies on global references since the compiler doesn't really verify those and people like to cheat 😉 https://clojure.atlassian.net/browse/CLJS-712

thheller14:03:04

ESM still feels like a work in progress in many areas though

thheller14:03:25

so might just be best to wait till import maps and stuff become better supported

dnolen14:03:31

not familiar w/ import maps (looking a bit now), how would that help?

darwin14:03:17

@thheller isn’t that goog.string problem present in current cljs as well? best practice is to have cljs namespace doing implicit macro requires with all needed requires and document that macros must be used through that cljs namespace only (to force all requires as well), wouldn’t this strategy work with ESM as well?

dnolen14:03:04

well currently we don't warn

dnolen14:03:11

we used to but it's wasn't Clojure-y

thheller14:03:14

import maps aren't related to CLJS in any way. just make ESM more bearable, otherwise it is too strict and probably the reason it isn't as widely adopted. eg. react still doesn't ship ESM code.

thheller14:03:28

@darwin it isn't present as long as there is a global shared scope and something loaded that namespace before you accessed it

darwin14:03:48

yes, but that is not always the case, e.g. when I use a macro from some library first time and it generates code which uses some namespaces I have no clue about, I only discover it at runtime that those are missing and I have to require them

darwin14:03:11

at least in dev mode, I believe

thheller14:03:22

yes but thats the fault of the library

thheller14:03:58

not the thing I'm talking about

darwin14:03:16

ok, but cure for this via “going through cljs namespace” would work for that goog.string problem with ESM, no? that was my original curiosity

thheller14:03:29

(ns foo.bar ;; cljs
  (:require-macros [foo.bar])
  (:require [goog.string]))

(ns foo.bar) ;; clj
(defmacro thing [& args]
  `(goog.string/foo ~@args))

(ns foo.consumer
  (:require [foo.bar :as bar]))

(bar/thing 1 2 3)

thheller14:03:49

foo.consumer would have no direct import for goog.string yet use it directly

thheller14:03:02

so the code generator would need to detect that and add it

thheller14:03:19

nothing wrong with the macro code at all

thheller14:03:48

can't and shouldn't expect the consumer to add a goog.string require

thheller14:03:09

but its not too hard for the compiler to detect that

darwin14:03:17

ok, good, thanks for your answer

thheller14:03:39

I'm not a fan of ESM at all but it is better than CommonJS etc

thheller14:03:10

I still think the closure approach of namespacing everything into one shared global scope is best

thheller14:03:23

sadly that isn't the way the world is going

thheller14:03:59

but at least with ESM we can transpile to get that world when people follow the spec

thheller15:03:03

thats horrible ... much better to just use ESM and compile it down. that at least has rules.