ClojureScript

Native Node modules Node (like "fs") cannot be required

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: 1.10.238
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code and Test

Description

test.cljs

(require 'fs)
(println (fs/readFileSync "README.md" "utf8"))

command:

clj -A:cljs test.cljs

Activity

Hide
Mike Fikes added a comment -

I can reproduce this only when running a file, but not when in the REPL.

deps.edn
{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}}}
test.cljs
(require 'fs)
(println (fs/readFileSync "deps.edn" "utf8"))

REPL

$ clj -Srepro -m cljs.main -re node
ClojureScript 1.10.238
cljs.user=> (require 'fs)
nil
cljs.user=> (println (fs/readFileSync "deps.edn" "utf8"))
{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}}}

nil
cljs.user=>

Run Directly

$ clj -Srepro -m cljs.main -re node test.cljs
Exception in thread "main" clojure.lang.ExceptionInfo: No such namespace: fs, could not locate fs.cljs, fs.cljc, or JavaScript source providing "fs" at line 1 test.cljs {:file "test.cljs", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (require (quote fs))}, :tag :cljs/analysis-error}
	at clojure.core$ex_info.invokeStatic(core.clj:4739)
	at clojure.core$ex_info.invoke(core.clj:4739)
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:697)
	at cljs.analyzer$error.invoke(analyzer.cljc:693)
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:695)
	at cljs.analyzer$error.invoke(analyzer.cljc:693)
	at cljs.analyzer$analyze_deps.invokeStatic(analyzer.cljc:2129)
	at cljs.analyzer$analyze_deps.invoke(analyzer.cljc:2103)
	at cljs.analyzer$ns_side_effects.invokeStatic(analyzer.cljc:3476)
	at cljs.analyzer$ns_side_effects.invoke(analyzer.cljc:3471)
	at cljs.analyzer$analyze_STAR_$fn__2510.invoke(analyzer.cljc:3596)
	at clojure.lang.PersistentVector.reduce(PersistentVector.java:341)
	at clojure.core$reduce.invokeStatic(core.clj:6747)
	at clojure.core$reduce.invoke(core.clj:6730)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3596)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3586)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3616)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3378)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3355)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3545)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3541)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3595)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3586)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3616)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3598)
	at cljs.repl$evaluate_form$fn__6365.invoke(repl.cljc:543)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:542)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:491)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:489)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$load_stream.invokeStatic(repl.cljc:575)
	at cljs.repl$load_stream.invoke(repl.cljc:570)
	at cljs.cli$default_main$fn__6799$fn__6800.invoke(cli.clj:348)
	at cljs.cli$default_main$fn__6799.invoke(cli.clj:347)
	at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1285)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1274)
	at cljs.compiler.api$with_core_cljs.invokeStatic(api.clj:50)
	at cljs.compiler.api$with_core_cljs.invoke(api.clj:34)
	at cljs.compiler.api$with_core_cljs.invokeStatic(api.clj:42)
	at cljs.compiler.api$with_core_cljs.invoke(api.clj:34)
	at cljs.cli$default_main.invokeStatic(cli.clj:326)
	at cljs.cli$default_main.invoke(cli.clj:299)
	at cljs.cli$script_opt.invokeStatic(cli.clj:403)
	at cljs.cli$script_opt.invoke(cli.clj:401)
	at cljs.cli$main.invokeStatic(cli.clj:612)
	at cljs.cli$main.doInvoke(cli.clj:601)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$apply.invoke(core.clj:652)
	at cljs.main$_main.invokeStatic(main.clj:61)
	at cljs.main$_main.doInvoke(main.clj:52)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Show
Mike Fikes added a comment - I can reproduce this only when running a file, but not when in the REPL.
deps.edn
{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}}}
test.cljs
(require 'fs)
(println (fs/readFileSync "deps.edn" "utf8"))

REPL

$ clj -Srepro -m cljs.main -re node
ClojureScript 1.10.238
cljs.user=> (require 'fs)
nil
cljs.user=> (println (fs/readFileSync "deps.edn" "utf8"))
{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}}}

nil
cljs.user=>

Run Directly

