请在2024 Clojure状态调查中分享您的想法!

欢迎!请参阅关于页面以了解更多关于此工作方式的信息。

0
tools.namespace

在路径{{public/js/out/foo/bar.cljc}}有一个cljc文件,并且命名空间形式为{{(ns foo.bar)}},这将导致命名空间{{foo.bar}}被重新加载。

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

我认为对于tools.namespace来说,忽略文件路径和命名空间不匹配的更改是有意义的。
另一个问题以及可能的解决办法是了解为什么在这种情况下依赖关系解析不起作用:依赖于输出目录中cljc文件的协议的命名空间不会被重新加载。

19 个答案

0

评论者:deraen

这几乎与 http://dev.clojure.org/jira/browse/TNS-24 相同,但在这个情况下不一致命名的ns将被重新加载,因为正确的路径中有一个副本。

0

评论者:deraen

带测试的补丁提案。

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

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

评论者:deraen

在补丁中修复了错别字。

0

评论:severeoverfl0w

我也遇到这个bug。当与bidi结合时尤其糟糕,因为它使用协议和记录。这个补丁完美地解决了我的问题。

0

评论:stuart.sierra

这个问题的根本原因是将编译后的 "web资产"(例如编译后的ClojureScript代码)放在 {{resources/public}} 的 Java 类路径上,这是一个常见的做法。

目前有两大解决方案可用

  1. 配置ClojureScript将编译的文件放在不在类路径上的目录中
  2. 使用 (链接: http://clojure.github.io/tools.namespace/#clojure.tools.namespace.repl/set-refresh-dirs 文本:set-refresh-dirs) 来将这些目录排除在 tools.namespace 搜索之外。

任何更改 tools.namespace 行为的事情(特别是在低级API中)都有可能破坏其他用例,因此必须谨慎考虑。例如,代码检查程序可能会使用 {{find-sources-in-dir}} 搜索所有Clojure源文件,包括那些有无效的 {{ns}} 声明的源文件。

0

评论者:deraen

  1. 配置ClojureScript将编译的文件放在不在类路径上的目录中

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

  1. 使用 set-refresh-dirs 来将这些目录排除在 tools.namespace 搜索之外。

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

0

评论:severeoverfl0w

如果 do-refresh 在其映射中接受一个选项 sane-paths-only?,您是否可以接受这个补丁?这个选项将传递给它对 find/find-sources-in-dir 的调号,其中 find-sources-in-dir 接受一个可选的第三个参数,指定是否 sane-paths-only?

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

简而言之,如果您同意只对 sane-paths-only? 进行 repl/refresh 运行,并通过参数传递下来,您会接受这个补丁吗?

0

评论人:malcolmjuxt

我同意你不应该在类路径上编译工件。

但是,由于 uberjars,这在 Clojure 世界中是一种非常普遍的做法。

我个人不喜欢 uberjars,但好像我是少数派!只要人们在生产中使用 uberjars,他们就会希望在开发时从类路径中提供服务。

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

这是一个非常让人烦恼的错误,对于那些使用 cljc 库(例如 bidi)的人来说。如果能提出另一种解决方案,那将非常棒。

0

评论:severeoverfl0w

  1. 使用 set-refresh-dirs 来将这些目录排除在 tools.namespace 搜索之外。

我发现这种策略的另一个问题是,它无法处理有不同的 :source-paths 的 Leiningen 配置文件。每次切换配置文件时,您都必须更改 set-refresh-dirs 的调用(并且当您做错时感到困惑)。

0

评论:stuart.sierra

tools.namespace 从未打算用于部署的应用程序,因此我认为现在可以安全地忽略 uberjars 的情况。

如果有意外路径中的文件被静默跳过,就很难理解为什么误命名的文件没有被加载。作为一个可能的折中方案,当 {{c.t.n.find}} 遇到文件时,它的 {{ns}} 声明与其路径不匹配,它可以在 * } 上打印警告,并从搜索结果中省略它。

这留下了虚假错误信息的问题。正如上面的评论中提到的,通过连接Leiningen profilesBoot,在静态定义应该扫描源文件的目录集时很难。如果我们能够指定要从中排除的目录,这对解决 ClojureScript 将 {{.cljc}} 文件复制到资源目录的问题是否足够?

0

评论者:deraen

我认为提到了uberjars是用来解释为什么人们从classpath中提供文件,而不是因为人们使用c.t.n与部署应用程序。

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

在Boot中,用户无法控制包含在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 会打印一个警告并将忽略的目录添加到跟踪器中,根据 Dominic Monroe 在以下链接中的建议(链接:https://dev.clojure.org/jira/browse/TNS-45?focusedCommentId=47280&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-47280 文本:评论)。

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

在首次发现与 {{scan-dirs}} 不匹配的文件时,将打印一个警告,显示声明的 {{ns}} 名称和未匹配的文件路径。此后,整个目录将被忽略,并在每次调用 {{scan-dirs}} 时打印一条通知。我希望这能在信息性和虚假警告太多之间找到良好的平衡。

注意:之前的补丁文件 TNS-42-3.patch 被误命名,但始终旨在应用于 TNS-45。

...