hoplon 2020-04-10

has anyone experienced hoplon's prerendering turning < in inline scripts into <? i'm running into trouble with my app's content security policy because the prerender step is changing the content of an inline script that contains < so that it contains < instead

which seems like a bug. the content of script tags should not be html-escaped

the prerenderer specifically skips scripts

in both the head and the body

in this case, the scripts have :prerender-keep

yeah but that code doesn't look like it knows about :prerender-keep

it's suspicious that it broke all of a sudden

in the middle of changing all the tooling

i think it's been broken the whole time, we just didn't notice in this particular project because the existing scripts we had with :prerender-keep happened not to contain &, <, or >

the new one we're adding wants to do a less-than comparison

can you make a minimal repro case?

to be clear, i've put the debooted branch aside

this is all using the existing boot pipeline for geir

like a helloworld that exhibits the problem

i'll see if i can take a stab at that when i have time

for now, i think i might need to add a postprocessing step that re-encodes < as <

i don't see how it can possibly be including those scripts

i guess inside of script tags only

there are better ways, you can use the baackslash hex encoding if you need to

ah! i was wondering if there were another way to encode it in the JS

like \x12 or whatever

but link me to the script tag in the cljs code

the problematic one

and also have you tried with prerendering off?

sending it to you on adzerk slack

i've actually been wondering if we really need prerendering

since we're getting caching via cloudfront, i'm not sure if prerendering is helping much beyond that

prerendering helps with the flash of unstyled content

have you built it with prerendering off though?

to eliminate other issues unrelated to prerendering that may be causing it

yeah, without prerendering the problem goes away

my first thought was that it might be an advanced optimizations issue, but that wasn't it

hmm, it looks like \x3C is the escape sequence for <, but it doesn't seem to work outside of javascript string literals

i've only experimented in the JS console, though. not sure if it behaves differently in an inline script tag

in the JS console you can do escape("<"), which returns "%3C"

then you can do "\x3C", and that returns "<"

you should be able to put that in the js source, no?

i'll give it a try

you need to escape the backslash because clojure strings interpret it

like (script "foo \\3C ...")

that doesn't appear to work

what does it look like when you do "show source" (not the elements pane of the devtools)

i changed the offending part of the code to for(w=0,x=v.length;w\\x3Cx;++w) whereas the \\x3C used to be <

i haven't tried prerendering yet, so show source doesn't include the script tag

i suspect the result would be the same

what does the script tag look like in the elements pane

the easiest thing to do really is to make your own html file and then mount the app in a div inside there

then you don't need prerendering because you can pull out the things you want to have prerendered into the html file

looks like what i expected: w\x3Cx intsead of w<x

making our own html file is a good suggestion. i've been thinking about that in the back of my mind

i would just need to do some post-processing to insert the right cache keys for cache busting

could another solution be to put the inline JS into a file instead? or would that potentially screw up the load order?

putting the JS into a file got me un-stuck

just catching up,was the problem literla JS inside a script tag in a cljs file?

with :prerender-keep

although i guess i'm not 100% sure if :prerender-keep was related

i think i've run into that before,with google analytics or something

i wonder about using js* to inline JS strategically

that would roll it into the cljs source

i guess sometimes you need JS to run before the cljs kicks in tho

does it need to be inline for that to be the case?

like is the load order for this:

<script>alert("lol");</script>
<script src="index.cachekeygoeshere.html.js"></script>
any different than the load order for this?
<script src="alert_lol.js"></script>
<script src="index.cachekeygoeshere.html.js"></script>

should be no different

i was suggesting using js* to put alert("lol") in index.cachekeygoeshere.html.js

we're actually doing that for google analytics in geir

it's just that it only works because the inline JS doesn't include &, <, or > which hoplon's prerenderer would escape

seems to me like the prerender shouldn't escape script tag innards

from a pure xml sense it make sense, and is a good default. maybe a bug in prerender?

i'm thinking it's a bug, too

like if the type of the DOM node is "script," then the escaping shouldn't happen

anothre option is golf that code to not use any escaped chars like goog did

partially as entertainment 😆

haha, nothing like a rousing game of code golf 😄

i'm sure there is some too-clever way to get "less-than" semantics without using <

function lt(x, y) { var z = (y - x); return z != "0" && z.toString()[0] !== "-"; }

function lt(x, y) { return (x - y).toString()[0] == "-"; }
would this work?

that would be <= because if x == y, [0] !== "-"

close but no banana

hmm, i tried that in my browser and lt(2, 2) is false like i expected

oh i see, yes, it should work

my thing was lte, yours is lt

x is < y iff (y - x) < 0, e.g. its string rep. has a leading dash

i bet google did that intentionally man

i wonder if a tool of theirs does this kind of rewriting even

lt=(x,y)=>(x-y).toString()[0]=="-"
golfed a bit further 😄

i keep forgetting that JS has had that for like 5-10 years lol

a few years ago at my new job i spent a few weeks relearning JS starting from 2012 or something

it has all the things now pretty much

in addition to all the crazy things it always had

you know, i guess you can't make a real short-circuting && as a function, because the real one delays evaluation

the best you could do is wrap every argument in a function at the call site

for the purposes of <= and >=, you could just implement < and > and then call it with !

lol this is unfolding into a great big tech company interview question

"please sort this linked list in JS, and you can't use &, <, or >"

oh sweet, yes good point

you could even say it's not a contrived question, it actually came up in the course of work haha