Fork me on GitHub
#clojurescript
<
2018-03-07
>
Mikko Harju05:03:40

Why is this:

app:filippos.translations=> (keyword "unit" "4g")
:unit/4g
app:filippos.translations=> :unit/4g
clojure.lang.ExceptionInfo: NO_SOURCE_FILE [line 1, col 9] Invalid keyword: :unit/4g. {:type :reader-exception, :ex-kind :reader-error, :file "NO_SOURCE_FILE", :line 1, :col 9}
...
So I know the spec says
;; A keyword string, like a symbol, begins with a non-numeric
;; character and can contain alphanumeric characters and *, +, !, -,
;; _, and ?.  (see  for details).
but why can I construct one without namespace, eg. :4g, with (keyword "unit" "4g") but not :unit/4g?

Mikko Harju05:03:36

The problem (that can really be circumvented in many ways is that I have a fixed set of constants (defined by a standard) and I’d hoped to just make them keys to translation maps that are stored as EDN. They are keyword-mapped and prefixed by their use (ie. unit/ for unit-like keys)

sonnyto05:03:10

how is clojurescript releases built?

Mikko Harju06:03:42

Ah!

;; keyword does not validate input strings for ns and name, and may
;; return improper keywords with undefined behavior for non-conformant
;; ns and name.

Mikko Harju06:03:46

That explains it.

Mikko Harju06:03:57

Did not read that far below 😄

sonnyto06:03:27

@blackawa cool...thanks!

blackawa06:03:12

I will write same answer to your google group, so some other people can find later.

sonnyto06:03:45

people should ask questions on the google groups instead of slack so that there is a searchable record. slack wouldnt let you search unless you pay

noisesmith10:03:36

@mikko the keyword function is kind of counterintuitive (keyword "(+ foo bar baz)") - no validation or sanity

raymcdermott10:03:51

a distribution question - does anyone have experience using yarn to publish cljs CLI scripts? Any gotchas?

thheller10:03:05

@raymcdermott I have experience. no gotchas.

raymcdermott10:03:29

@thheller excellent - which toolchain did you use to create the scripts?

thheller10:03:18

shadow-cljs of course which is also the package on npm I made the experiences with 😉

raymcdermott10:03:46

yeah, I was looking at that and it does look excellent for this purpose

raymcdermott10:03:57

@thheller [ I understand that you’re biased but still, it does look right ]

thheller10:03:02

based on this conversation https://github.com/kkinnear/zprint/issues/45 I made a little toy example of how I'd structure things https://github.com/thheller/zprint-npm

raymcdermott10:03:38

oh that’s great - thanks very much

thheller10:03:11

basics is simple. the package directory is what gets published to npm. the build config writes package/main.js

thheller10:03:32

cd package; npm publish would publish the thing

thheller10:03:55

been meaning to write a guide for this but didn't get around to it yet

raymcdermott11:03:40

looks like you use npm rather than yarn, no?

thheller11:03:32

I do use yarn most of the time but weirdly enough npm to publish.

thheller11:03:20

yarn is nicer when installing things but for deploying I never had issues with npm

thheller11:03:36

should be pretty much identical though

thheller11:03:01

it all goes to the same places

raymcdermott11:03:29

sure, ok just wanted to confirm - thanks

raymcdermott11:03:27

hmmm …. I’m missing something

raymcdermott11:03:02

when I run npm install I don’t get the package installed anywhere

raymcdermott11:03:44

hmm - I’ll have to go read the manual properly

thheller11:03:11

you can install npm install ./package

thheller11:03:19

to install it without publishing

thheller11:03:37

or what do you mean?

raymcdermott11:03:42

yes that’s what I mean

raymcdermott11:03:35

I did cd package; npm install

raymcdermott11:03:42

and that did not work

thheller11:03:57

yeah that would only install the dependencies of that package

raymcdermott11:03:57

also now npm install ./package barfs

raymcdermott11:03:03

enoent ENOENT: no such file or directory, chmod ‘/Users/ray/dev/oss/zprint-npm/node_modules/not-the-official-zprint/main.js’

raymcdermott11:03:13

so there is a step before this..

thheller11:03:28

did you shadow-cljs release script?

raymcdermott11:03:49

like I said - I need to read the manual

thheller11:03:54

yeah I haven't hooked that into the scripts part

thheller11:03:30

shadow-cljs release script compiles and optimizes the CLJS and writes the output to package/main.js

raymcdermott11:03:58

of course yes, sorry I was assuming that it was all hooked up

thheller11:03:16

yeah its not hooked up because I'm lazy 😉

raymcdermott11:03:26

I will leave you alone now

raymcdermott11:03:39

yeah you’re a terrible person

raymcdermott15:03:41

it seems I have to call process.exit even in the normal case - seems odd

raymcdermott15:03:44

otherwise I get JS runtime connected.

raymcdermott15:03:55

and I have to Ctrl-C

thheller17:03:57

you are talking about watch? that is the REPL client connecting to the server which will also keep the process alive.

thheller17:03:59

{:source-paths
 ["src"]

 :dependencies
 [[zprint "0.4.6"]]

 :builds
 {:script {:target :node-script
           :output-to "package/main.js"
           :main zprint.npm/main
           :compiler-options {:infer-externs :auto
                              :optimizations :advanced}
           :devtools {:enabled false}}}}

thheller17:03:14

you can disable those by setting :devtools {:enabled false}

raymcdermott08:03:07

regardless of that setting, I get the JS runtime connected message with code like this …

raymcdermott08:03:16

(defn main
  "obtain the tenant from the command line and save it to the config file"
  [tenant]
  (set-config!)
  (if tenant
    (spit (get-config-file-str) (vouch/set-tenant! tenant))
    (js/process.stderr.write "Please specify the tenant\n")))

raymcdermott08:03:12

if I adjust the code to add js/process.exit I don’t get the runtime connected message

thheller08:03:44

whats your config? are you sure its in the correct location? don't use process.exit please 😛

raymcdermott08:03:52

so maybe I’m missing something about whether stdin is hooked in somehow

raymcdermott08:03:10

I know - I don’t want to 🙂

thheller08:03:24

JS runtime connected is the REPL which gets injected by default connecting to the server.

raymcdermott08:03:35

I took the config from the zprint project you made

thheller08:03:47

{:source-paths
 ["src"]

 :dependencies
 [[zprint "0.4.6"]]

 :builds
 {:script {:target :node-script
           :output-to "package/main.js"
           :main zprint.npm/main
           :compiler-options {:infer-externs :auto
                              :optimizations :advanced}
           :devtools
           {:enabled false}}}}

thheller08:03:01

you are sure that devtools didn't slide into :compiler-options or so?

raymcdermott08:03:20

ah, that’s possible

raymcdermott08:03:38

(in fact it did of course)

thheller09:03:02

stupid spec doesn't complain when optional keys appear somewhere else 😛

raymcdermott09:03:57

the watch still works so what do I lose

thheller09:03:09

REPL and hot reloading

thheller09:03:20

which you don't need for CLI scripts anyways

raymcdermott09:03:37

I’m writing a script so that’s not a concern on this one

raymcdermott09:03:15

how can I help you to document this?

thheller09:03:21

good question. maybe I should mention it somewhere here? https://shadow-cljs.github.io/docs/UsersGuide.html#target-node

thheller09:03:09

it might be best to just disable the devtools for node builds

thheller09:03:31

if you are writing server type stuff the REPL/reload stuff is good

thheller09:03:38

for CLI script its not required

thheller09:03:43

dunno about the default 😛

raymcdermott09:03:14

yes, I think it would to have it mentioned for node CLI scripts

thheller09:03:15

just had an idea. it should be possible to figure out if the REPL websocket is the only thing keeping the process alive. if so close it down and let the process exit.

raymcdermott10:03:43

that would be nice

raymcdermott10:03:52

then nothing to doc

raymcdermott10:03:08

I’ll fork it and give it a spin

Bravi10:03:22

this might sound like a silly question, but whenever I create an app using clojurescript (re-frame + reagent to be more specific), to deploy the app, I usually upload contents in my resources/public to the server. is this not the way I should be deploying it?

thheller10:03:42

@bravilogy depends on what is in your resources/public 😉

Bravi10:03:21

well.. some images, css, index.html and compiled cljs->js

thheller10:03:07

in general uploading the files is perfectly fine. I do that.

thheller10:03:39

you should however ensure that only optimized builds are used

thheller10:03:50

ie. :advanced compiled not :none development builds

Bravi10:03:00

i was reading a tutorial on how to deploy a clojure application to digitalocean, and every single tutorial I’ve seen, they just talk about lein uberjar command, but none of them mention the folder structure and clojurescript part

Bravi10:03:31

yeah I have those in my project.clj

thheller10:03:34

yeah many people just bundle an uberjar and use a clj webserver to serve the files out of that jar

thheller10:03:47

but you don't have to if you don't have a clj server part

Bravi10:03:57

and what’s the folder structure for that?

thheller10:03:08

I keep the js separate even though I have a clj server

Bravi10:03:13

for example, I have a chestnut template application here

thheller10:03:41

the resources directory gets added to the uberjar

Bravi10:03:57

oh so is the end result just 1 file?

Bravi10:03:16

hmm.. I didn’t know that 😄

Bravi10:03:28

how about the images?

thheller10:03:35

everything in resources

Bravi10:03:09

but what I mean is, if I have an image upload script and users can upload their images

noisesmith10:03:59

yeah, then you have to start doing server management since you have a static OS dependent thing specific to that server

noisesmith10:03:08

(unless you use a bucket API like s3)

thheller10:03:48

@bravilogy it depends on the server you use. ring can serve files from a directory just fine.

Bravi10:03:10

yeah I use ring. so a pseudo structure of the application would be

my-app
\
 application.jar
 uploads/

thheller10:03:46

use ring.middleware.file to serve the uploads directory under whichever path you want

Bravi10:03:19

it’s so strange that everything is packaged in 1 file 😄

noisesmith10:03:47

it's handy - in the end using a file system is the weird / inflexible thing

noisesmith10:03:02

it's nice when I know putting my one uberjar on a server that can run java is all I need

thheller10:03:13

I don't like using uberjars for .cljs since you have to restart the java process in order to update the cljs

Bravi10:03:16

I’m still into the PHP folder structure mindset

noisesmith10:03:17

complexity comes when I start trying to use resources tied to that server

thheller10:03:48

so my cljs output just gets copied to the server with an nginx in front of it

Bravi10:03:03

yeah that’s exactly what I’ve done so far @thheller

noisesmith10:03:15

@thheller: sure but at that point you are taking on the complexity of managing the server as a stateful resource, in exchange for the optimization of new cljs without restarts

Bravi10:03:19

copying the cljs output

noisesmith10:03:27

it works, just be aware of the tradeoffs you are making complexity wise

thheller10:03:44

@noisesmith given that I do CLJS updates with a frequency of like 10:1 compared to CLJ updates

thheller10:03:02

I happily accept that tradeoff since I can do restartless deploys of CLJS

noisesmith10:03:34

right, sounds like that is worth it for you - for the app I've been working on for the last few years, there are rarely cljs updates that are not tied to new clj code, so it's simpler to just treat the whole app as a single uberjar package

thheller11:03:25

yeah uberjars are a great option as well

Bravi11:03:00

and I’m guessing there’s no threat for end users to ‘read’ the BE code somehow, even though everything is served from 1 file right?

thheller11:03:32

@bravilogy depends on how you expose those jar contents but typically no

noisesmith11:03:47

@bravilogy in theory no more risk than it is to serve files from a file system that also include secrets

noisesmith11:03:14

I mean, if you are serving from the fs, a misconfiguration means you could serve up the whole app as source code for download, right?

noisesmith11:03:43

at least with serving inside a jar, the simple failure case is limited to things inside that jar :P

noisesmith11:03:26

(in past ring versions there have been file system path traversal exploits btw, there's been no jar traversal exploits that I know of)

Bravi11:03:35

yeah I get that, but what I mean is for example in typical LEMP, you have a public or public_html folder which has index.php file that loads the application that lives at the upper level of that public folder

noisesmith11:03:55

sure, and someone asks for ../../ and gets all your other files :P

thheller11:03:23

that hasn't been an issue for ages

noisesmith11:03:37

I know what you mean, but in practice the management of resource paths inside a jar is actually simpler than managing them on the os level

noisesmith11:03:57

@thheler ring had that bug very recently on file systems, but not serving from a jar

noisesmith11:03:15

I know php has been fine, they got abused so much they had to fix it

noisesmith11:03:22

if security matters to you at all, consider using nginx as a reverse proxy for actual access to things on disk though

noisesmith11:03:46

@bravilogy but to your original point, I haven't heard of bugs serving unintended resources from jars - the classpath is managed relatively strictly. I have heard of bugs on the file system though.

Bravi11:03:08

yeah, that’s what I was trying to understand. I guess that standalone jar sort of ‘knows’ to only serve stuff from resources/public

Bravi11:03:47

I’ve never worked with java before and not entirely sure how jar files work 😄

noisesmith11:03:56

specifically the wrap-resource ring handler is configured to look at a specific subpath

thheller11:03:10

@bravilogy jars are basically zip files. nothing more.

Bravi11:03:56

interesting

Bravi11:03:23

I guess I need to actually deploy an application to fully understand how things work 😄

noisesmith11:03:26

a key thing here is that the java concept of classpath abstracts over things inside zip files as well as things on disk

Bravi11:03:37

I usually use travisCI to trigger my cljs compilations and then it automatically deploys whatever is in resources/public

noisesmith11:03:48

so the same code can access things from either place, depending on what's visible at runtime (the classpath argument to the vm)

Olical13:03:52

Anyone had any issues with jdk9 + advanced optimisations + externs?

Olical14:03:34

Interesting! Thanks a lot!

Olical14:03:57

I'm guessing there will be another release before it's marked as stable. The current latest jar gives us errors around MapEntry in the schema lib, it looks like a fix for that went onto master a little while ago.

Olical14:03:18

So we can't upgrade just yet, we're just going to drop our JDK version down until then.

mfikes14:03:08

@olical Right. The 1.10.126 is just a pre-release, a "beta" if you will

mfikes14:03:37

@olical Having said that, if your toolchain is using deps.edn ClojureScript can itself now be used as a git dep, so you can depend on the latest :sha in the interim

Olical14:03:10

Did think of that, I'm afraid we're on lein 😄 we can deploy a fork no problem though.

Olical14:03:14

(if we need to)

Olical14:03:25

I want that sweet, sweet, 1.10 caching so bad.

mfikes14:03:57

Yeah, if you want the caching behavior, you need to depend on a built dep anyway (not a git dep)

amarjeet15:03:33

Hi folks, have a rookie-level question: In the context of a CLJS web app, when we make network call from the client-side, an obvious idea is to execute it when the client triggers an event (ex: onClick). Are there other places/stages that can be good fit for network calls? Executing it within component lifecycle doesn't seem to be a great idea. What has been your experience?

noisesmith15:03:48

depends on what kind of call, I work on an app where a websocket connection is initiated by the client (or else fail-over to long polling) and async callbacks are set up so the rest of the system doesn't start up until that facility is available

souenzzo15:03:30

@mfikes clojurescript 1.10 has know issue with cyclic deps? I'm having problems trying to load my user.cljs namespace.

mfikes15:03:52

@souenzzo No new regressions AFAIK. There is an older issue https://dev.clojure.org/jira/browse/CLJS-2055 where it fails to indicate cyclic deps and locks up. If you see a lock up do a jps and then a jstack on the compiler process to get the stack trace.

amarjeet15:03:23

@noisesmith One common use-case is similar to the one you described - say, I want to render a component that shows the data for an user. But, before I render the component, I need to have this data (for certain user-id).

noisesmith15:03:24

sure, I think a common thing (certainly what we do) is to start with the data parts empty and fill them in when the data comes back

souenzzo15:03:36

It's on #figwheel repl. my hotreload isn't working anymore cos it cant load user.cljs. I'm deploying some patches, I will debug this problem soon.

amarjeet15:03:03

@noisesmith hmm, yeah, this can work

doubleagent21:03:28

How do I call await on a js promise?

noisesmith21:03:48

(.await p ...)

justinlee21:03:18

wow how does that work? await is a keyword not a method

noisesmith21:03:08

@lee.justin.m oh, I thought it was a method, if it's a syntax I don't know

noisesmith21:03:27

and in that case .await ... might not even work

justinlee21:03:54

I’d be interested if there was actually a way to do that without a library. You can’t call await outside of a function that has been declared async

noisesmith21:03:26

oh, right, that is special

justinlee21:03:59

the simple solution is to call then on the promise instead of trying to await it

noisesmith21:03:22

thanks for the correction

justinlee21:03:54

i’ve never used it, so i can’t promise it will work

doubleagent21:03:28

i'm really just trying to simulate a long-running function call

doubleagent21:03:39

(sleep x) doesn't exist

justinlee21:03:20

can you just use js/setTimeout?

justinlee21:03:01

oh wait, by long running, do you mean blocking and long running or async and long running

doubleagent21:03:11

i'll be simulating an ajax call, so async. js/setTimeout should work

justinlee21:03:12

it’s a slight pain because the scoping rules are different but a straight setTimeout or the combination of a Promise + timeout will almost always serve