2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面以了解更多关于它是如何工作的信息。

0 投票
ClojureScript
这里有一个小(非最小)的仓库: https://github.com/au-phiware/boot-parsets

该仓库包括一个 es6 模块和一个 cljs 文件,它们都依赖于一个 node 模块,产生了以下错误。


正在编写 main.cljs.edn...
编译 ClojureScript...
• main.js
                                       java.lang.Thread.run                  Thread.java:  748
         java.util.concurrent.ThreadPoolExecutor$Worker.run      ThreadPoolExecutor.java:  617
          java.util.concurrent.ThreadPoolExecutor.runWorker      ThreadPoolExecutor.java: 1142
                        java.util.concurrent.FutureTask.run              FutureTask.java:  266
                        ...                                   
                        clojure.core/binding-conveyor-fn/fn                     core.clj: 2022
                              adzerk.boot-cljs/compile-1/fn                boot_cljs.clj:  160
                                   adzerk.boot-cljs/compile                boot_cljs.clj:   72
                                          boot.pod/call-in*                      pod.clj:  413
                        ...                                   
org.projectodd.shimdandy.impl.ClojureRuntimeShimImpl.invoke  ClojureRuntimeShimImpl.java:  102
org.projectodd.shimdandy.impl.ClojureRuntimeShimImpl.invoke  ClojureRuntimeShimImpl.java:  109
                        ...                                   
                                          boot.pod/call-in*                      pod.clj:  410
                                      boot.pod/eval-fn-call                      pod.clj:  359
                                         clojure.core/apply                     core.clj:  657
                        ...                                   
                         adzerk.boot-cljs.impl/compile-cljs                     impl.clj:  151
                                       cljs.build.api/build                      api.clj:  205
                                       cljs.closure/build                  closure.clj: 2595
                            cljs.closure/handle-js-modules                  closure.clj: 2496
                            cljs.closure/process-js-modules                  closure.clj: 2389
                            cljs.closure/convert-js-modules                  closure.clj: 1680
                            com.google.javascript.jscomp.Compiler.parse                Compiler.java:  995
                         com.google.javascript.jscomp.Compiler.parseInputs                Compiler.java: 1731
      com.google.javascript.jscomp.deps.ModuleLoader.<init>            ModuleLoader.java:   92
com.google.javascript.jscomp.deps.ModuleLoader.resolvePaths            ModuleLoader.java:  276
java.lang.IllegalArgumentException: 解析后重复的模块路径:/home/corin/Projects/Demos/boot-parsets/node_modules/d3/d3.js
        clojure.lang.ExceptionInfo: 解析后重复的模块路径:/home/corin/Projects/Demos/boot-parsets/node_modules/d3/d3.js
    来自::boot-cljs
        clojure.lang.ExceptionInfo: 解析后重复的模块路径:/home/corin/Projects/Demos/boot-parsets/node_modules/d3/d3.js
    行:33


运行 `boot cljs` 以重现问题。

此补丁在预处理之前从输入源文件集中删除重复项。使用此补丁,仓库可以正确编译。

13 个回答

0 投票
by

评论由:mfikes 提出

