Fork me on GitHub

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


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?



   [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:

   ["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?


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


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.


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


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


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 ..


I'm not sure what you mean by 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

npx all-the-package-names | grep get-or-set | wc -l
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: (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
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" ( package every where. In its README, you can find the 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


Hi guys. I ran into some problems with the latest version of CLJS. I reported about it here


@dnolen any thoughts on it?


no will have to look at that later

👌 4

just added one more issue related to node target


reproduced and fixed, cutting a release just for the Quick Start - it seems like a pretty old bug

🎉 4

@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


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?


(:require [ :as math]) (:require [$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 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)

Roman Liutikov12:04:19

@dnolen after looking more into 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


I left that comment on that one

Roman Liutikov12:04:40

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

Roman Liutikov12:04:49

> 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

Roman Liutikov12:04:27

I can work on that, just need more input


in the symbol passed to deftype just (vary-meta x assoc :private true)

👍 4

@dnolen okay, understood. Is it true that $ would only be used in ns declarations? Or could I go (ns (:require [X.Y])) (Y$someFn))?


I tried to be pretty clear about what I proposed


if I haven't mentioned then it's not something under consideration


Didn't you say the main impl would be in the resolve machinery? That sounds like it's usable in code, so I was wondering where you were going with the idea. And you can do (Y$someFn) in Clojure, so I was wondering if the general behavior of $ applied here.


so just rule out anything not in mentioned in the two forms above


using this example (:require [$Math :as math]). How do you know at compile time what can be used before $? I mean even 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


Didn't you say the main impl would be in the resolve machinery? That sounds like it's usable in code, so I was wondering where you were going with the idea. And you can do (Y$someFn) in Clojure, so I was wondering if the general behavior of $ applied here.


@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

dnolen13:04:40 - you can't know what's there anyway, that's the whole point


you used (:require [$Math :as math]) as an example. I'm trying to figure out how you get to the point of knowing that 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?


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

Roman Liutikov13:04:28

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

👍 4
Roman Liutikov14:04:40

I guess there already was a discussion regarding generating JS exports for ^:export vars with :bundle target?


@roman01la what do you mean?

Roman Liutikov14:04:15

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

Roman Liutikov14:04:38

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


this is literally what krell does


just call cljs.main


to rewrite requires for assets


instead a morass of plugins and plugin architectures or giant balls of mud

👍 8

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

Roman Liutikov14:04:42

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 is krufty


so it maybe that we need


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

Roman Liutikov14:04:44

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 😉

Roman Liutikov15:04:15

sounds like a plan 🙂


cljs-3233: what does "disciplined" mean here?


a proper require vs. interacting with global libraries will-nilly


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?


there'll probably be one later in the week to address straggling issues

Roman Liutikov19:04:06

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.

Roman Liutikov19:04:03

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

Roman Liutikov20:04:34

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

Roman Liutikov20:04:02

> 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

Roman Liutikov20:04:10

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

Roman Liutikov20:04:18

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

Roman Liutikov20:04:35

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


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

Roman Liutikov20:04:22

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

Roman Liutikov20:04:44

but again, can probably write a custom target-fn to handle this somehow


You have two different build outputs that depend on each other

Roman Liutikov20:04:38

mm, no, just this

import "./out/index.js"


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

Roman Liutikov20:04:39

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


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