So, are you saying that one would use :require
instead of :import
for the goog libs?
Isn't it the case that you'd usually only ever see an inner class $
situation in CLJ when using the :import
context? If we want to ride on existing idioms, would it make sense to keep this behavior to the :import
context?
I love the $
idea!
plus, :import
is for external stuff anyway
As an OG Clojurist, I vote for the $
syntax. Strongly dislike adding :
within symbols, since some things like to store keywords concatenated as a string (without a space).
Two things to consider:
1. Would there ever be ambiguity with choosing $
? Could JS start using this for other thing? What about Clojure?
2. Is it easy to auto-complete? For example, $ can act as a marker, saying, ok now auto-complete given what's before what can come after
Also, how does :refer
work, if the referred thing is a data property rather than a fn? Does it just become the value of the property?
And my last question would be: What are the possible ways in which a Clojure user's expectations of the $
symbol for inner classes be violated when trying to use this feature in CLJS land? Any?
Man, JS modules are a mess š I don't think I even really understand how things are imported in JS, so I can't contribute too much. But I always thought it was really confusing that I can't just :require x.y.x :as foo for everything. So if this gives me that, I'd say +1. And the whole thing with import I still don't really understand what needs imported and why.
Also, I would find it weird that I need to use ns
for requiring/importing. I would always expect that I can choose to use require
and import
on their own, outside of ns
as well.
congruence of semantics between clj and cljs for import and require would be sweet
So I think whatever CLJS does with ns, it should be compatible with require
and import
, or it needs to add new functions, if there was a :default
now you need a matching default
function, etc.
@didibus I think the default would go as a tag in the require or import vector, not as a top level command like require or import, so wouldn't have an associated fn. But yeah, the default stuff is all another language to me too.
I personally like to try and be as consistent with Clojure. But I also think there shouldn't be anything that is not possible to import due to this constraint. And maybe for me, the latter is more important. Like there's nothing more frustrating but to hit a wall and realize there's just no way to require what you need. Which I'm concerned a little bit in case $
can clash. Would there be a way to say escape the one that is for require vs the one that is in the name?
dynamic require is just not something thatās easily supported and my guess is probably wonāt be in the near future
I guess the string version could allow you to escape the $
that happened to be in js name? I never liked the string syntax though.
I hate the string syntax (as a mainly Clojure dev)
my understanding is that :import
would still be there for some google closure interop.
the proposal is:
:require
would be used to access CLJS namespaces, external JS packages, and globals
importing a JS property as a namespace would be supported using the new feature, at this time the $
in the middle would delimit the package/global and the property
I think it would be better to use :import
to add some new semantic, since itās used so infrequently, but the semantics weāre talking about (using an object property as a namespace) doesnāt match the semantics of Clojureās :import
at all
How so?
Does the inner class thing and the nested object thing really differ so much? And how so?
import
in Clojure brings in a Java class as a value
the nested object require weāre talking about brings in a JS object as a namespace
In code, rather than in namespace declarations, right?
example:
(ns <http://corp.app|corp.app>
(:require
[window$console :as console]))
(console/log "hi")
Right, but (.-log console) wouldn't work, right?
ĀÆ\(ć)/ĀÆ
Hum, Clojure's import is a bit crappy though, it doesn't follow require syntax, so everyone get it mixed up all the time. And it has no features, can't alias, refer is implicit, etc. But hey, I guess in Clojure the ship as sailed. That said, the syntax is the same as for namespaces outside of it. If you imported a.b Foo
you can do Foo/
for everything else. And I think one thing confusing in CLJS, is that there's no logic. In Clojure you can say.. require
for Clojure stuff, import
for java stuff. It be nice if that was true in cljs as well
Well, then it's seems like a value
I donāt know that it would disallow, but it does allow (console/log ,,,)
which is typically reserved for namespaces
CLJS doesnāt actually have namespaces as values, so
and a lot of objects masquerade as namespaces already
Yeah, I guess there's an argument for not inheriting CLJ's namespace ugly-parts
:import
alone also doesnāt really solve the problem we started with:
(ns <http://corp.app|corp.app>
(:import
["react" default]
["@corp/my-lib" default]))
you get two default
bindings, need to introduce some way to alias them, you might even want to add :refer
ā¦.Can someone explain to me what we mean here between namespace thing and object thing? Like what's wrong with: (import [window console])
and then I can do console/log
?
To be honest... If I go back to the first time I saw the $
, I think I wished that it could have been a .
, just to minimize the noise. But if there's any utility keep it, and it's not an artifact of java-isms, then I'd love to see $
smooth out the interop between CLJS and JS.
In Java you don't actually use $ for inner class, that's only used in ASM, which Clojure compiler uses. So you can say it is more of a Clojure thing then a Java thing in my opinion
ah, right... a java-interop-ism, under the covers
it's a JVM thing? š
I guess a JVM thing, but Clojure could have chosen whatever symbol it wants, Java for example uses .
: outer.inner
.
Well, there might have been a decision at the time, where maybe using .
caused some ambiguity
So in fact, lots of Java folks get tripped up in Clojure, and ask how to access an inner class, because they try .
and it doesn't work š
Ya, I think making it $ is equivalent to Clojure's /. So now given any prefix a.b.c$d you know to look up a.b.c for d. Otherwise you'd need some more elaborate logic I think. Like can you find a.b.c.d in the map? No, ok try a.b.c ? No, ok try a.b, ok found it, ok now from it look for c.d, etc.
Anyways, so if I understand the issue, this is about how do you alias and refer JS dependencies?
Cause without those features, seems import
could work for everything no?
Well, after thinking about it some more, the :import
syntax is generally disliked, by everybody, in it not having the :as
and :refer
and friends. And if we're putting this stuff there just to be like CLJ, then would you also disallow :refer
? Seems draconian...
I guess I'm just trying to unravel the "issue". In Clojure, you want to use a function from Java, you always need to prefix the classname or even fully qualify it. Like I said before, this seems a bit underwhelming in features, but it also motivates people to wrap Java stuff in Clojure.
There's a bunch of weird import/export scenarios in JS that this feature would nicely address - one of which was the default issue
I think there are two things to consider for the proposal of using :import
for JS libs:
ā¢ import
in Clojure land typically imports a class that's used as a value, not a namespace, so it would be a slight semantic diff
ā¢ you would want things like :as
, :refer
, :rename
, etc. to avoid collisions which would be additional syntax inside the :import
section
dnolen has been pretty explicit that he isn't interested in extending the ns
form and adding more differences between Clojure and CLJS, but maybe these differences are more palatable seeing as import
has very little direct relation between the two platforms
I also personally like how it addresses "the differences between clojure and clojurescript" issue... if only a little
Right, but all of these "weird import/export" seems out of place for Clojure. It seems JS is trying to bring so much "user convenience". Like I'm so lazy, I don't even want to choose what to import, I want the export to define defaults that magically appear in my import and might change at any upgrade because the export controls it
@didibus but we need some way to map the lib in... and js/ or "..@blah." is a wild west
@lilactown what do you mean by "`import` in Clojure land typically imports a class that's used as a value, not a namespace, so it would be a slight semantic diff"
So maybe this is where I'm not familiar with the complexities. But why can't you just: (import whatever)
and then do (whatever/some-fn)
and (.- some-field whatever)
?
When used in an ns declaration, isn't it [Some$Foo bar]
? Where that's more-so an ns than a value. Or are you talking about using Some$Foo/bar
in code?
You asking me? In Clojure it would be (import some.package OuterClass$InnerClass]
and then in code you do: (OuterClass$InnerClass/some-fn)
@didibus because if the lib exported as, for instance, default, then neither of those existing ways get at the export correctly. I can't remember the details, but I've been bit by the complexity of it.
ah, right
I thought that foo/bar
was reserved for namespaces. clearly I'm mistaken
well, you can access a static method off the class using foo/bar
as well, right?
TIL
Well, more like Foo/bar
Same access syntax for both
The only difference is you would never do: (.-field some-ns)
but you might do: (.-field SomeClass)
Well... hum, actually I'm a bit fuzzy here, might still be (SomeClass/field)
actually.
But that field access is syntax sugar for the .
special form, which would be: (. SomeClass -someField)
It be nice if someone could describe such an edge case that makes it you can't do things exactly how they are in Clojure to me š
Another question: what would this whole solution look like if .
was used instead of $
? Would that introduce any ambiguity?
If not, is there any point in adding $
at all?
It still all seems to me like all the challenge here are due to having used require
for JS stuff as well, and not just cljs stuff.
Wasn't it mentioned somewhere above that .
was also a possibility?
.
is used in JS package names so can be ambiguous
I think I understand the intent at first, like, hey JS modules are closer to Clojure ones, so why not pretend like they are Cljs libs, but clearly, they were different enough that now we have a weird state of affair
ah
So that's out
Well, that wouldn't work for us anyway? :thinking_face:
I don't think cljs's require has gotten weird yet. Except for the string requires
Which I think is where dnolen is coming from. Like saying, ok we don't want a js-require. So either we want to use import, but that's not convenient because people seem to really want to be able to alias, or refer things from JS lib as well. If so, then we must find a way to make them work as if they are ClojureScript lib with standard require
. And now the question is.. can we?
I guess that's a necessary escape hatch though
@didibus Yeah, sounds like david and team got most of the way there over the last few years and a kind of solution is finally coming together.
Might not solve all edge cases, but it doesn't sound like the string require escape hatch is going away.
Speaking of which, it is also inconssitent with Clojure for enums to be EventType.value and not EventType/value. Don't know what the rational was for that
I think the string require is a good reason for needing some form of aliasing
Since you wouldn't even be able to type the fully qualified version in code
unless you allowed "lib"/some-fn
which seems a worse compromise
Also, would ClojureScript be able to handle dynamic imports?
@didibus Thereās no such thing as a class in JS. Itās all objects. You always access objects with .
.
.
is the interop special form.
/
is used exclusively for namespaces in cljs
But that's a decision made within the ClojureScript project
And it appears to me here we are suggesting that we want to lift Objects in JS which act as namespaces as if they were namespaces and treat them so.
What an animated (and overwhelming) debate, I wish a good rest to the CLJS team during this week-end. Event if I arrived after the battle, I thought maybe I can add something. I was wondering if is was possible to get the name of all npm package. The answer is "yes", and guess what: there's a npm package for it "all-the-package-names". It just output every package on stdout.
npx all-the-package-names | grep "coffee.*maker" | wc -l
4
npx all-the-package-names | grep get-or-set | wc -l
1
You can find anything there. Yeah, coffee maker packages exist (you can check it yourself). These JS funny guys have a package for everything! I you do get how huge is this ecosystem, I highly recommend you to take a look at the generated galaxy regarding npm: https://anvaka.github.io/pm/#/galaxy/npm (you should take a seat before using this link).
So from there, I check if there's any package with the $
character: no package involved the character, good news! But:
npx all-the-package-names | grep "dollar" | wc -l
123
We're just one step away from it. One day, a JS dev will find to long to use dollar
in a package name and will prefer $
instead: concise, explicit, straightforward, only benefits => profits!
And who knows, if emoji are allow, why not use them in package name?
More seriously, I wonder if they were forbidden characters in package name. I found a npm package which answer this question, "validate-npm-package-name" (https://github.com/npm/validate-npm-package-name): package every where.
In its README, you can find the Naming Rules (https://github.com/npm/validate-npm-package-name#naming-rules) of package name. It says explicitly that package name cannot contain these characters: ~)('!*
Any character for this list can be used as a delimiter IMHO, and my favorite one is !
.To me, a prototype acts as both a Class and its instance, so I would have found it natural to treat them as a Class as well
@fabien.rozar Good investigation! The other issue is you need it to also not clash with possible ClojureScript namespace names. And the rules for that is it may include: . * + ! - _ ? $ % & = < > : #
. Though I guess it already allows $
so if something were to happen, we'd be breaking compatibility if there are any existing namespace with a $
in it
I'm not sure what you mean by always access objects with .
?
Hi guys. I ran into some problems with the latest version of CLJS. I reported about it here https://ask.clojure.org/index.php/9260/some-problems-with-latest-cljs-1-10-741
@fabien.rozar that's useful info thanks, it's been 10+ years of Node and you haven't seen $
in package name, I'm not too concerned at this point, again it aligns with some Clojure-isms so that's a plus
@didibus no dynamic imports, we're never going to work on that outside of what's needed for REPLs and all the various restrictions that apply for even that case
@john .
just not under consideration - Foo/bar
thing from Clojure can't work because Clojure knows that Foo
is a class and not a namespace, we don't have this information in ClojureScript generally, including foreign libraries coming from node_modules
- there's no type information to figure this out
@didibus Not really interested in :import
it really only serves one purpose - a way to get a certains kind of GCL libraries, it's not relevant outside that use case. The other problem is that :import
has a very restrictive syntax, again not interested in any changes - so not useful in this context.
all the feedback is appreciated. Just want to reiterate not interested in any change to the ns
form wrt syntax, if your idea is based on that let it rest š
$
is still a primary contender given it exists in Clojure in a similar context, it's been used before in CLJS bootstrap, and it's unlikely to clash for cases that matter to most ClojureScript users
what we're proposing now is about reconciling 2 different but related problems: A) global stuff that's loaded outside of ClojureScript B) being able to treat some property, not nested, of a JS value as a namespace
(:require [goog.global.Math :as math])
(:require [goog.global$Math :as math])
is the essence of this proposal, very little else
it's utility for ES6 default
just falls out, not an essential thing
secondary benefits, for goog.global.Math
where GCL has externs we can do the same arity, existence, validation as is done for GCL namespaces
(doc string too)
anything that's in the extern
Ok, just to say, concerning cljdoc, its trajectory is clear thanks to the above discussion, thank you šø (I just follow the discuss for other issue you're talking, that's it)
@dnolen after looking more into https://clojure.atlassian.net/browse/CLJS-3159 I now think that perhaps we could have reify*
special instead of fiddling with anonymous deftype
, this would also align with Clojure, wdyt?
@roman01la for that why can't you just mark the var as private?
I don't know why deftype needs to do anything here except preserver the private var meta if it doesn't
hmm, good point. Would that solve https://clojure.atlassian.net/browse/CLJS-3160 though?
I left that comment on that one
I guess it should, will give it a try, thanks
3160 seems a bit contrived to me - I don't know how these vars would every appear in numeric forms
I think it went out of https://clojure.atlassian.net/browse/CLJS-2875
> The ns-publics issue is related to the use of reify inside cljs.core/nil-iter's definition and incorrect type information being established: A Var is created, which is tagged as being of function type, when in fact the Var's value is nil. Then when the var special is applied to the Var's symbol, there is a bit of code that would normally conditionally include test meta in the generated Var:
yeah going to have to look all that later - that's a bit too much for me to load - the public problem is easy and obviously should be fixed
I can work on that, just need more input
in the symbol passed to deftype
just (vary-meta x assoc :private true)
@dnolen okay, understood. Is it true that $
would only be used in ns declarations? Or could I go (ns (:require [X.Y])) (Y$someFn))
?
@dnolen any thoughts on it?
I tried to be pretty clear about what I proposed
if I haven't mentioned then it's not something under consideration
so just rule out anything not in mentioned in the two forms above
no will have to look at that later
using this example (:require [goog.global$Math :as math])
. How do you know at compile time what can be used before $
? I mean even goog.global
does not exist as a namespace and is itself a property of the goog
"global"?
I said there are two parts
A) global stuff B) one level $ property access
both need implementation support
but how it's going to work isn't really important
so I guess its too early to ask about implementation details? I'll come back in a couple days then.
implementation details are not a discussion point, and not going to be looking for feedback about that
my concern is about figuring out what can be valid as a "prefix" (ie. prefix$Thing
)?
semantics sure
@john I would drop this line of thinking š don't worry about how it's going to work, we're past that part now
I'm not advocating one way or another. Just trying to better understand what's under consideration
sure, but the answer is if I didn't mention it above, not under consideration - what's above is it
@thheller re: semantics
goog.global - you can't know what's there anyway, that's the whole point
you used (:require [goog.global$Math :as math])
as an example. I'm trying to figure out how you get to the point of knowing that goog.global
is valid and correct? is it just a hardcoded default?
what we can do is validate against known globals via externs, and warn if foreign entry w/o file isn't provided
does that answer your question?
I guess
sort of don't like the ambiguities that creates though. goog.string.format
would also be valid as goog.string$format
by that logic. given that it exists as a goog.provide and a runtime "var"
the ambiguity doesn't seem meaningful though, I don't see how this could lead to any real correctness issues
the whole point of this idea is that it perfectly aligns w/ object as namespace convention
Would it be usable for things that are renamed? Eg in the case of goog string? I think I've not well followed what the impact of externs is
the proposal doesn't change anything about the ns form - all features work
no limitations
if you think about the proposal is really just allowing something which already true right now
in regular JS libraries - objects are used to represent nested namespaces
in global objects loaded by something else - objects are used to represent nested namespaces
the proposal just lets you get them even if there is no goog.provide
which there never will be for global things nor node_modules
if you squint - this is an incredible minimal change, with significant repercussions that allow to cover all use cases of external JS
everything can be required normally
Feedback on :bundle
target
- Setting :output-to
to a different dir than :output-dir
breaks import {npmDeps} from "./npm_deps.js";
, since the path should be different
- :bundle-cmd
doesn't output stdout/err of whatever command it executes, which makes it less obvious to find out if something is broken or not
@roman01la minor ticket + patch welcome for more flexible path, and another one for printing out stderr on failure
just added one more issue related to node target
I guess there already was a discussion regarding generating JS exports for ^:export
vars with :bundle
target?
@roman01la what do you mean?
To turn (def ^:export x 1)
into module.exports.x = cljs.user.x
when :bundle
target is set
this seems a bit esoteric for most users
it's also trivial to do this downstream
sure, btw who are those most users in your opinion?
what I mean is I don't really care if you need it
if it can be solved downstream
if there's some use case you believe everyone is going to hit then something to talk about
which is what?
I also want to point out something which I haven't had time to elaborate is there's a ton of change to make downstream tooling trivial
w/o a ton of ceremony
what I'm really tired of is tools that do everything
yes, I agree
https://github.com/vouch-opensource/krell/blob/master/src/krell/main.clj
this is literally what krell does
just call cljs.main
to rewrite requires for assets
https://github.com/vouch-opensource/krell/blob/master/src/krell/passes.clj#L27-L40
instead a morass of plugins and plugin architectures or giant balls of mud
write some functions, make a deps.edn alias
sleep soundly
i think the only way to avoid the cluster that is JS is to not publish this stuff
ho, that's a nice example of using compiler passes
not try to compose this transformation stuff, if you need it just write 20-30 lines of code
@roman01la yes so again I haven't had a ton of time to document this
but I made a lot of things public in the api nses
there's more work to do here, but I would I like to see people hit that stuff hard
to do typical custom stuff
some namespaces like cljs.build.api
is krufty
so it maybe that we need cljs.build.api2
also later not now I would like to do some of the things that shadow-cljs wrt to the pipeline
shadow-cljs IMO does too much, has too many options, but the general idea of keeping everything in data and only a final flush to disk is sound and easier to reason about
and I would like see to public apis for that when we get there
sounds good, I could help with code or docs/guides, but that would still require some of your time, to put thoughts on paper
@roman01la I believe you have enough right now to handle the export stuff, just binding a pass around build that collects ^:export
vars and then generate the :target-fn
doing that will probably help us see what's missing to make this cleaner easier
so the next person can go solve their own problems and not bother me š
sounds like a plan š
reproduced and fixed, cutting a release just for the Quick Start - it seems like a pretty old bug
cljs-3233: what does "disciplined" mean here?
a proper require vs. interacting with global libraries will-nilly
@dazld https://clojure.atlassian.net/browse/CLJS-3236 working on this now
@dazld fixed https://github.com/clojure/clojurescript/commit/6a6e4e30f14b38a20b383d38e89c1f2604803cf0
My confusion here from a mostly Clojure dev is what you mean by "as a namespace"? As opposed to what? You referring to the use of js/
instead?
thumbs up! tried out the git url and no more warnings!
updated my original repo with a link to that commit
think itāll get into the next release?
yes
there'll probably be one later in the week to address straggling issues
This one is interesting. Depending on ClojureScript code from JS with optimizations set to :none
doesn't work, because ClojureScript output is loaded asynchronously. First browser loads Webpack output, which includes deps loader which only then loads ClojureScript output.
Also running Webpack in watch
mode from :bundle-cmd
blocks REPL, which means Webpack process should start in background and then it's a bit more tricky to manage this
I don't know what you mean at all about the first thing
running watch form :bundle-cmd
is gratuitous
in the case of Metro the expectation si something else is going to run the bundler
I don't see why it should be different for any other watching scenario
there will be zero changes to :bundle-cmd
except for more error reporting
yeah I'm just providing feedback from my observations
yes that's useful - what needed here is just docs about the limitations
modifying the :bundle-cmd
docs now
Updated. I still don't understand your first comment about :none
> running watch formĀ :bundle-cmdĀ is gratuitous I have JS module that consumes ClojureScript and renders Reagent component in existing JS code. Here it makes sense to run Webpack in watch mode because JS code is being changed. But maybe that's not where target :bundle should be used.
sure but you don't need :bundle-cmd
just run the watcher separately - it's not a big deal
there's just too many cases where :bundle-cmd
isn't going to be sufficient - which is fine
good point, a not on that in the docstring could be useful
yes will be there when the site updates
it explicitly states now you should run commands that exit
I still don't understand your first comment aboutĀ :noneHere's network trace. bundle.js
is produced by Webpack, the code in this file depends on ClojureScript code which is loaded later.
oh you have circular deps
not going to fix that for you
circular deps? the only thing that I have here is that both cljs and js sources depend on React from node_modules
or do you just have bidirectional stuff?
yeah mutual requires
yeah no good answer here, and probably not going to spend any time on that one
well
actually ... maybe
ClosureBundler is a thing - like prep work to get rid of the debug loader
not sure when/if they're going to kill it
@roman01la you could try :whitespace
which is REPL compatible
@roman01la the other option is to have your JS sources go through Closure instead of Webpack
but I guess you probably want JSX
then :whitespace
is probably best
not sure about mutual deps, it's just JS code that depends on cljs, but yes, bundling all cljs output into a single file will solve this
but again, can probably write a custom target-fn to handle this somehow
You have two different build outputs that depend on each other
mm, no, just this
import "./out/index.js"
console.log(window.hello_bundler.core);
Right canāt work
You can wait for that stuff though
Thatās what Krell does
in dev Krell waits for the entry point, in prod it just invokes the entrypoint
Looks like it hooks into closure's debug loader?
ha not even
though that would be better
it just waits for cljs.core
to appear then starts
in dev it's easy, it either exists or it doesn't
once it exists then you know that goog and all the other stuff is available
@roman01la the problem with waiting for anything is that goog.base
has to be there, but even goog.base
is written as a script tag
https://github.com/vouch-opensource/krell/blob/dbc31f264349b466174393f465cf2611855708ef/resources/main.dev.js haha, for anyone else curious
Should I expect some kind of requiring-resolve
*function* in cljs in the future?
Something using JS import
function and GCC code splinting?
PS. I know that right now I can use code splitting and dynamic load "directly" from GCC, asking about requiring-resolve
in particular
@souenzzo unlikely to ever have a requiring-resolve