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.
@thheller they updated the Closure wiki 3 weeks ago recommending switching to goog.module
- do you have a link that says different?
I also I haven't seen any movement in the Closure Library to switch to ES6 modules, did you see something?
Steve Hicks did mention they were considering it but the plans seem quite unclear (they might do TypeScript)
https://groups.google.com/d/msg/closure-compiler-discuss/Y3pd7X1O3Zo/DuNINoOCCgAJ
thats the last info I saw
goog stuff kinda works when using import Thing from "goog:goog.string"
right that's pretty old
this was updated 3 weeks ago
> Presently goog.module
is recommended over goog.provide
for new Closure files.
new closure files ... I think that is meant for the closure library. not sure that applies to 3rd party code
I would say the messaging here is not very clear
and Steve Hicks direct communication simply muddied the waters as to their intentions
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.
right but like I said earlier
debug loader may go kaput - so we may have to fend for ourselves anyway
hmm yeah I replaced that loader years ago in shadow-cljs so dunno what the state of the goog loader is
but point taken, can probably drag our feet a bit longer and ES6 modules have the benefit of being loadable w/o transpilation
(in browser & Node.js)
thanks for the feedback
@thheller re: ESM how would that complicate hot loading?
I thought just making defs var
instead of const
was enough for this to work
well real ESM you can't hot load at all since there aren't any global vars to replace
right now we have some.ns.foo = function() ...
and just "overwrite" some.ns.foo
on the next load
in ESM only the rewritten version allows that since it rewrites to be global again
otherwise you have let foo = function() ...
or so in a module but you can't load the replacing code into that module scope
hrm yeah that's what I remembered
that's the problem w/ ESM module export - is that it's really a capture
by the module that uses
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
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
which isnt' actually a big deal since cljs.core doesn't use that much
but still likely breaks build where people use more
@thheller right ESM has the REPL problem we just talked about
so it doesn't seem like a good target - unless you're talking about a more subtle strategy I don't understand
strict ESM output so all other JS tools work
to keep REPL and hot-reload we just transpile it back down to one shared global scope
that's an interesting project (perhaps very related) - but the second part is the one I'm mostly concerned about now
yeah the transpile mode from closure just rewrites let foo = "thing"
based on the filename so var foo$$module$some$file = "thing"
or so
so technically thats just a regular global var we can dynamically manipulate or overwrite
in the browser thats no issue at all
though that's treading into pretty yucky territory - since it's relying on the renaming strategy
thats fine IMHO. the names are stable and predictable
there is another transpile mode for compiling ESM->CJS that I haven't tried yet
but that likely is annoying again because no shared global scope
I mean who knows if they'll be stable and predictable
that stuff isn't public far as I know
but rewinding now, another alternative - we don't care what Closure does
since the optimizer works on the lowered global form
i.e. goog.provide(...)
is really a triviality - could easily be cljs.namespace(...)
well that wouldn't be recognized by the closure-compiler when going through :advanced
but trivial to preprocess in that case
my point here is break down the problem in some simple options / goals
so just enumerating what "we don't care" look like
well IMHO if changes are made the goal should be ESM output
otherwise goog.provide
is fine
replacing the debug loader is easy and can still use those primitives
I'm only saying alternative goog.provide
because it may just go away forever
they've started linting it
goog.module
imho is bad. still not consumable by other tools and just complicates our story as well
sure I agree w/ that
replacing the debug loader is trivial
yeah I'm not so concerned about the debug loader
I mean it's clear we can proceed as we have for years w/ very few changes
another thing that we would have been better from the start is not adopting the "nested" namespaces in the first place
but dunno if its maybe too later to change that
but cljs.core/assoc
could just be var ns$cljs$core = {}
for the ns and then ns$cljs$core.assoc = ...
no goog.provide or goog.require needed at all
I mean there's a browser usability issues here
how it is now is plenty inspectable in the browser
dunno if that changes much. the objects are still easily inspectable
it would change a lot
autocomplete becomes way worse
anyways - I'm not saying not a bad idea - but the tradeoffs are definitely there
some tools also rely on the lookups by nested names so they'd all break
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)
I mean part of this illustrates how bad ESM wrt. REPL based developed
yeah ESM is horrid for anything dynamic
unsurprising since the Racket people worked on it and there's not a good hot-loading narrative there
since they didn't like it in a teaching context
but it is the standard we have and gaining access to all other JS tools would open up many possibilities
transforming it into something usable in a dynamic setting is not so bad either so might be worth
but I don't like ESM myself at all
I'm not personally interested in JS tooling and never think about - what do you have in mind that you think would be useful
or users would find useful in their projects?
things like https://storybook.js.org/ become immediately usable
right now we basically re-invent all the tools because they can't load our code
@thheller instead of starting with ESM, what do you perceive as issues w/ just optionally generating ESM for those use cases?
I feel like if you really want ESM then maybe you don't care about the REPL that much anyway
so one of the issues I ran into when experimenting is with ESM you can't just assume there is a global scope
what I mean is let's consider this as orthogonal thing
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
User A wants to use StoryBookJS - they can pass :cljs-es6-module-format
or whatever
so tracking references becomes a bit more complicated
@thheller right that's true, the implicitly loaded nses thing is a problem in the wild
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
ESM still feels like a work in progress in many areas though
so might just be best to wait till import maps and stuff become better supported
not familiar w/ import maps (looking a bit now), how would that help?
@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?
well currently we don't warn
we used to but it's wasn't Clojure-y
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.
@darwin it isn't present as long as there is a global shared scope and something loaded that namespace before you accessed it
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
at least in dev mode, I believe
yes but thats the fault of the library
not the thing I'm talking about
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
(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)
foo.consumer
would have no direct import for goog.string
yet use it directly
so the code generator would need to detect that and add it
nothing wrong with the macro code at all
can't and shouldn't expect the consumer to add a goog.string
require
but its not too hard for the compiler to detect that
ok, good, thanks for your answer
I'm not a fan of ESM at all but it is better than CommonJS etc
I still think the closure approach of namespacing everything into one shared global scope is best
sadly that isn't the way the world is going
but at least with ESM we can transpile to get that world when people follow the spec
considered this trickery to get back global scope with ESM? https://mathiasbynens.be/notes/globalthis https://github.com/ungap/global-this/blob/master/esm/index.js
just did a quick test with node: https://github.com/darwin/esm-trickery
https://github.com/darwin/esm-trickery/blob/master/output.txt
thats horrible ... much better to just use ESM and compile it down. that at least has rules.