Skip to content

Commit

Permalink
add support for npm "exports" with wildcard suffix
Browse files Browse the repository at this point in the history
webpack docs don't explicitely say that these are allowed, but
node docs do.

  https://nodejs.org/api/packages.html#subpath-patterns
  • Loading branch information
thheller committed Apr 24, 2024
1 parent 53d59bd commit b3e22da
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 32 deletions.
72 changes: 41 additions & 31 deletions src/main/shadow/build/npm.clj
Expand Up @@ -86,21 +86,22 @@
(path-map? exports)
(reduce-kv
(fn [package path match]
(cond
(str/ends-with? path "/")
(if (str/ends-with? path "/")
(update package :exports-prefix util/vec-conj
{:prefix path
:match match})

;; FIXME: are wildcards only allowed at end?
(str/ends-with? path "*")
(update package :exports-wildcard util/vec-conj
;; strip * here, so we don't have to do it again later
{:prefix (subs path 0 (dec (count path)))
:match match})

:just-a-path
(assoc-in package [:exports-exact path] match)))
(if-some [star-idx (str/index-of path "*")]
(update package :exports-wildcard util/vec-conj
;; strip * here, so we don't have to do it again later
{:prefix (subs path 0 star-idx)
:suffix (when (not= star-idx (dec (count path)))
(subs path (inc star-idx)))
:match match})

;; just-a-path
(assoc-in package [:exports-exact path] match))))
package
exports)

Expand Down Expand Up @@ -763,28 +764,37 @@
(defn find-resource-from-exports-by-wildcard
[npm {:keys [package-dir] :as package} rel-require]
(reduce
(fn [_ {:keys [prefix match]}]
(fn [_ {:keys [prefix suffix match] :as x}]
(when (str/starts-with? rel-require prefix)
(let [suffix (subs rel-require (count prefix) (count rel-require))]

(when-some [replacement (find-exports-replacement npm match)]
(let [path (str/replace replacement #"\*" suffix)
file (test-file package-dir path)]

(cond
(not file)
nil
#_(throw (ex-info "package export wildcard match referenced a file that doesn't exist"
{:rel-require rel-require :package-dir package-dir :prefix prefix :match match}))

(.isDirectory file)
nil
#_(throw (ex-info (format "package export wildcard match referenced a directory")
{:file file :rel-require rel-require :package-dir package-dir :prefix prefix :match match}))

:else
(reduced (file-as-resource npm package rel-require file)))
)))))
(let [fill (subs rel-require (count prefix) (count rel-require))

;; patterns may either have ended in ./foo/* or with an additional suffix ./foo/*.js
;; the merge-package-exports* already parsed that and left :suffix nil if at the end
;; otherwise we need to check of the rel-require also matched the suffix
fill (if (nil? suffix)
fill
(when (str/ends-with? fill suffix)
(subs fill 0 (- (count fill) (count suffix)))))]

(when fill
(when-some [replacement (find-exports-replacement npm match)]
(let [path (str/replace replacement #"\*" fill)
file (test-file package-dir path)]

(cond
(not file)
nil
#_(throw (ex-info "package export wildcard match referenced a file that doesn't exist"
{:rel-require rel-require :package-dir package-dir :prefix prefix :match match}))

(.isDirectory file)
nil
#_(throw (ex-info (format "package export wildcard match referenced a directory")
{:file file :rel-require rel-require :package-dir package-dir :prefix prefix :match match}))

:else
(reduced (file-as-resource npm package rel-require file)))
))))))
nil
(:exports-wildcard package)))

Expand Down
11 changes: 11 additions & 0 deletions src/test/shadow/build/npm_test.clj
Expand Up @@ -281,6 +281,17 @@
(is (= "node_modules/exports/other/foo.js" resource-name))
)))

(deftest test-exports-wildcard-with-suffix
(with-npm [x {}]
(let [{:keys [resource-name] :as rc1}
(find-npm-resource x nil "exports/wildcard-with-suffix/foo.js")]

(is rc1)
(is (string? resource-name))
(is (= "node_modules/exports/other/foo.js" resource-name))
)))


(deftest test-exports-wildcard-missing
(with-npm [x {}]
(is (thrown? ExceptionInfo (find-npm-resource x nil "exports/wildcard/bar")))
Expand Down
3 changes: 2 additions & 1 deletion test-env/node_modules/exports/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b3e22da

Please sign in to comment.