你好,Corin,

  1. 你签署了CA吗?(你的名字没有出现在https://clojure.org/community/contributors上)
  2. 你能提供一个不使用下游工具的最小重现示例吗?(错误报告详情在https://script.clojure.org/community/reporting-issues
0 投票

评论者:phiware

  1. 我已经签署了CA(我在提交这个错误后所做的)。
  2. 没问题,我今天稍后会继续处理。在GitHub上链接到项目可以吗,或者我应该上传tar包?
0 投票

评论由:mfikes 提出

你好Corin,不可以链接到GitHub,任何重现案例都不应使用任何下游工具(Leiningen,Boot等)——这意味着重现应该仅依赖于发货的{{cljs.jar}},可执行文件,就像快速入门中的示例一样 https://script.clojure.org/guides/quick-start

0 投票

评论由:mfikes 提出

Hey Corin,你可能想按照https://script.clojure.org/community/patches中的说明提交补丁(你的当前补丁不会使用{{git am}}应用,我相信这是David最终使用的工具)。

0 投票
_评论者:mfikes_

应用{{patch}}时{{lein test}}失败


在(commonjs-module-processing)(module_processing_tests.clj:54)中失败
处理后的模块被添加到:libs
预期:当在编译器环境cenv中执行(closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/reactJS.js", :provides ["React"], :module-type :commonjs} {:file "src/test/cljs/Circle.js", :provides ["Circle"], :module-type :commonjs, :preprocess :jsx}], :closure-warnings {:non-standard-jsdoc :off}})时,(:foreign-libs [], :ups-foreign-libs [], :libs [(test/platform-path "out/src/test/cljs/reactJS.js") (test/platform-path "out/src/test/cljs/Circle.js")], :closure-warnings {:non-standard-jsdoc :off}})
  实际:(not (= {:foreign-libs [], :ups-foreign-libs [], :libs ["out/src/test/cljs/reactJS.js" "out/src/test/cljs/Circle.js"], :closure-warnings {:non-standard-jsdoc :off}} {:foreign-libs [], :closure-warnings {:non-standard-jsdoc :off}, :libs ["out/src/test/cljs/Circle.js" "out/src/test/cljs/reactJS.js"], :ups-foreign-libs []}))
0 投票

评论者:phiware

谢谢Mike,

我会尽力遵循所有这些说明,但我看不出我应该如何提供复现(没有链接,没有附件)……是否应该是内联代码块?

此外,在提交下一个补丁之前,我将继续运行{{lein test}}。我注意到差异仅在于{{:libs}}向量的项目顺序,你能建议顺序是否重要吗?

0 投票

评论由:mfikes 提出

你好,Corin,

内联代码块非常棒。任何最小化且不依赖于超出版本{{cljs.jar}}的示例都可以用来演示问题(直接在REPL中或通过构建API编译)。这是一个使用构建API的近期示例:https://dev.clojure.org/jira/browse/CLJS-2397?focusedCommentId=47278&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-47278

除了{{lein test}}以外的其他测试也在进行中。请参见https://script.clojure.org/community/running-tests

我还没有深入研究这个问题,所以不能确定项目的顺序是否重要。

0 投票
_评论者:phiware_

h2. 复现问题的步骤。

考虑以下三个源文件

{code:title=src/distinct_inputs/core.cljs}
(ns distinct-inputs.core
  (:require [d3]
            [circle :refer [circle]]))

(-> d3
    (.select "body")
    (.append "svg")
    (.attr "width" 200)
    (.attr "height" 200)
    (.call circle "steelblue"))


{code:title=es6/circle.js}
import * as d3 from 'd3';

export function circle(sel, color) {
  return sel
    .append("circle")
    .attr("cx", "100px")
    .attr("cy", "100px")
    .attr("r", "100px")
    .attr("fill", color);
}


{code:title=build.clj}
(require 'cljs.build.api')

(cljs.build.api/build
  "src"
  {:main 'distinct-inputs.core
   :output-to "out/main.js"
   :install-deps true
   :foreign-libs [{:file "es6"
                   :module-type :es6}]
   :npm-deps {:d3 "3.5.16"}})


执行{{cljs}}

{code:none}
java -cp cljs.jar:src clojure.main build.clj


h2. 预期结果

{{cljs}}应该在标准输出中没有产生任何内容,干净地退出并写入以下文件(大约)。

{code:title=out/main.js}
var CLOSURE_UNCOMPILED_DEFINES = {};
var CLOSURE_NO_DEPS = true;
if(typeof goog == "undefined") document.write('<script src="out/goog/base.js"></script>');
document.write('<script src="out/goog/deps.js"></script>');
document.write('<script src="out/cljs_deps.js"></script>');
document.write('<script>if (typeof goog == "undefined") console.warn("ClojureScript could not load :main, did you forget to specify :asset-path?");</script>');
document.write('<script>goog.require("process.env");</script>');
document.write('<script>goog.require("distinct_inputs.core");</script>');


{code:title=out/distinct_inputs/core.js}
goog.provide('distinct_inputs.core');
引入依赖:goog.require('cljs.core');
引入依赖:goog.require('module$distinct_inputs$node_modules$d3$d3');
引入依赖:goog.require('module$distinct_inputs$es6$circle');
module$distinct_inputs$node_modules$d3$d3.select("body").append("svg").attr("width",(200)).attr("height",(200)).call(module$distinct_inputs$es6$circle.circle,"steelblue");

文件映射地址:// sourceMappingURL=core.js.map


{code:title=out/es6/circle.js}
定义模块:goog.provide("module$distinct_inputs$es6$circle"); 引入依赖:goog.require("module$distinct_inputs$node_modules$d3$d3"); 函数定义:function circle$$module$distinct_inputs$es6$circle(sel,color){return sel.append("circle").attr("cx","100px").attr("cy","100px").attr("r","100px").attr("fill",color)}定义全局变量:module$distinct_inputs$es6$circle.circle=circle$$module$distinct_inputs$es6$circle


2. 实际结果。

{{cljs}} 运行退出码为 1,并产生以下标准输出。

{code:none|title=stdout}
主线程异常:java.lang.IllegalArgumentException: 解析模块路径重复:/distinct_inputs/node_modules/d3/d3.js,编译中:(distinct_inputs/build.clj:3:1)
   在 clojure.lang.Compiler.load(Compiler.java:7391)
   在 clojure.lang.Compiler.loadFile(Compiler.java:7317)
   在 clojure.main$load_script.invokeStatic(main.clj:275)
   在 clojure.main$script_opt.invokeStatic(main.clj:335)
   在 clojure.main$script_opt.invoke(main.clj:330)
   在 clojure.main$main.invokeStatic(main.clj:421)
   在 clojure.main$main.doInvoke(main.clj:384)
   在 clojure.lang.RestFn.invoke(RestFn.java:408)
   在 clojure.lang.Var.invoke(Var.java:379)
   在 clojure.lang.AFn.applyToHelper(AFn.java:154)
   在 clojure.lang.Var.applyTo(Var.java:700)
   在 clojure.main.main(main.java:37)
由以下异常触发:java.lang.IllegalArgumentException: 解析模块路径重复:/distinct_inputs/node_modules/d3/d3.js
   在 com.google.javascript.jscomp.deps.ModuleLoader.resolvePaths(ModuleLoader.java:276)
   在 com.google.javascript.jscomp.deps.ModuleLoader.<init>(ModuleLoader.java:92)
   在 com.google.javascript.jscomp.Compiler.parseInputs(Compiler.java:1731)
   在 com.google.javascript.jscomp.Compiler.parse(Compiler.java:995)
   在 cljs.closure$convert_js_modules.invokeStatic(closure.clj:1680)
   在 cljs.closure$process_js_modules.invokeStatic(closure.clj:2371)
   在 cljs.closure$handle_js_modules.invokeStatic(closure.clj:2495)
   在 cljs.closure$build.invokeStatic(closure.clj:2592)
   在 cljs.build.api$build.invokeStatic(api.clj:204)
   在 cljs.build.api$build.invoke(api.clj:189)
   在 cljs.build.api$build.invokeStatic(api.clj:192)
   在 cljs.build.api$build.invoke(api.clj:189)
   在 user$eval24.invokeStatic(build.clj:3)
   在 user$eval24.invoke(build.clj:3)
   在 clojure.lang.Compiler.eval(Compiler.java:6927)
   在 clojure.lang.Compiler.load(Compiler.java:7379)
   ... 11 more


上述预期文件没有产生。

2.1 异常原因。

{{ModuleLoader#resolvePaths}} 抛出的异常是由于同一个输入文件(即 {{node_modules/d3/d3.js}})被指定多次给 {{Compiler#initModules}}。{{Compiler#getAllInputsFromModules}} 中有如下注释:

{code:title=src/com/google/javascript/jscomp/Compiler.java}
        // 注释(nicksantos): 如果一个输入存在于多个模块中,
        // 它会在输入列表中显示两次,然后我们
        // 将在后续步骤中得到错误。


{{cljs.closure/process-js-modules}} 被提供一个包含重复条目 {{:foreign-libs}} 向量,该向量为重复调用的结果 {{cljs.closure/node-inputs}};一次是为 {{out/cljs$node_modules.js}}(这可能是由于 {{distinct_inputs/core}} 中的依赖),另一次是为 {{es6/circle.js}}。

简而言之,对 D3 的依赖被 ClojureScript 源文件和 JavaScript 模块源文件同时引入。

2.2 建议的解决方案。

在这种情况下,尽管在 {{cljs.closure/node-inputs}} 中使用了 {{distinct}},但 {{:foreign-libs}} 向量中仍包含重复的条目。一个可能的解决方案是在 {{cljs.closure/node-inputs}} 中删除 {{distinct}} 的使用,并要求调用者使用 {{distinct}}。

{code:title=解决方案 A}
来自 063e35080c14d35189ab7827f25f071e958ab5b4 Mon Sep 17 00:00:00 2001
发件人:Corin Lawson <[email protected]>
日期:2017年11月21日星期二 01:31:53 +1100
主题:[PATCH] CLJS-2402:确保:foreign-libs 向量包含唯一的条目。
 

---
 src/main/clojure/cljs/closure.clj | 19 ++++++++++---------
 1 个文件已更改,10 行代码被插入,9 行代码被删除。

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index a686f878..74a0cc86 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2219,7 +2219,7 @@
      (when env/*compiler*
        (:options @env/*compiler*))))
   ([entries opts]
-       (into [] (distinct (mapcat #(node-module-deps % opts) entries)))))
+       (into [] (mapcat #(node-module-deps % opts) entries))))
 
 (defn index-node-modules
   ([modules)
@@ -2480,14 +2480,15 @@
        output-dir (util/output-directory opts)
        opts (update opts :foreign-libs
                (fn [libs]
-                 (into (if (= target :nodejs)
-                         []
-                         (index-node-modules node-required))
-                           (into expanded-libs
-                               (node-inputs (filter (fn [{:keys [module-type]}]
-                                                    and (some? module-type)
-                                                        not= module-type :amd)))
-                                                    expanded-libs))))))
+                 (distinct
+                   (into (if (= target :nodejs)
+                         []
+                         (index-node-modules node-required))
+                         (into expanded-libs
+                               (node-inputs (filter (fn [{:keys [module-type]}]
+                                                      and (some? module-type)
+                                                      not= module-type :amd)))
+                                                    expanded-libs)))))))
         opts (if (some
                      (fn [ijs]
                        (let [dest (io/file output-dir (rel-output-path (assoc ijs :foreign true) opts))]
--
2.13.0



一种更通用的解决方案是必须确保输入文件集(即 {{js-modules}})是唯一的。此补丁会更简单(即不涉及我不理解的代码)且更接近 Google Closure Compiler 的调用。

{code:title=解决方案B}
从 6bf11a24b93642e118e6d29c5af8a137fa01ea94 Mon Sep 17 00:00:00 2001
发件人:Corin Lawson <[email protected]>
日期:2017年11月19日星期日 20:25:31 +1100
主题:[PATCH] CLJS-2402:确保输入源文件是唯一的。

---
 src/main/clojure/cljs/closure.clj | 3 ++-
 1 文件更改,2 个插入 (+),1 个删除 (-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index a686f878..24421bde 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2364,7 +2364,8 @@
   (let [;; 来自 :foreign-libs (编译器选项) 和 :ups-foreign-libs (deps.cljs) 的模块
         ; 被一起处理,因此两个来源的文件可以相互依赖。
         ; 例如,:foreign-libs 中的 CommonJS 模块可以依赖于 :ups-foreign-libs 中的 CommonJS 模块。
-        js-modules (filter :module-type (concat (:foreign-libs opts) (:ups-foreign-libs opts)))]
+        js-modules (filter :module-type (concat (:foreign-libs opts) (:ups-foreign-libs opts)))
+        js-modules (distinct js-modules)]
     (if (seq js-modules)
       (util/measure (:compiler-stats opts)
         "处理 JS 模块"
--
2.13.0



FWIW:我更喜欢方案 B。
0 投票

评论者:phiware

附加方案 B

0 投票

评论者:phiware

嗨,Mike,

我希望这现在能满足你的要求(以及 BDFL 的要求);我针对提出的两个方案都运行了 {{lein test}},没有收到任何失败。然而,我确实收到了一些错误,这些错误在断言中没有出现。我假设原因是我的配置中存在某些特殊(或不普通)的因素。如果你还需要其他任何东西,请告诉我。

再见,
Corin。

0 投票

评论由:mfikes 提出

谢谢 Corin。我的整个测试套件对你的最新补丁通过了测试。

0 投票

评论由:mfikes 提出

CLJS-2402.patch 已添加到 Patch Tender (i)

0 投票
参考:https://clojure.atlassian.net/browse/CLJS-2402(由 phiware 报告)
...