Fork me on GitHub
#shadow-cljs
<
2021-02-08
>
Sean Poulter05:02:57

Thomas. Your docs are amazing. Thank you! It has helped me make sense of ClojureScript macros. πŸ™‚

Sean Poulter05:02:29

Is it possible to write a macro that uses an npm package as a dependency? I’ve run into Gotcha #1: Namespace Aliases and Dependencies from https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html. My goal was to write a quick utility for faking timers in tests with @sinonjs/fake-timers but it seems like that’s not possible with deps from npm. :/ my.util.clj

(ns test.fixtures)

(defmacro with-fake-timers
  [symbol & body]
  `(let [~symbol (fake-timers/install)]
    (try ~@body
      (finally (.uninstall ~symbol)))))
my.util.cljs
(ns test.fixtures
  (:require
   ["@sinonjs/fake-timers" :as fake-timers])
  (:require-macros
   [test.fixtures :refer [with-fake-timers]]))
my.util-test.cljs
(ns test.fixtures-test
  (:require
    [cljs.test :refer [deftest testing is]]
    [test.fixtures :refer [with-fake-timers]]))

(deftest macro-test
  (is (= '_ (macroexpand-1 '(with-fake-timers clock (prn "Hello, world"))))))

(deftest with-fake-timers-test
  (testing "Given a symbol to bind to and a body to execute, "
           "it should replace the global timer APIs and execute the body"
    (let [previous-value js/window.setTimeout]
      (with-fake-timers clock
        (let [actual (= js/window.setTimeout previous-value)
              expected false]
          (is (= actual expected)))))))

Sean Poulter05:02:18

Could this be related to it being a :karma build target? The macro expands as expected but uses the missing ns alias, then throws: > Chrome Headless 88.0.4324.150 (Mac OS 10.14.6) test.fixtures-test with-fake-timers-test FAILED > FAIL in (with-fake-timers-test) (ReferenceError:NaN:NaN) > failed with ReferenceError: fake_timers is not defined > message: Uncaught exception, not in assertion. >

Sean Poulter05:02:22

I’m guessing I should stop fighting and give up on the macro. πŸ˜•

johnjelinek06:02:21

hihi, I'm trying to use shadow-cljs where I need to consume some javascript generated by the typescript compiler. I've tried different ways to transpile and I just don't seem to be getting it right. If I transpile in ES2015+, shadow-cljs complains about references to import * as cdktf from 'cdktf';. If I transpile to CommonJS, it complains as well:

; Execution error (ReferenceError) at (<cljs repl>:1).
module$docker$index is not defined

johnjelinek06:02:45

> tree ./.gen/providers-out/
./.gen/providers-out/
└── docker
    β”œβ”€β”€ config.d.ts
    β”œβ”€β”€ config.js
    β”œβ”€β”€ container.d.ts
    β”œβ”€β”€ container.js
    β”œβ”€β”€ data-docker-network.d.ts
    β”œβ”€β”€ data-docker-network.js
    β”œβ”€β”€ data-docker-registry-image.d.ts
    β”œβ”€β”€ data-docker-registry-image.js
    β”œβ”€β”€ docker-provider.d.ts
    β”œβ”€β”€ docker-provider.js
    β”œβ”€β”€ image.d.ts
    β”œβ”€β”€ image.js
    β”œβ”€β”€ index.d.ts
    β”œβ”€β”€ index.js
    β”œβ”€β”€ network.d.ts
    β”œβ”€β”€ network.js
    β”œβ”€β”€ secret.d.ts
    β”œβ”€β”€ secret.js
    β”œβ”€β”€ service.d.ts
    β”œβ”€β”€ service.js
    β”œβ”€β”€ volume.d.ts
    └── volume.js

1 directory, 22 files

johnjelinek06:02:04

all the exports are within index.js and the modules it refers to call out to cdktf (expected from node_modules)

johnjelinek06:02:29

any help would be much appreciated πŸ™‚

johnjelinek07:02:03

for CommonJS:

> (require '["/docker"])
FileNotFoundException: ~/hello-world/.gen/providers-out/docker (Is a directory)

> (require '["/docker/index" :as docker])
nil

> docker

Execution error (ReferenceError) at (<cljs repl>:1).
module$docker$index is not defined
nil

johnjelinek07:02:50

for ES2015:

ExceptionInfo closure errors {:tag :shadow.build.closure/errors, :errors [{:resource-name "docker/config.js", :source-name "docker/config.js", :line 3, :column 0, :msg "Namespace imports (goog:some.Namespace) cannot use import * as. Did you mean to import cdktf from 'goog:shadow.js.shim.module$cdktf';?"}]

johnjelinek07:02:50

and here's how the module looks like in the node REPL after transpiling to CommonJS:

> var docker = require('./.gen/providers-out/docker')
undefined
> docker
{
  Config: [Getter],
  ContainerNetworkData: [Getter],
  Container: [Getter],
  Image: [Getter],
  Network: [Getter],
  Secret: [Getter],
  Service: [Getter],
  Volume: [Getter],
  DataDockerNetworkIpamConfig: [Getter],
  DataDockerNetwork: [Getter],
  DataDockerRegistryImage: [Getter],
  DockerProvider: [Getter]
}

thheller09:02:33

with which shadow-cljs version did you try? I fixed a bug related to this very recently. try 2.11.17

johnjelinek13:02:04

"shadow-cljs": "^2.11.17",

johnjelinek18:02:51

@U05224H0W: looks like the same version ☝️

thheller18:02:06

on problem is that for all npm dependencies the closure compiler will see them as commonjs

thheller18:02:22

thus import * as cdktf from 'cdktf'; is invalid and would need to be import cdktf from 'cdktf';. which the warning is telling you basically

johnjelinek19:02:13

@U05224H0W: ok, so that's something I'd need to manually change after the transpiler completes if I want to target ES2015? But if it's CommonJS as the target and I'm getting module$docker$index is not define -- what should I do in that case?

johnjelinek19:02:43

regarding ES2015 output and your suggestion about manual change, magic!

> docker
#js {:Config #object[Config$$module$docker$config], :Container #object[Container$$module$docker$container], :ContainerNetworkData #object[ContainerNetworkData$$module$docker$container], :DataDockerNetwork #object[DataDockerNetwork$$module$docker$data_docker_network], :DataDockerNetworkIpamConfig #object[DataDockerNetworkIpamConfig$$module$docker$data_docker_network], :DataDockerRegistryImage #object[DataDockerRegistryImage$$module$docker$data_docker_registry_image], :DockerProvider #object[DockerProvider$$module$docker$docker_provider], :Image #object[Image$$module$docker$image], :Network #object[Network$$module$docker$network], :Secret #object[Secret$$module$docker$secret], :Service #object[Service$$module$docker$service], :Volume #object[Volume$$module$docker$volume]}

> Container
#object[Container$$module$docker$container]

thheller19:02:46

I don't know. can you try with a browser-repl or an actual build?

johnjelinek19:02:43

I didn't make any changes to the references or to the shadow-cljs.edn after transpiling to ES2015 and changing the import statement for every single generated .js file

johnjelinek19:02:59

note, my current ns where it's working with ES2015 now:

(ns demo.script
  (:require ["constructs" :refer (Construct)]
            ["cdktf" :refer (App TerraformStack)]
            ["/docker/index" :as docker :refer (Container)]))

johnjelinek19:02:21

what should I look for in a browser-repl for this?

johnjelinek19:02:31

here's my shadow-cljs.edn:

{:source-paths ["src/main" ".gen/providers-out"]
 :builds {:app {:target :node-script
                :output-to "target/script.js"
                :main demo.script/main}}}

thheller19:02:51

browser-repl for the cases where you get module$docker$index is not defined now

thheller19:02:09

if things even run in the browser. seems to be a node target so that may not be an option.

johnjelinek19:02:06

ya, it's definitely a node target

johnjelinek19:02:37

I'll go with the ES2015 module output for now, thanks! I'm not sure how I would've troubleshooted this without you.

thheller09:02:13

@sean.poulter the easiest path is just creating a function in CLJS that delegates to the npm dep (defn install-timer [] (fake-timers/install)) and call that from the macro instead.

❀️ 3
Sean Poulter12:02:22

Thanks for keeping it simple. πŸ™‚

Sean Poulter14:02:31

That works perfectly. πŸ™

Sean Poulter14:02:09

Do you have a preference for Patreon or PayPal?

thheller14:02:00

all fine πŸ™‚

Adam Helins14:02:03

Wouldn't it be useful to also take into account file deletions when it comes to live reloading? (Eg. UI reflects that a CSS file has been deleted (browser will reload non-existing file))

thheller14:02:35

how would it reload a non-existing file? doesn't make much sense to me

Adam Helins14:02:56

Do what it does for modifications (ie. modify the r query argument -> browser tries to reload file and fails)

thheller14:02:38

yes but why would you willingly force a 404?

Adam Helins15:02:34

For instance in the context of live reloading CSS, it's a bit confusing that nothing changes when a CSS file gets deleted (because the browser knows nothing about that)

thheller15:02:48

yeah still no clue what you are doing but so far I'm not interested in adding that

Adam Helins15:02:25

Haha, all right, it's just I would expect that deleting any live-reloaded asset that is still being used by my code would induce a change to help me notice it, but all right

thheller16:02:44

yeah I can only imagine getting into weird situations. the only sensible things to do would be deleting the link tag used to import the style as thats the only way to remove the css

πŸ‘ 3
thheller16:02:00

but then when you add it again the HTML doesn'lt have the link anymore so it won't be loaded

thheller16:02:35

in general you should only have one css file anyways. at least thats what I have always done. take the many files and generate one that is loaded in the html