$ clj -Srepro -m cljs.main -re node test.cljs
Exception in thread "main" clojure.lang.ExceptionInfo: No such namespace: fs, could not locate fs.cljs, fs.cljc, or JavaScript source providing "fs" at line 1 test.cljs {:file "test.cljs", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (require (quote fs))}, :tag :cljs/analysis-error}
	at clojure.core$ex_info.invokeStatic(core.clj:4739)
	at clojure.core$ex_info.invoke(core.clj:4739)
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:697)
	at cljs.analyzer$error.invoke(analyzer.cljc:693)
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:695)
	at cljs.analyzer$error.invoke(analyzer.cljc:693)
	at cljs.analyzer$analyze_deps.invokeStatic(analyzer.cljc:2129)
	at cljs.analyzer$analyze_deps.invoke(analyzer.cljc:2103)
	at cljs.analyzer$ns_side_effects.invokeStatic(analyzer.cljc:3476)
	at cljs.analyzer$ns_side_effects.invoke(analyzer.cljc:3471)
	at cljs.analyzer$analyze_STAR_$fn__2510.invoke(analyzer.cljc:3596)
	at clojure.lang.PersistentVector.reduce(PersistentVector.java:341)
	at clojure.core$reduce.invokeStatic(core.clj:6747)
	at clojure.core$reduce.invoke(core.clj:6730)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3596)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3586)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3616)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3378)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3355)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3545)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3541)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3595)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3586)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3616)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3598)
	at cljs.repl$evaluate_form$fn__6365.invoke(repl.cljc:543)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:542)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:491)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:489)
	at cljs.repl$evaluate_form.invoke(repl.cljc:484)
	at cljs.repl$load_stream.invokeStatic(repl.cljc:575)
	at cljs.repl$load_stream.invoke(repl.cljc:570)
	at cljs.cli$default_main$fn__6799$fn__6800.invoke(cli.clj:348)
	at cljs.cli$default_main$fn__6799.invoke(cli.clj:347)
	at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1285)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1274)
	at cljs.compiler.api$with_core_cljs.invokeStatic(api.clj:50)
	at cljs.compiler.api$with_core_cljs.invoke(api.clj:34)
	at cljs.compiler.api$with_core_cljs.invokeStatic(api.clj:42)
	at cljs.compiler.api$with_core_cljs.invoke(api.clj:34)
	at cljs.cli$default_main.invokeStatic(cli.clj:326)
	at cljs.cli$default_main.invoke(cli.clj:299)
	at cljs.cli$script_opt.invokeStatic(cli.clj:403)
	at cljs.cli$script_opt.invoke(cli.clj:401)
	at cljs.cli$main.invokeStatic(cli.clj:612)
	at cljs.cli$main.doInvoke(cli.clj:601)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$apply.invoke(core.clj:652)
	at cljs.main$_main.invokeStatic(main.clj:61)
	at cljs.main$_main.doInvoke(main.clj:52)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Hide
Mike Fikes added a comment -

The root cause is that in the code path when cljs.main executes a script, it uses the ensure macro, which ends up establishing a compiler environment that isn't set up for Node—set up using (cljs.env/default-compiler-env). If an opts map is passed to cljs.env/default-compiler-env which contains :target :nodejs entry, then the desired native Node modules, in cljs.js-deps/native-node-modules will be included.

The fix is to instead of using ensure, either use with-compiler-env or to just bind *compiler* to one set up using the right opts. The attached patch looks much larger than it really is, owing to a lot of whitespace change produced by removing an outermost ensure wrapper. It removes the ensure and simply adds env/*compiler* (env/default-compiler-env opts) to the binding list.

I checked that the patch also works in the case of using cljs.main to compile code using 'fs. A CLI test is added ensuring that you can use native modules in -e forms, which is roughly analogous to what happens when running a script. (I also checked tat this new CLI test fails if you don't include the production code change.)

Show
Mike Fikes added a comment - The root cause is that in the code path when cljs.main executes a script, it uses the ensure macro, which ends up establishing a compiler environment that isn't set up for Node—set up using (cljs.env/default-compiler-env). If an opts map is passed to cljs.env/default-compiler-env which contains :target :nodejs entry, then the desired native Node modules, in cljs.js-deps/native-node-modules will be included. The fix is to instead of using ensure, either use with-compiler-env or to just bind *compiler* to one set up using the right opts. The attached patch looks much larger than it really is, owing to a lot of whitespace change produced by removing an outermost ensure wrapper. It removes the ensure and simply adds env/*compiler* (env/default-compiler-env opts) to the binding list. I checked that the patch also works in the case of using cljs.main to compile code using 'fs. A CLI test is added ensuring that you can use native modules in -e forms, which is roughly analogous to what happens when running a script. (I also checked tat this new CLI test fails if you don't include the production code change.)

People

Vote (13)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: