2024 Clojure 状态调查中分享您的看法!

欢迎!请参阅关于页面以了解更多关于如何使用本网站的信息。

0
tools.namespace

在路径 {{public/js/out/foo/bar.cljc}} 有一个名为 {{(ns foo.bar)}} 的 cljc 文件会导致命名空间 {{foo.bar}} 被重新加载。

这是问题所在,因为 ClojureScript 编译器将会将所有输入文件复制到 {{:output-dir}} 以用于 source-map。最近随着越来越多的库开始使用 cljc 格式,这已开始在 Clojure 环境中导致问题。 Clojs 编译将会导致库代码的重新加载,这可能重新定义协议等,并破坏 Clojure 环境。

我认为 tools.namespace 应该忽略文件路径和命名空间不匹配的更改情况。
另一个问题和可能的解决方案是理解为什么依赖解析在这种情况下不起作用:依赖于输出目录中 cljc 文件的协议的命名空间不会被重新加载。

19 回答

0
by

由 deraen 发布的评论:

这与http://dev.clojure.org/jira/browse/TNS-24几乎相同,但在这个案例中,命名空间由于在正确路径上的复制而不一致地重新加载。

0
by

由 deraen 发布的评论:

提供了一个带有测试的补丁。

{{find-sources-in-dir}}是进行此检查的最简单位置,因为我们需要目录路径。

  • 这可能破坏了对{{find-sources-in-dir}}的直接使用者?
  • 现在{{find-sources-in-dir}}和{{file/files-and-deps}}都需要读取命名空间形式。
0

由 deraen 发布的评论:

在补丁中修正了一个错误。

0

评论者:severeoverfl0w

我也有过这个问题的经历。特别是当与双向文本结合使用时,由于它的协议和记录的使用,这个问题尤为严重。这个补丁完美地解决了我的问题。

0

评论者:stuart.sierra

这个问题的根源是将编译后的 "web 资产"(如编译后的 ClojureScript 代码)放入 Java 类路径中的 {{resources/public}} 的常见做法。

现在有两个简单的解决方案可用

  1. 配置 ClojureScript 将编译后的文件放在类路径之外的目录中
  2. 使用(链接:http://clojure.github.io/tools.namespace/#clojure.tools.namespace.repl/set-refresh-dirs 文本:set-refresh-dirs)来忽略这些目录的工具命名空间搜索。

任何更改 tools.namespace 行为的内容,特别是在低级别 API 中,都有可能破坏其他用例,因此必须仔细考虑。例如,代码检查器可能会使用 {{find-sources-in-dir}} 来搜索所有 Clojure 源文件,包括具有无效的 {{ns}} 声明的文件。

0

由 deraen 发布的评论:

  1. 配置 ClojureScript 将编译后的文件放在类路径之外的目录中

使用类路径来提供文件是最高效的方法,不太可能改变。许多工具,如 Boot,鼓励使用类路径来提供文件。

  1. 使用 set-refresh-dirs 来忽略这些目录的工具命名空间搜索。

在 Boot 中这是不可能的。在 Boot 中,类路径是由 Boot 管理的,一个目录将包含来自多个来源的文件,例如 Boot-cljs 和 {{source-paths}}。

0

评论者:severeoverfl0w

如果 do-refresh 在其 map 的 sane-paths-only? 选项中接受一个选项,并将其传递给其调用下的 find/find-sources-in-dir,你会接受这个补丁吗?其中 find-sources-in-dir 接受一个可选的第三个参数,指定是否 sane-paths-only?

find-sources-in-dir 的最后一个参数重构为选项 map 会很好,但打破这个 API 可能超出范围。

tl;dr 如果 sane-paths-only? 只在 repl/refresh 中运行,并通过参数传递,你会接受这个补丁吗?

0

评论者:malcolmjuxt

我同意你不应该把构建产物编译到类路径中。

然而,由于 uberjars 的影响,在 Clojure 世界中这是一个极其普遍的做法。

就我个人而言,我不喜欢 uberjars,但看来我是少数派!只要人们在生产中使用 uberjars,他们就会希望在开发中使用类路径。

因此我认为,Stuart 提出的简单解决方案都令人不满意。

对于使用 cljc 库(如 bidi)的人来说,这是一个真正让人生厌的 bug。如果能提出另一个解决方案就好了。

0

评论者:severeoverfl0w

  1. 使用 set-refresh-dirs 来忽略这些目录的工具命名空间搜索。

我还注意到了这种策略的一个问题:它不会处理有不同 :source-paths 的 leiningen 分Profile(每次切换Profile时都必须修改对 set-refresh-dirs 的调用,而且可能在你出错时感到困惑)。

0

评论者:stuart.sierra

tools.namespace 从未打算在部署的应用中使用,因此我认为现在可以安全地忽略关于(链接:[https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=44822&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-44822](https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=44822&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-44822) 文本:uberjars)的情况。

如果忽略意外路径中的文件,就很难理解为什么一个重命名的文件没有被加载。作为一个可能的妥协方案,当 {{c.t.n.find}} 遇到其 {{ns}} 声明与其路径不匹配的文件时,可以向 * } 打印一个警告,并将其从搜索结果中排除。

这留下了一个虚假错误信息的问题。如上所述,链接:[https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=45080&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-45080](https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=45080&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-45080) 文本:Leiningen profiles)和链接:[https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=44149&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-44149](https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=44149&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-44149) 文本:Boot)使静态定义要扫描源文件的目录集合变得困难。如果我们能够指定要 *排除 的目录,这将足够解决 ClojureScript 将 {{.cljc}} 文件复制到资源目录的问题吗?

0

由 deraen 发布的评论:

我认为提到了uberjars,关于人们为什么从classpath提供服务文件,而不是因为人们使用c.t.n与已部署的应用程序。

被排除的目录会被定义为文件路径还是classpath前缀?

在启动过程中,用户无法控制包含在classpath中的目录。相反,Boot创建一些自己控制的临时目录,它们包含在classpath中。来自多个来源的文件(项目资源路径、Cljs工件等)都复制到同一个临时目录,因此不能根据此排除Cljs工件。

定义要忽略的classpath前缀可能会与Boot一起工作。

0

评论者:stuart.sierra

按照classpath前缀排除可能是可能的,但很棘手。目前,{{t.n.s.dir}}在不知道这些目录是否在classpath上时,搜索任意文件系统目录。尽管如此,如果排除是作为classpath相关目录路径表示,它们可以被解析成真实的文件系统路径。

还有另一种可能性,我应该早点想到,即使用文件哈希值,而不是现在使用的时戳来确定文件何时“更改”。这将防止仅仅因为ClojureScript编译器复制它们而重新加载{{.cljc}}文件。另一方面,这将是一个更大的变化,它将防止使用{{touch}}(或编辑器中的重新保存)强制重新加载文件。

0

评论者:stuart.sierra

新的补丁TNS-42-3.patch将责任从c.t.n.find提升到c.t.n.dir。这避免了c.t.n.find/find-sources-in-dir的任何中断,同时实现了c.t.n.repl/refresh所需的行为。

这可能是一个合理的折中方案,尽管我仍然有点担心误命名的文件会被静默忽略,导致困惑。

0

评论者:severeoverfl0w

可能会将弃用的文件/命名空间添加到跟踪器中,以减轻困惑。刷新时,它将像现在一样使用prn提醒这些文件。

如果弃用的文件再次成为有效文件,则需要将它们从弃用列表中删除。

我不确定这应该阻止TNS-45,但这可能是有人可以跟进的事情。

0

评论者:stuart.sierra

新补丁文件 TNS-45-4.patch 会打印警告并将忽略的目录添加到跟踪器中,遵循(链接:https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=47280&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-47280 文字:评论)Dominic Monroe 的建议。

这仅适用于 {{c.t.n.dir/scan-dirs}},因此 tools.namespace 中低级 API 的其他使用不应受到影响。

在第一次遇到不匹配 {{scan-dirs}} 的文件时,将打印带有声明 {{ns}} 名称和不匹配文件路径的警告到 *。之后,整个目录将被忽略,每次调用 {{scan-dirs}} 时将打印通知。我希望这能够在信息性消息过多和太多虚假警告之间取得良好平衡。

注意:上一个补丁文件 TNS-42-3.patch 名字误写,但总是打算应用于 TNS-45。

...